7

我不时遇到奇怪的 MySQL 行为。假设我有索引(type、rel、created)、(type)、(rel)。像这样的查询的最佳选择:

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

将使用 index (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 采用 EXPLAIN 命令的“行”列中包含较少数字的执行计划。从这个角度来看,与 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;
4

2 回答 2

15

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

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

rel如果和之间存在相关性type,则 的 基数(rel, type)将小于 的 基数的乘积,rel并且type与相应列上的索引分开。

这就是行数计算不正确的原因(交集的大小不能大于并集)。

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

SET optimizer_switch = 'index_merge_intersection=off'
于 2010-12-24T14:02:38.810 回答
4

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

于 2010-12-24T19:38:12.580 回答