ときどき、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 に固執する必要があるため、オプティマイザーのヒントを使用できません。私が見つけた唯一の解決策は、これらの 1 フィールド インデックスを削除することです。
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;