我不时遇到一种奇怪的mysql行为。假设我有索引(类型,rel,创建),(类型),(rel)。这样的查询的最佳选择:

SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;

将使用索引 (type, rel, created)。但是MySQL决定与索引相交 (type)(rel), ,这导致了更严重的表现。这是一个示例:

mysql> EXPLAIN
    -> SELECT id FROM tbl
    -> WHERE rel = 3 AND type = 3
    -> ORDER BY created\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl
         type: index_merge
possible_keys: idx_type,idx_rel,idx_rel_type_created
          key: idx_type,idx_rel
      key_len: 1,2
          ref: NULL
         rows: 4343
        Extra: Using intersect(idx_type,idx_rel); Using where; Using filesort

还有相同的查询,但提示添加了一个提示:

mysql> EXPLAIN
    -> SELECT id FROM tbl USE INDEX (idx_type_rel_created)
    -> WHERE rel = 3 AND type = 3
    -> ORDER BY created\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl
         type: ref
possible_keys: idx_type_rel_created
          key: idx_type_rel_created
      key_len: 3
          ref: const,const
         rows: 8906
        Extra: Using where

我认为MySQL采用了一个执行计划,该计划包含在“说明命令”的“行”列中的数字较少。从这个角度来看,与4343行的索引相交看起来比将我的组合索引与8906行使用更好。因此,也许问题在这些数字之内?

mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
|     3056 |
+----------+

由此我可以得出结论,MySQL在计算组合索引的大约行数时被误认为是错误的。

那么,在这里我该怎么做才能使MySQL采用正确的执行计划?

我无法使用优化器提示,因为我必须坚持使用Django Orm,我发现的唯一解决方案就是删除那些一场索引。

MySQL版本为5.1.49。

表结构是:

CREATE TABLE tbl (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` tinyint(1) NOT NULL,
  `rel` smallint(2) NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_type` (`type`),
  KEY `idx_rel` (`rel`),
  KEY `idx_type_rel_created` (`type`,`rel`,`created`)
) ENGINE=MyISAM;
有帮助吗?

解决方案

很难确切说明为什么 MySQL 选择 index_merge_intersection 在索引扫描上,但是您应该注意到,使用复合索引,为复合索引存储直至给定列的统计信息。

的价值 information_schema.statistics.cardinality 对于列 type 复合索引的基数将显示 (rel, type), , 不是 type 本身。

如果之间存在相关性 reltype, ,然后是基数 (rel, type) 将少于红衣主义的产物 reltype 从相应列上的索引分开拍摄。

这就是为什么错误计算的行数(交叉路口的大小不能大于联合)的原因。

你可以禁止 index_merge_intersection 通过将其设置为关闭 @@optimizer_switch:

SET optimizer_switch = 'index_merge_intersection=off'

其他提示

另一件事值得一提的是:如果您仅在type上删除索引,就不会遇到问题。不需要该索引,因为它重复了复合索引的一部分。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top