我有一个大表(250M 行),其列 group_id 将表大致分为组(group_id)。它具有以下索引:
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 过滤器都会导致它使用索引。我已经运行了分析表。
值得注意的是,行估计值相当高:
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版本:
版本 5.1.57-rel12.8-log,用于 x86_64 上的 debian-linux-gnu((Percona Server (GPL),12.8,修订版 233))
为什么mysql会选择一个它认为会涉及扫描257386914行而不是1883760行的计划?我知道它可能重视顺序读取,但为什么它会选择 2000 行的索引,而不是 200 行?为什么要按不同的组 id 过滤?
编辑:我也尝试过创建索引 (group_id, id, type_id) 以便仅使用索引扫描即可完成所有排序,但我无法让它选择该索引。