问题:当 IN (...)
里面有超过396件物品。我甚至强迫引擎使用主键。
表定义:
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
1条答案
按热度按时间vvppvyoh1#
如果mysql优化器认为使用索引比简单地硬扫描表更昂贵,那么它不会使用索引。
类似于书后面的索引。为什么不包括像“the”这样的常用词呢?因为它最终会告诉你,这个普通的词出现在书的每一页上。这不是帮助你找到正确页面的有效方法。
mysql也做了类似的事情,如果它认为您要搜索的值出现在表的足够大的子集上,它就会跳过索引。这方面没有文档化的阈值,但根据我的经验,当优化器估计您的条件匹配表的20%以上时,就会发生这种情况。
这个
FORCE INDEX
优化器提示旨在解决这个问题,当您知道使用索引比表扫描更好时,不管优化器如何估计。但也许这样做只是为了避免表扫描,而不是索引扫描。另一种可能性是:mysql不优化元组比较 predicate ,比如
(a,b) IN ((val, val)...)
很好。这是MySQL5.7中的一个新特性,看起来它仍然不能像更简单的 predicate 那样工作。下面是我用explain测试查询时得到的优化计划,其中只有两个值对
IN()
条款。这个
type: index
不是个好兆头。这意味着它要扫描整个主键索引。这几乎和表格扫描一样糟糕。这个
Using temporary
这也是一项代价高昂的行动。我猜这是在创建一个临时表来存储IN()
谓语。下面是一个等价的查询,它可以在不使用元组比较的情况下获得更好的优化计划:
这个
type: range
是一个更有利的优化。没有临时工作台。