web-dev-qa-db-ja.com

パーティションテーブルで使用されていないインデックス

これに似たテーブルがあります

 mysql> SHOW CREATE TABLE my_requests\G 
 *************************** 1.行** ************************* 
テーブル:my_requests 
テーブルの作成:CREATE TABLE `my_requests`(
 `rq_id` bigint(20)NOT NULL、
` t_id` bigint(20)NOT NULL、
 `u_id` bigint(20)DEFAULT NULL、
` rq_date` datetime DEFAULT NULL、
 `rq_type` tinyint(4)DEFAULT '1'、
` rq_creationdate` datetime NOT NULL、
 `rq_modifydate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP、
 `rq_ttotal` mediumint(8)unsigned DEFAULT '0'、
 KEY` index_rqcd`( `t_id`、` rq_date`)、
 KEY `idx_rq_u`(` u_id`)、
 KEY `idx_rq_id`(` rq_id`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8 
/*!50100 PARTITION BY LIST(to_days(rq_date))
 SUBPARTITION BY HASH(t_id)
 SUBPARTITIONS 10 
(PARTITION p736417 VALUES IN(736417)ENGINE = InnoDB、
 PARTITION p736419 VALUES IN(736419)ENGINE = InnoDB、
パーティションp736430の値S IN(736430)ENGINE = InnoDB、
 PARTITION p736439 VALUES IN(736439)ENGINE = InnoDB、
 PARTITION p736443 VALUES IN(736443)ENGINE = InnoDB、
 PARTITION p736444 VALUES IN (736444)ENGINE = InnoDB、
 PARTITION p736445 VALUES IN(736445)ENGINE = InnoDB、
 PARTITION p736446 VALUES IN(736446)ENGINE = InnoDB、
 PARTITION p736447 VALUES IN(736447) )ENGINE = InnoDB、
 PARTITION p736448 VALUES IN(736448)ENGINE = InnoDB、
 PARTITION p736449 VALUES IN(736449)ENGINE = InnoDB、
 PARTITION p736450 VALUES IN(736450)ENGINE = InnoDB、
 PARTITION p736451 VALUES IN(736451)エンジン= InnoDB、
 PARTITION p736452 VALUES IN(736452)エンジン= InnoDB、
 PARTITION p736453 VALUES IN(736453)エンジン= InnoDB 、
 PARTITION p736454 VALUES IN(736454)ENGINE = InnoDB、
 PARTITION p736455 VALUES IN(736455)ENGINE = InnoDB、
 PARTITION p736456 VALUES IN(736456)ENGINE = InnoDB、
 PARTITION p736457 VALUES IN(73 6457)ENGINE = InnoDB、
 PARTITION p736458 VALUES IN(736458)ENGINE = InnoDB、
 PARTITION p736459 VALUES IN(736459)ENGINE = InnoDB、
 PARTITION p736460 VALUES IN(736460) ENGINE = InnoDB、
 PARTITION p736461 VALUES IN(736461)ENGINE = InnoDB、
 PARTITION p736462 VALUES IN(736462)ENGINE = InnoDB、
 PARTITION p736463 VALUES IN(736463)ENGINE = InnoDB、
 PARTITION p736464 VALUES IN(736464)ENGINE = InnoDB、
 PARTITION p736465 VALUES IN(736465)ENGINE = InnoDB、
 PARTITION p736466 VALUES IN(736466)ENGINE = InnoDB、 
 PARTITION p736467 VALUES IN(736467)ENGINE = InnoDB、
 PARTITION p736468 VALUES IN(736468)ENGINE = InnoDB、
 PARTITION p736469 VALUES IN(736469)ENGINE = InnoDB)*/

日ごとのパーティションを使用すると、テーブルは巨大で数億のレジスターがあり、-最終日のデータを選択しようとしています=フルスキャンを回避し、最後のパーティションのみにアクセスします。

しかし、説明すると、インデックスは使用されません。

 EXPLAIN SELECT * FROM my_requests USE INDEX(index_rqcd)
 WHERE t_id <> -1 AND rq_date = DATE_SUB(now()、INTERVAL 1 DAY); 
 + ---- + ------------- + ------------- + ------ + -------------- -+ ------ + --------- + ------ + --------- + ------------- + 
 | id | select_type |テーブル|タイプ|可能性のあるキー|キー| key_len | ref |行|追加| 
 + ---- + ------------- + ------------- + ------ +- ------------- + ------ + --------- + ------ + --------- +- ----------- + 
 | 1 |シンプル| my_requests |すべて| index_rqcd | NULL | NULL | NULL | 4020468 |使用場所| 
 + ---- + ------------- + ------------- + ------ +- -------------- + ------ + --------- + ------ + --------- +- ------------ + 

編集:EXPLAIN PARTITIONSは、クエリがパーティションの一部にのみ影響することを示していますが、インデックスが使用されない理由がまだわかりません。

 mysql> EXPLAIN PARTITIONS SELECT * FROM my_requests 
 WHERE t_id <> -1 AND rq_date = DATE_SUB(now()、INTERVAL 1 DAY)\ G 
 ****** ********************* 1.行************************** * 
 id:1 
 select_type:SIMPLE 
 table:my_requests 
 partitions:p736467_p736467sp0、p736467_p736467sp1、p736467_p736467sp2、p736467_p736467sp7,467p467p736_p736_4673736p467_p736_4673736_p736_p736_p736_p736_p736_p736_p736_p736_467736 p736467_p736467sp9 
タイプ:ALL 
 possible_keys:index_rqcd 
キー:NULL 
 key_len:NULL 
 ref:NULL 
 rows:4064737 
 Extra:where 
 1行をセットで使用(0.06秒)

このインデックスがそのクエリに使用されない理由を理解するのに役立ちますか?

2
pconcepcion

ここにいくつかの問題があります:

まず、jkavalikがOPのコメントで述べているように、インデックスの列の順序が重要です。基本的に、index_rqcdrq_dateのフィルタリングに使用する場合、t_idを使用してrq_dateを「表示」してフィルタリングする必要があります。通常、クエリに対してインデックスで実行できる範囲スキャンは1つだけであり、それは使用されるインデックスの最後の部分でなければならないため、オプティマイザはこのインデックスの使用を正しくスキップします。インデックスで、次にテーブルまたはこの場合はパーティションでフルスキャンします。テーブルにrq_dateで始まるインデックスがある場合、そのインデックスが使用される可能性があります。

次に、パーティションを使用するときに注意する必要があるのは、MySQLにはパーティションテーブルのグローバルインデックスがないということです。つまり、各パーティションには独自のインデックスがあるため、そのパーティション内でのみ使用できます。オプティマイザは、クエリがクエリとテーブル定義(パーティションプルーニングと呼ばれる最適化)に基づいてパーティションp736467を使用するだけでよいことを知っているので、クエリを実行します。したがって、オプティマイザはパーティションp736467のみを使用しているので、index_rqcdの使用を検討できますが、それでも最初の問題の影響を受け、その使用はインデックスのフルスキャンにつながるため、データのフルスキャンを実行します。タイプのEXPLAINALLを示していますが、パーティションのプルーニングのため、テーブル全体ではなく、p736467パーティションのデータのみをスキャンしています。 rq_dateで始まるインデックスを追加しても、パーティションが存在する日のすべての行を取得する場合は使用されません。ただし、指定したクエリでは、1日前から2日目までの行のみが返され、何をしたいかを説明するときに1日が返されるわけではありません。 1日ではなく特定の時間の行を取得したい場合は、rq_dateで始まるインデックスが便利です。

おそらくここで最も重要なことは、jkavalikが投稿したリンクで述べられているように、正当な理由がない限りパーティションを使用すべきではないということです。時間範囲はパーティションの有効な使用法ですが、通常、時系列データのスライディングウィンドウで使用する場合のみです。毎日除去される7日間のログメッセージ。一般に、すべてのクエリでパーティションプルーニングを使用できない限り、すべてのパーティションにアクセスする必要があるため、パフォーマンスが低下します。特定の日付の行についてこのテーブルを常にクエリし、他のいくつかのインデックス付き列の範囲でフィルタリングする場合は、パーティションが役立つ場合があります。多くの場合、パーティションは適切な方法ではありません。 veryサブパーティションを使用する十分な理由がない限り、ほとんどすべてのクエリにオーバーヘッドが追加されるだけです。

私の提案は、ほとんどのクエリがrq_dateの範囲に基づいていると仮定して、パーティションを使用せず、rq_dateで始まるインデックスを追加することです。

また、InnoDBを使用する場合は、常に主キーを定義する必要があります。保存しているデータについてはわかりませんが、rq_idは一意であり、適切な候補のようです。

4
G-Nugget

そのパーティショニングに関する3つの問題:

  • _BY HASH_は無用です。
  • SUBPARTITIONingは役に立たない。
  • 何百ものパーティションは非効率的です。

PARTITION BY RANGE(TO_DAYS(rq_date))のみを使用します。大きなメリットは「スライディングウィンドウ」にあります。

my partitioning blog のその他のコメント。

行の何パーセントに_t_id <> -1_がありますか?約20%を超える場合、_PRIMARY KEY__t_id_で始まる場合を除き、インデックスは使用されません。また、_t_id <> -1_を_t_id > -1_に変更できますか?これは、オプティマイザが「範囲」テストを実行するのに役立つ場合があります。 _<>_は最適化が困難です。

InnoDBは本当に_PRIMARY KEY_を明示的に指定することを望んでいます。列(または列の組み合わせ)のいずれかはすでに「一意」ですか?その場合、それは_PRIMARY KEY_である必要があります。ただし、最後に_rq_date_を追加する必要があります。 _rq_id_についての言及があったので、

_PRIMARY KEY(t_id, rq_id_, rq_date)
_

それは少し長くなっているので、多分それは最善ではありません。列をよりよく理解する必要があります。

1
Rick James