为什么MySQL使用索引交叉路口而不是组合索引?
-
13-10-2019 - |
题
我不时遇到一种奇怪的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
本身。
如果之间存在相关性 rel
和 type
, ,然后是基数 (rel, type)
将少于红衣主义的产物 rel
和 type
从相应列上的索引分开拍摄。
这就是为什么错误计算的行数(交叉路口的大小不能大于联合)的原因。
你可以禁止 index_merge_intersection
通过将其设置为关闭 @@optimizer_switch
:
SET optimizer_switch = 'index_merge_intersection=off'
其他提示
另一件事值得一提的是:如果您仅在type上删除索引,就不会遇到问题。不需要该索引,因为它重复了复合索引的一部分。