web-dev-qa-db-ja.com

MySQLはWHERE BETWEENを使用する場合、クエリはすべてのパーティションにヒットします

これはおそらく、MySQLパーティションの動作を誤解しているだけですが、次のように定義されたテーブルがあります。

  `ID` int(11) NOT NULL,
  `target_id` int(11) NOT NULL,
  `created_at` datetime NOT NULL,

  PRIMARY KEY (`ID`, `created_at`),
  KEY `index_created_at_target_id` (`created_at` desc, `target_id`)
  KEY `index_on_created_at` (`created_at`)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
PARTITION BY HASH(MONTH(`created_at`))
PARTITIONS 12

単一のcreated_atタイムスタンプでデータをクエリすると:

select * from my_table where `created_at`= '2018-12-00 05:00:00' AND target_id in (6,7,8);

その後、説明は1つのパーティションのみをヒットすることを知っています。これは正しいです。


ただし、範囲でクエリを実行した場合:

select * from my_table where `created_at` BETWEEN '2018-12-00 05:00:00' AND '2019-01-04 04:59:59' AND target_id in (6,7,8);

Explainはすべてのパーティションにヒットします。これは既知の制限ですか、それとも何か間違っているのですか?


サイドノート;他の「パーティション化」の質問から判断すると、「パーティション化は正しくインデックスを作成するのに役立たない」という応答がいくつかあると思います...私は自分で試していくつかのメトリックを収集しているので、ニーズに最適なソリューションを見つけることができます。 (その後、すべてのパーティションをチェックする場合でも、テーブルをパーティション化すると、クエリ時間を半分に削減できます。同じインデックスを持つ1つのモノリシックテーブルを使用する場合と比較して、2.3秒と0.8秒)


編集1:

両方のクエリがindex_on_created_atインデックスにヒットすることを確認しました。唯一の違いは、インデックスの「一意でないキールックアップ」と「インデックスレンジスキャン」です。

2
CodingWithSpike

PARTITIONingがほとんど役に立たない理由の1つを見つけました。

firstSELECTを使用しても、適切なインデックスを持つパーティション化されていないテーブルを使用した場合に比べて、パフォーマンス上の利点はありません。 j特に:

_INDEX(target_id, created_at)  -- in this order
_

secondSELECTは、 all パーティションを調べることにより、_PARTITION BY HASH_が範囲を処理する方法を示しています。それをするしかない。ええ、ええ、もっと賢いので、その日付範囲は2か月しかヒットしないはずです。それでも、適切なインデックスを使用してパーティション化されていない場合は、実行が速くなり、単純になります。

インデックスに関するその他のコメント:

_KEY `index_created_at_target_id` (`created_at` desc, `target_id`)
KEY `index_on_created_at` (`created_at`)
_

2つの問題があります。

  • INDEX(a,b), INDEX(a)-前者がその目的を果たすことができるので、後者を投げることもできます。
  • MySQL 8.0までは、DESCINDEX定義で無視されます。 (それに依存する_ORDER BY_がないため、害はありません。)

あなたの目標がどのパーティショニングを理解することである場合 can/cannot do、私はお勧めします my blog

その後、すべてのパーティションをチェックする場合でも、同じインデックスを持つ1つのモノリシックテーブルを使用するよりも、クエリ時間を半分に短縮できます。

私はそれに異議を唱えます。クエリキャッシュをオフにして実行しましたか?各テストを2回実行しましたか?buffer_pool_キャッシングの違いを避けるためですか?実際、キーはおそらく「同じインデックスを持つ」でしょう。 ほとんどの場合、パーティション分割と非パーティション分割を切り替えるときは、インデックスを修正する必要があります。

  • パーティション分割するときは、パーティションキーを後で必要な各インデックスに配置します。または、パーティションのプルーニングで十分な場合は省略します。
  • パーティション化されていない場合、範囲外の列はインデックスの初期にある必要があります。
  • パーティション化されていない場合、範囲列(日付範囲など)のインデックスが遅くなります。

ポイントクエリであっても、パーティショニングの速度が向上しないのはなぜですか。どちらの方法もほぼ同じ量の作業であるように見えることに注意してください。

  • 非パーティション:使用されるBTreeインデックスは、たとえば3レベルの深さです。ホップスキップジャンプ、あなたが望む行があります。
  • パーティション化:まず、目的のパーティション(「ホップ」)にプルーニングします。たとえば、BTreeの深さは2レベルしかない(「スキップジャンプ」)。

ああ。パーティショニングの5番目のユースケースを発見できるかもしれません。

1
Rick James