首页 » 网站建设 » phpmysql保留json技巧_MySQL8 下的 JSON

phpmysql保留json技巧_MySQL8 下的 JSON

duote123 2024-11-11 0

扫一扫用手机浏览

文章目录 [+]

json类型的字段的内容在mysql中是按text类型存储的。

官方文档地址:

phpmysql保留json技巧_MySQL8 下的 JSON

json数据类型: https://dev.mysql.com/doc/refman/8.0/en/json.html

phpmysql保留json技巧_MySQL8 下的 JSON
(图片来自网络侵删)

json类型搜索的函数: https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html

给json类型创建索引: https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued

本文内容基于 win10+MySQL8.0.28 + MySQL workbench 环境编写。

mysql的JSON类型支持key-value及json数组格式,本文紧张研究json数组格式,运用处景为信息的单、复选择项记录在筛选页面的过滤。

基本用法

数据转换

-- key-value格式

SELECT CAST('{"deptName": "部门5", "deptId": "5", "deptLeaderId": "5"}' AS json) AS `key_value_format`

-- json数组格式

SELECT CAST( '[1,2,3]' AS json) AS `json_array_format`

key-value格式的查询

CREATE TABLE `dept` (

`id` INT UNSIGNED NOT NULL,

`dept` VARCHAR(255) DEFAULT NULL,

`json_value` JSON DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB;

INSERT INTO `dept`(id, dept, json_value)

VALUES(1,'部门1','{"deptName": "部门1", "deptId": 1, "deptLeaderId": 3}')

,(2,'部门2','{"deptName": "部门2", "deptId": 2, "deptLeaderId": 4}')

,(3,'部门3','{"deptName": "部门3", "deptId": 3, "deptLeaderId": 5}')

,(4,'部门4','{"deptName": "部门4", "deptId": 4, "deptLeaderId": 5}')

,(5,'部门5','{"deptName": "部门5", "deptId": 5, "deptLeaderId": 5}');

-- 查询

SELECT FROM `dept` WHERE json_value->'$.deptLeaderId' IN (5, 3);

json数组格式查询

-- 必须全部包含

SELECT

FROM (

SELECT JSON_ARRAY(1,2,3,4,5,6,7,8) AS ids

) AS a

WHERE JSON_CONTAINS(a.ids, json_array(4,3));

-- 只要包含一个即可

SELECT

FROM (

SELECT JSON_ARRAY(1,2,3,4,5,6,7,8) AS ids

) AS a

WHERE JSON_OVERLAPS(a.ids, json_array(4,13));

索引

-- key-value格式

CREATE TABLE `employees` (

`student_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

`data` JSON,

INDEX `IX_name`((data->>'$.name'))

) ENGINE=InnoDB;

-- json数组格式,要建立多值索引

CREATE TABLE `students` (

`student_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

`class_ids` JSON COMMENT '所有的课程id的数组',

INDEX `IX_classIds`( (CAST(`class_ids` AS UNSIGNED ARRAY)) )

) ENGINE=InnoDB;

更多信息请参考官方文档。

key-value格式下的某一个key仍旧可以利用json数组, 并建立多值索引

业务仿照测试

每一个信息对应一个分类(category_id),每个分类下有多个项目(category_item_id),每个项眼前有多个选项(item_option_id),项目可能是多选也可能是单选,把所选的选项id记录到字段value中。

信息的单、复选择项记录,我们之前利用的是一行一行的记录,但是这个在列表页过滤时,可能会将这个表多次innner join查询以过滤所有选择的项都要涌现。

我们建两个表,一个是原始的一行一行记录格式,一个利用json格式保存所有数据.

-- 原始表: 一行一行记录

CREATE TABLE `info_to_category_item_options3` (

`id` int unsigned NOT NULL AUTO_INCREMENT,

`category_id` smallint unsigned NOT NULL DEFAULT '0' COMMENT '分类id',

`info_id` int unsigned NOT NULL COMMENT '信息id',

`category_item_id` smallint unsigned NOT NULL DEFAULT '0' COMMENT '分类的项目id',

`item_option_id` mediumint unsigned NOT NULL DEFAULT '0' COMMENT '选中的选项id',

PRIMARY KEY (`id`,`category_id`)

) ENGINE=InnoDB COMMENT='项目选择结果'

/!50100 PARTITION BY HASH (`category_id`) PARTITIONS 10 /;

-- 新表: json格式

CREATE TABLE `info_to_category_item_options4` (

`id` int unsigned NOT NULL AUTO_INCREMENT,

`category_id` smallint unsigned NOT NULL DEFAULT '0' COMMENT '分类id',

`info_id` int unsigned NOT NULL COMMENT '信息id',

`category_item_id` smallint unsigned NOT NULL DEFAULT '0' COMMENT '分类的项目id',

`item_option_ids` JSON COMMENT '选中的选项id的json数组',

PRIMARY KEY (`id`,`category_id`)

) ENGINE=InnoDB COMMENT='项目选择结果'

/!50100 PARTITION BY HASH (`category_id`) PARTITIONS 10 /;

仿照1000w信息的数据插入到原始表

//分类id在100-200之间

$arrCategoryItemIdAll = range(100, 200);

//保存sql的values字句内容

$arrSqlValues = [];

$batchSize = 100;

//批次插入时用来分批的计数

$k = 0;

for ($i = 1; $i <= 10000000; $i++) {

//信息id

$infoId = $i;

//分类id, 从1-200中随机取一个

$categoryId = rand(1, 200);

//随机1-5个项目

$t = rand(1, 5);

if ($t === 1) {

//把稳: 只有一个时返回的是key

$arrCategoryItemId = [$arrCategoryItemIdAll[array_rand($arrCategoryItemIdAll, 1)]];

} else {

$arrCategoryItemId = array_rand($arrCategoryItemIdAll, $t);

}

$arrCategoryItemIdToItemOptionId = [];

foreach ($arrCategoryItemId as $key => $categoryItemId) {

//每个项目随机1-3个选中的属性

$t2 = rand(1, 3);

for ($j = 1; $j <= $t2; $j++) {

$arrCategoryItemIdToItemOptionId[] = [

'category_item_id' => $categoryItemId,

'item_option_id' => $j 100 + rand(10, 50),

];

}

}

foreach ($arrCategoryItemIdToItemOptionId as $row) {

$categoryItemId = $row['category_item_id'];

$item_option_id = $row['item_option_id'];

$arrSqlValues[] = '(' . $categoryId . ', ' . $infoId . ', ' . $categoryItemId . ', ' . $item_option_id . ')';

}

if ($k === $batchSize) {

//达到100个信息的时候批量写入

$sql = 'INSERT INTO `info_to_category_item_options3`(`category_id`,`info_id`,`category_item_id`,`item_option_id`)VALUES'

. implode(',', $arrSqlValues);

echo $sql . PHP_EOL;

$db->query($sql);//数据库类实行sql

//重置

$arrSqlValues = [];

$k = 0;

} else {

$k++;

}

}

if ($k < $batchSize && count($arrSqlValues) > 0) {

echo '不敷100的' . PHP_EOL;

$sql = 'INSERT INTO `info_to_category_item_options3`(`category_id`,`info_id`,`category_item_id`,`item_option_id`)VALUES'

. implode(',', $arrSqlValues);

echo $sql . PHP_EOL;

$db->query($sql);//数据库类实行sql

}

原始表示履行数6000w旁边, 单分区表600w(文件大小200M旁边)

将原始表数据复制到新表

INSERT INTO `info_to_category_item_options4`

(category_id, info_id, category_item_id, `item_option_ids`)

SELECT category_id, info_id, category_item_id, CONCAT('[', GROUP_CONCAT(`item_option_id`), ']') AS `item_option_ids`

FROM info_to_category_item_options3

GROUP BY category_id, info_id, category_item_id;

添加索引

-- 原始表

ALTER TABLE `info_to_category_item_options3`

ADD INDEX IX_categoryId_categoryItemId_itemOptionId(`category_id`, `category_item_id`, `item_option_id`);

-- 新表

ALTER TABLE `info_to_category_item_options4`

ADD INDEX IX_categoryId_categoryItemId_itemOptionIds(`category_id`, `category_item_id`, (CAST(`item_option_ids` AS UNSIGNED ARRAY)));

查询测试

本地测试数据中, 项目选项ID(item_option_id)为131的单独有6800行旁边, 215的有4400行旁边。

1. 多个值的"部分包含"查询

这里我们查询选项id包含131、215个中之一的数据。

-- 原始表

SELECT SQL_NO_CACHE category_id

FROM info_to_category_item_options3

WHERE category_id=98 AND category_item_id=98 AND `value` IN (131, 215)

LIMIT 0,100000;

-- 新表

SELECT SQL_NO_CACHE category_id

FROM info_to_category_item_options4 WHERE category_id=98 AND category_item_id=98

AND JSON_OVERLAPS(`values`, CAST('[131,215]' AS JSON))

LIMIT 0,100000;

在mysql workbench下实行查询,二者都比较快, 但是前者更快。

而且我还创造两个很奇怪的问题:

我本地workbench的配置是默认LIMIT 100,两个查询的SQL都不加LIMIT时,原表的查询返回100行数据,这是符合预期的;但是新表的查询却返回了所有1万多行数据

上面的SQL中返回的字段category_id是在索引中的,不须要再回表查询;但是如果换成不在索引中的info_id,原表的查询由于须要回表会变慢,这是符合预期的;但是新表的查询却仍旧很快,貌似根本不须要回表。
而原表和新表的单个分区表(加索引后)的物理文件大小都在340M旁边。

2. 多个值的必须"全部包含"查询

这里我们查询选项id必须包含131和215的数据。

-- 原始表

SELECT SQL_NO_CACHE a.

FROM info_to_category_item_options3 AS a

, info_to_category_item_options3 AS b

WHERE a.category_id=98 AND a.category_item_id=98 AND a.`item_option_id`=131

AND b.category_id=98 AND b.category_item_id=98 AND b.`item_option_id`=215

AND a.info_id=b.info_id;

-- 新表

SELECT SQL_NO_CACHE

FROM info_to_category_item_options4

WHERE category_id=98 AND category_item_id=98

AND JSON_CONTAINS(`item_option_ids`, CAST('[131,215]' AS JSON));

利用原始表查询,韶光很很很长,以至于不得不终止查询。
由于inner join的条件字段info_id不在索引中, 须要回表11000多次,

利用新表查询,除了第一次1秒多,别的基本都在0.05秒旁边。

很奇怪的是,新表的查询中,SQL_NO_CACHE 参数失落效了,不然为什么第一次那么慢后面却那么快呢?

那我们现在修正一下原始表的索引:

ALTER TABLE `info_to_category_item_options3`

DROP INDEX IX_categoryId_categoryItemId_itemOptionId,

ADD INDEX IX_categoryId_categoryItemId_itemOptionId(`category_id`, `category_item_id`, `item_option_id`, `info_id`);

修正后,再次实行上面的查询,其性能已经比新表的json格式的快,而且是快3-4倍。

结论

综合本用例的测试结果来看,json数组在这种场景下性能不如传统办法,不建议利用。

原文链接:https://blog.csdn.net/aben_sky/article/details/125690495

标签:

相关文章

微信第三方登录便捷与安全的完美融合

社交平台已成为人们日常生活中不可或缺的一部分。微信作为我国最受欢迎的社交软件之一,拥有庞大的用户群体。为了方便用户在不同平台间切换...

网站建设 2025-02-18 阅读1 评论0

广东高速代码表解码高速公路管理智慧

高速公路作为国家交通动脉,连接着城市与城市,承载着巨大的物流和人流。广东作为我国经济大省,高速公路网络密布,交通流量巨大。为了更好...

网站建设 2025-02-18 阅读1 评论0