0

クエリは簡単ですが、以下の実行計画は期待外れで最適とは言えません。

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=3FORCE INDEX

実行計画が二次索引の予想される効率的な使用を示さない理由を誰か説明できますか? まっすぐにする方法はありますか?

4

1 に答える 1