我不时遇到奇怪的 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;