为什么 MEMBER OF() 比 JSON_CONTAINS() 快?
Why is MEMBER OF() faster than JSON_CONTAINS()?
我正在使用 MySQL 8 的新 JSON 功能,特别是多值索引。
我注意到有两种方法可以检查 JSON 数组是否包含给定值:MEMBER OF() operator, and the JSON_CONTAINS() 函数。
对于我进行的每个查询,它们都 return 相同的结果集,但令人惊讶的是,MEMBER OF
似乎比 JSON_CONTAINS
快 3 倍。
示例 table 有 200,000 条记录,catIds
字段中总共有大约 700,000 个值:
CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
catIds JSON NOT NULL,
PRIMARY KEY (id),
KEY test_categories ((CAST(catIds AS UNSIGNED ARRAY)))
);
INSERT INTO test (catIds) VALUES('[123, 456]');
...
...稍后插入了大约 200,000 条记录:
mysql> SELECT count(*) FROM test WHERE 51 member of (catIds);
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.11 sec)
mysql> SELECT count(*) FROM test WHERE JSON_CONTAINS(catIds, '51');
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.36 sec)
如果一开始认为这是因为字符串化的 JSON 值 '51'
可能在每次迭代时都被转换,所以我先尝试将其分配给一个变量;但这并没有让它变得更快:
mysql> SET @value = CAST(51 as JSON);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT count(*) FROM test WHERE JSON_CONTAINS(catIds, @value);
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.38 sec)
除非我弄错了,否则 MEMBER OF()
和 JSON_CONTAINS()
在功能方面是等效的。在这种情况下,为什么其中一个比另一个快?
JSON_CONTAINS()
做的工作比 MEMBER OF
.
更复杂
JSON_CONTAINS()
必须解析其第二个参数,即您正在存储的 JSON 文档中搜索的候选 JSON 文档。
候选人可能不是您在上面的示例中搜索的简单标量。它可能是一个更复杂的文档,具有自己的嵌套数组和对象。
因此,将候选文档与存储的文档进行比较可能必须以更复杂的方式进行比较,而不仅仅是搜索单个标量值,而是递归地比较所有嵌套元素。
即使您的示例搜索是针对一个简单的标量值,它仍然会调用 可能 搜索复杂文档所需的相同代码路径。根据您的计时测量,该代码路径似乎有更多开销。
而MEMBER OF
只搜索标量值,并且只在数组中搜索。它还可以通过使用缓存的、预先排序的数组进行优化。
代码见https://github.com/mysql/mysql-server/blob/8.0/sql/item_json_func.cc#L3852。
我正在使用 MySQL 8 的新 JSON 功能,特别是多值索引。
我注意到有两种方法可以检查 JSON 数组是否包含给定值:MEMBER OF() operator, and the JSON_CONTAINS() 函数。
对于我进行的每个查询,它们都 return 相同的结果集,但令人惊讶的是,MEMBER OF
似乎比 JSON_CONTAINS
快 3 倍。
示例 table 有 200,000 条记录,catIds
字段中总共有大约 700,000 个值:
CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
catIds JSON NOT NULL,
PRIMARY KEY (id),
KEY test_categories ((CAST(catIds AS UNSIGNED ARRAY)))
);
INSERT INTO test (catIds) VALUES('[123, 456]');
...
...稍后插入了大约 200,000 条记录:
mysql> SELECT count(*) FROM test WHERE 51 member of (catIds);
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.11 sec)
mysql> SELECT count(*) FROM test WHERE JSON_CONTAINS(catIds, '51');
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.36 sec)
如果一开始认为这是因为字符串化的 JSON 值 '51'
可能在每次迭代时都被转换,所以我先尝试将其分配给一个变量;但这并没有让它变得更快:
mysql> SET @value = CAST(51 as JSON);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT count(*) FROM test WHERE JSON_CONTAINS(catIds, @value);
+----------+
| count(*) |
+----------+
| 7287 |
+----------+
1 row in set (0.38 sec)
除非我弄错了,否则 MEMBER OF()
和 JSON_CONTAINS()
在功能方面是等效的。在这种情况下,为什么其中一个比另一个快?
JSON_CONTAINS()
做的工作比 MEMBER OF
.
JSON_CONTAINS()
必须解析其第二个参数,即您正在存储的 JSON 文档中搜索的候选 JSON 文档。
候选人可能不是您在上面的示例中搜索的简单标量。它可能是一个更复杂的文档,具有自己的嵌套数组和对象。
因此,将候选文档与存储的文档进行比较可能必须以更复杂的方式进行比较,而不仅仅是搜索单个标量值,而是递归地比较所有嵌套元素。
即使您的示例搜索是针对一个简单的标量值,它仍然会调用 可能 搜索复杂文档所需的相同代码路径。根据您的计时测量,该代码路径似乎有更多开销。
而MEMBER OF
只搜索标量值,并且只在数组中搜索。它还可以通过使用缓存的、预先排序的数组进行优化。
代码见https://github.com/mysql/mysql-server/blob/8.0/sql/item_json_func.cc#L3852。