MySQL JSON 数据类型是否会影响数据检索的性能?

use*_*492 5 mysql performance json mysql-5.7

假设我有一个名为custom_properties媒体表的 MySQL JSON 数据类型:

在此输入图像描述

存储在列中的 json 数据的示例custom_properties可能是:

{
 "company_id": 1, 
 "uploaded_by": "Name", 
 "document_type": "Policy", 
 "policy_signed_date": "04/04/2018"
}
Run Code Online (Sandbox Code Playgroud)

在我的 PHP Laravel 应用程序中,我会执行以下操作:

$media = Media::where('custom_properties->company_id', Auth::user()->company_id)->orderBy('created_at', 'DESC')->get();
Run Code Online (Sandbox Code Playgroud)

这将获取属于公司 1 的所有媒体项目。

我的问题是,假设我们有 100 万条媒体记录,从性能角度来看,这是否是一种糟糕的获取记录的方法?谁能解释一下 MySQL 如何索引 JSON 数据类型?

来自MySQL 官方文档

存储在 JSON 列中的 JSON 文档将转换为允许快速读取文档元素的内部格式。当服务器稍后必须读取以此二进制格式存储的 JSON 值时,不需要从文本表示中解析该值。二进制格式的结构使服务器能够直接通过键或数组索引查找子对象或嵌套值,而无需读取文档中它们之前或之后的所有值。

sha*_*een 9

所以我用大数据做了一些测试;我创建了这两个表:

CREATE TABLE `json` (
  `data` json DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci


CREATE TABLE `users` (
  `name` varchar(255) DEFAULT NULL,
  `relation_id` int DEFAULT NULL,
  `description` text
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Run Code Online (Sandbox Code Playgroud)

第一个表将其数据存储在 JSON 字段中,另一个表使用列。

我在每个表中填充了1,000,000 条记录

我运行了这两个查询:

select * from users where relation_id > 100 order by relation_id limit 100


select json_extract(data, '$.name') as name,
       json_extract(data, '$.relation_id') as relation_id
       from json
        where json_extract(data, '$.relation_id') > 100 order by relation_id
        limit 100
Run Code Online (Sandbox Code Playgroud)

第一个查询查询不使用 JSON 的表,结果是

[2022-03-02 00:37:46] 100 rows retrieved starting from 1 in 854 ms (execution: 838 ms, fetching: 16 ms)

使用 JSON 的第二个查询结果:

[2022-03-02 00:42:52] 100 rows retrieved starting from 1 in 1 s 10 ms (execution: 994 ms, fetching: 16 ms)

显然,在简单的情况下,这并没有太大的不同。

现在尝试运行聚合函数,这两个查询:

select name, relation_id, description , max(relation_id) as max_relation
from users
where relation_id > 100
group by name, relation_id, description
order by relation_id
limit 100; # executed in 5 s 3 ms

select data -> '$.name'             as name,
       data -> '$.relation_id'      as relation_id,
       data -> '$.description'      as description,
       max(data -> '$.relation_id') as max_relation
from json
where json_extract(data, '$.relation_id') > 100
group by data -> '$.name', data -> '$.relation_id', data -> '$.description'
order by relation_id
limit 100 # executed in 6 s 238 ms
Run Code Online (Sandbox Code Playgroud)

第一个查询结果为:

[2022-03-02 15:23:13] 100 rows retrieved starting from 1 in 5 s 21 ms (execution: 5 s 3 ms, fetching: 18 ms)
Run Code Online (Sandbox Code Playgroud)

第二个查询结果为:

[2022-03-02 15:23:25] 100 rows retrieved starting from 1 in 6 s 238 ms (execution: 6 s 218 ms, fetching: 20 ms)
Run Code Online (Sandbox Code Playgroud)

所以看起来使用聚合函数时存在明显的性能差异。


dbd*_*mon 4

我认为我们可以有把握地假设性能会比使用常规列更差。

对 JSON 属性进行索引的方法是为所需的特定属性 (company_id) 创建一个虚拟生成列,然后对虚拟列进行索引,并使用该列来筛选查询。