Iva*_*yan 7 mysql query-optimization sql-execution-plan database-indexes
我不时会遇到奇怪的MySQL行为.假设我有索引(type,rel,created),(type),(rel).像这样的查询的最佳选择:
SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;
Run Code Online (Sandbox Code Playgroud)
将是使用索引(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
Run Code Online (Sandbox Code Playgroud)
和相同的查询,但添加了一个提示:
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
Run Code Online (Sandbox Code Playgroud)
我认为MySQL采用的执行计划在EXPLAIN命令的"rows"列中包含较少的数字.从这个角度来看,4343行的索引交集看起来比使用8906行的组合索引要好得多.那么,也许问题在于这些数字?
mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
| 3056 |
+----------+
Run Code Online (Sandbox Code Playgroud)
由此可以得出结论,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;
Run Code Online (Sandbox Code Playgroud)
Qua*_*noi 12
很难确切地说出为什么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'
Run Code Online (Sandbox Code Playgroud)