TL; DR; MySQL认为索引不可能用于查询,但由于基数较低,所以使用它的所有值仍然更快。有没有办法强制MySQL总是使用组合索引,即使它认为它不是可能的索引?MySQL迫使MySQL使用它认为不可能的索引
全部问题...
我存储在MySQL中的排队系统的状态。将队列项目推送到队列服务器,但是我们使用数据库来确保仅处理节点上的相关对象1次。
在同一个表中有多个队列被跟踪,由queue_name
varchar字段标识。任何项目的状态可以是queued
,processing
,done
或failed
之一。要快速计数或获取未完成项目,请在queue_name
+ status
上有一个复合索引。
queue_name
是一个非常低的基数列(目前只有3个可能的值)。
模式:
CREATE TABLE `queue` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`queue_name` varchar(255) NOT NULL,
`status` enum('queued','processing','done','failed') NOT NULL,
`payload` longtext NOT NULL,
PRIMARY KEY (`id`),
KEY `queue_queue_name_status_index` (`queue_name`,`status`)
) ENGINE=InnoDB;
时,取在processing
状态MySQL的所有项目执行全表扫描。
EXPLAIN SELECT * FROM queue WHERE status IN ('queued', 'processing');
select_type: SIMPLE
table: queue
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1036882
filtered: 50.00
Extra: Using where
这将如我所料,因为没有“合适的”索引来快速选择此选项。
然而,鉴于我知道queue_name
列具有非常低的基数,我也能达到同样的查询列出每一个可能的queue_name
值:
EXPLAIN SELECT * FROM queue
WHERE queue_name IN ('default', 'email', 'order') /* All values */
AND status IN ('queued', 'processing');
select_type: SIMPLE
table: queue
partitions: NULL
type: range
possible_keys: queue_queue_name_status_index
key: queue_queue_name_status_index
key_len: 767
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index condition; Using where
这正确地使用综合指数,从100万行的过滤器取决于当前的数据为5-10。
这对于相同的结果要快得多。我试图告诉MySQL使用该索引,但查询计划员似乎抛弃它并忽略它。例如。
EXPLAIN SELECT * FROM queue
FORCE INDEX (queue_queue_name_status_index)
WHERE status IN ('queued', 'processing');
select_type: SIMPLE
table: queue
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1037684
filtered: 50.00
Extra: Using where
这产生与未指定索引相同的解释和慢查询。索引没有显示为可能的索引,并且未被使用。
有没有办法迫使MySQL总是使用复合索引,即使它认为它不是一个可能的索引?查询规划器总是将其排除,因此即使使用FORCE INDEX
,MySQL也会决定不使用索引,而是进行全表扫描。这显然要慢得多。
首先创建状态列的索引 –
'分析表“来更新基数可能有帮助吗? – GhostGambler
@IlyaBursov当然,我们可以在状态列上单独创建一个单独的索引,但我宁愿我们的数据库只有一个可能的索引来维护。这是一个相当高的吞吐量表。鉴于MySQL已经不得不对这个领域进行分类索引,那么做两次就太浪费了。 – AndySavage