MySQL迫使MySQL使用它认为不可能的索引

时间:2017-07-27 19:21:43

标签: mysql

TL; DR; MySQL认为查询无法获得索引,但由于基数较低,使用所有值的速度仍然更快。有没有办法迫使MySQL总是使用复合索引,即使它认为它不是一个可能的索引?

完整的问题......

我有一个存储在MySQL中的队列系统的状态。队列项目被推送到队列服务器,但我们使用数据库确保仅处理节点上的依赖对象的1次处理。

在同一个表中跟踪多个队列,由queue_name varchar字段标识。任何项目的状态可以是queuedprocessingdonefailed之一。要快速计算或获取未完成的项目,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

这正确地使用了复合索引,并根据当前数据从1M行过滤到5-10。

对于相同的结果,这要快得多。我曾尝试告诉MySQL使用该索引,但查询计划程序似乎抛弃它并忽略。 E.g。

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也会决定不使用索引并执行全表扫描。这显然要慢得多。

1 个答案:

答案 0 :(得分:0)

我认为没有办法强迫它超越你已经做过的事情;虽然用queue_name IN ('default', 'email', 'order')之类的东西替换“强制”条件queue_name <> 'someimpossiblevalue'可能更简单(也更快)。

queue_name IS NULL可能更快,因为该字段定义为NOT NULL,允许MySQL优化器将其替换为“TRUE”;但它最终可能会忽略指数。值得一试。