0

我有一个大表(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) 以便仅使用索引扫描即可完成所有排序,但我无法让它选择该索引。

4

1 回答 1

1

你有问题吗?

请注意,因为type_id必须检查列上的谓词,并且因为您的查询返回至少一个不在索引中的列,所以 MySQL 必须访问表的数据页,才能访问那些列。

因此,MySQL 可能更喜欢集群键,因为那是数据页所在的位置;集群键还允许 MySQL 避免排序操作(“使用文件排序”)。(我们确实注意到使用您的索引的执行计划也避免了排序操作。)

如果您希望 MySQL 支持您的索引,您可以考虑type_id在该索引中包含第三列,如果这是有选择性的。

或者,您可以考虑将查询修改为“ORDER BY group_id, id”以影响优化器。

您是否测量了带有提示和不带提示的查询的性能?

于 2013-01-16T22:00:33.433 回答