クエリは簡単ですが、以下の実行計画は期待外れで最適とは言えません。
MySQL 5.7 を使用しています。これがフィドルです(5.6しか提供していませんが)。
CREATE TABLE `event` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(63) CHARSET ASCII COLLATE ASCII_BIN NOT NULL,
`is_sequenced` TINYINT(3) UNSIGNED NOT NULL,
`sequence_number` BIGINT(20) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Name-SequenceNumber` (`name`,`sequence_number`),
KEY `Name-IsSequenced` (`name`,`is_sequenced`,`id`)
) ENGINE=INNODB
;
INSERT INTO `event`
(id, `name`, is_sequenced, sequence_number)
VALUES
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL)
;
Name-IsSequenced
セカンダリ インデックスを使用します。次のことを試してみましょうEXPLAIN
。(クエリは Fiddle にあります。[実行計画の表示] を開いてEXPLAIN
結果を確認してください。)
EXPLAIN
SELECT * -- This part needs the PK
FROM `event` e
WHERE e.name = 'OrderCreated'
AND e.is_sequenced = 0
AND e.id <= 3
;
ここまでは順調ですね。Using index condition
理にかなっています: 条件全体は予想される indexName-IsSequenced
で解決でき、PK は の残りのデータを取得するために必要ですSELECT *
。
Using index
セカンダリ インデックスの一部のみを選択すれば、これを改善できるはずですよね? (PK は常にセカンダリ インデックスの一部であることに注意してください。ただし、セカンダリ インデックスの最後に含めることid
でこれを保証することもできます。結果は、本来あるべきものと同じです。)
EXPLAIN
SELECT id
FROM `event` e
WHERE e.name = 'OrderCreated'
AND e.is_sequenced = 0
AND e.id <= 3
;
さて、結果はUsing where; Using index
です。待って、それは…もっと悪い?! 私たちはそれをより少ない仕事にしました、そして計画はそれがより一生懸命働いていることを示しています.
Using index
達成可能であるべきです。範囲 whereを見つけ、その中name=OrderCreated
にサブ範囲 whereis_sequenced=0
を見つけ、その中にサブ範囲 where を見つけますid<=3
。
不思議なことに、私は ( PK を優先するのを防ぐために と組み合わせて) にUsing index
変更id<=3
することで取得できる他の実験 (いくつかのデータを使用) を持っています。違いの理由はわかりません。(これを Fiddle で試しても、おそらくデータ セットが小さいため、同じままです。)id=3
FORCE INDEX
実行計画が二次索引の予想される効率的な使用を示さない理由を誰か説明できますか? まっすぐにする方法はありますか?