问题:当IN (...)
中包含超过396个项目时,Mysql未在更新查询中使用索引。甚至我强迫引擎使用主键。
表格' n:
CREATE TABLE `store_product` (
`store_id` bigint(20) NOT NULL,
`product_id` bigint(20) NOT NULL,
`created_at` timestamp NOT NULL,
`last_updated_at` timestamp NOT NULL,
`status` varchar(255) NOT NULL,
`active_promotion_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`store_id`,`product_id`),
KEY `store_product_status_index` (`status`),
KEY `store_product_active_promotion_id_index` (`active_promotion_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
查询:
UPDATE store_product FORCE INDEX (PRIMARY) SET active_promotion_id = NULL, WHERE (store_id, product_id) IN ((x1, y1), ..., (xn, yn))
其中n> 396(总共59M行)。
这个神奇数字的含义是什么?为什么MySQL没有收听我,甚至在更新时没有使用primary key
?
版本: mysql-5.7.17.R1
答案 0 :(得分:0)
如果MySQL优化器认为使用索引会比简单扫描表格更加昂贵,那么它就不会使用索引。
类比将是书后面的索引。为什么不包括像“the”这样的常用词?因为它最终会告诉你这本常用词出现在书的每一页上。这不是帮助您找到正确页面的有效方法。
MySQL做了类似的事情,如果它认为你要搜索的值发生在表的一个足够大的子集上,它会跳过索引。这没有记录的阈值,但根据我的经验,当优化器估计您的条件匹配表的20%以上时会发生这种情况。
FORCE INDEX
优化器提示旨在解决此问题,前提是您知道使用索引优于表扫描,尽管优化程序估计了。但也许这只能避免表扫描,而不是索引扫描。
另一种可能性:MySQL不能很好地优化像(a,b) IN ((val, val)...)
这样的元组比较谓词。这是一个new feature in MySQL 5.7,看起来它仍然不如简单的谓词那么好。
以下是我使用EXPLAIN测试查询时获得的优化计划,IN()
子句中只有两个值对。
+----+-------------+---------------+-------+---------------+---------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------+---------------+---------+---------+------+------+------------------------------+
| 1 | SIMPLE | store_product | index | NULL | PRIMARY | 16 | NULL | 1 | Using where; Using temporary |
+----+-------------+---------------+-------+---------------+---------+---------+------+------+------------------------------+
type: index
不是一个好兆头。这意味着它将扫描整个主键索引。这几乎与表扫描一样糟糕。
Using temporary
也是一项代价高昂的操作。我想它正在创建一个临时表来存储IN()
谓词的元组列表。
这是一个等价的查询,它可以在不使用元组比较的情况下获得更好的优化计划:
EXPLAIN UPDATE store_product SET active_promotion_id = NULL
WHERE (store_id=1 AND product_id=1) OR (store_id=2 AND product_id=2)
+----+-------------+---------------+-------+---------------+---------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------+---------------+---------+---------+-------------+------+-------------+
| 1 | SIMPLE | store_product | range | PRIMARY | PRIMARY | 16 | const,const | 1 | Using where |
+----+-------------+---------------+-------+---------------+---------+---------+-------------+------+-------------+
type: range
是更有利的优化。并且没有临时表。