0

テーブルをグループ (group_id) に大まかに分割する列 group_id を持つ大きなテーブル (250M 行) があります。次のインデックスがあります。

mysql> show indexes from table\G;
*************************** 13. row ***************************
       Table: table
  Non_unique: 1
    Key_name: myindex
Seq_in_index: 1
 Column_name: group_id
   Collation: A
 Cardinality: 181819
    Sub_part: NULL
      Packed: NULL
        Null: YES
  Index_type: BTREE
     Comment: 
*************************** 14. row ***************************
       Table: table
  Non_unique: 1
    Key_name: myindex
Seq_in_index: 2
 Column_name: id
   Collation: A
 Cardinality: 213456239
    Sub_part: NULL
      Packed: NULL
        Null: 
  Index_type: BTREE
     Comment: 

次のクエリを実行したい:

mysql> explain select * from `table` WHERE (`table`.`type_id` IN (11, 17, 12, 19) AND `table`.`group_id` = 310248) ORDER BY `table`.`id` ASC LIMIT 201\G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: table
         type: index
possible_keys: [SOME INDEX NAMES]
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 257386914
        Extra: Using where
1 row in set (0.00 sec)

WHERE ... IN () のインデックス作成に問題があるため、いくつかの行をスキャンする必要があることを理解しています。しかし、驚くべきことに、主キー インデックスを使用して、可能な限り多くの行をスキャンすることを選択しています。

以下は、明白に (そして明らかに) 優れているようです。

mysql> explain select * from `table` USE INDEX (myindex) WHERE (`table`.`type_id` IN (11, 17, 12, 19) AND `table`.`group_id` = 310248) ORDER BY `table`.`id` ASC LIMIT 201\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: table
         type: ref
possible_keys: myindex
          key: myindex
      key_len: 5
          ref: const
         rows: 1883760
        Extra: Using where
1 row in set (0.00 sec)

LIMIT (2000) に大きな値を使用したり、異なる group_id の値を使用したり、ORDER BY を削除したり、type_id フィルターを削除したりすると、すべてインデックスが使用されます。ANALYZE TABLE を実行しました。

行の見積もりが非常に高いことに注意してください。

mysql> select count(*) from table where group_id=310248 and type_id in (11, 17, 12, 19) ;
+----------+
| count(*) |
+----------+
|   583868 |
+----------+
1 row in set (0.61 sec)

mysql バージョン:

Ver 5.1.57-rel12.8-log for debian-linux-gnu on x86_64 ((Percona Server (GPL), 12.8, Revision 233))

なぜ mysql は、1883760 行ではなく 257386914 行をスキャンする計画を選択するのでしょうか? 順次読み取りを評価する可能性があることは理解していますが、2000 行ではなく 2000 行のインデックスを選択するのはなぜですか? 別のグループ ID でフィルタリングするのはなぜですか?

編集済み:インデックス (group_id、id、type_id) の作成も試みたので、インデックス スキャンのみを使用してすべての並べ替えを実行できますが、そのインデックスを選択することはできません。

4

1 に答える 1

1

質問がありましたか?

列の述語をtype_idチェックする必要があり、クエリがインデックスにない少なくとも1つの列を返すため、MySQLはテーブルのデータページにアクセスして、それらの値にアクセスする必要があることに注意してください。列。

したがって、MySQLはクラスターキーを優先している可能性があります。これは、データページがそこにあるためです。クラスタキーを使用すると、MySQLでソート操作(「filesortの使用」)を回避することもできます。(インデックスを使用する実行プランでは、並べ替え操作も回避されることに注意してください。)

MySQLにインデックスを優先させたい場合type_idは、それがまったく選択的であれば、そのインデックスの3番目の列として含めることを検討してください。

または、クエリを「ORDER BY group_id、id」に変更して、オプティマイザに影響を与えることを検討してください。

ヒントありとヒントなしの両方で、クエリのパフォーマンスを測定しましたか?

于 2013-01-16T22:00:33.433 に答える