为什么 MySQL 选择范围扫描(慢)与索引扫描(快)

Why MySQL chooses range scan (slow) vs index scan (faster)

我 运行 遇到一个问题 table,描述如下:

mysql> desc myTable;
+---------------------+--------------+------+-----+---------+----------------+
| Field               | Type         | Null | Key | Default | Extra          |
+---------------------+--------------+------+-----+---------+----------------+
| id                  | int(11)      | NO   | PRI | NULL    | auto_increment |
| score               | int(11)      | YES  | MUL | 0       |                |
| created_at          | datetime     | YES  |     | NULL    |                |
| updated_at          | datetime     | YES  |     | NULL    |                |
| previous_score      | int(11)      | NO   | MUL | 0       |                |
+---------------------+--------------+------+-----+---------+----------------+

具有以下指标:

mysql> show indexes from myTable;
+-------+------------+-------------------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name                           | Seq_in_index | Column_name    | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| myTable |          0 | PRIMARY                          |            1 | id             | A         |          48 |     NULL | NULL   |      | BTREE      |         |               |
| myTable |          1 | index_my_table_on_previous_score |            1 | previous_score | A         |          48 |     NULL | NULL   |      | BTREE      |         |               |
| myTable |          1 | index_my_table_on_score          |            1 | score          | A         |          48 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+-------------------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

问题如下:

mysql> select count(*) from myTable where score > 10;
+----------+
| count(*) |
+----------+
|       48 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from myTable ignore index(index_my_table_on_score) where score > 10;
+----------+
| count(*) |
+----------+
|       48 |
+----------+
1 row in set (0.00 sec)

如您所见,使用索引产生的结果完整table扫描。由于我在 previous_score 列上有另一个索引,我决定使用 explain 来尝试进一步了解问题:

mysql> select count(*) from myTable where previous_score > 10;
+----------+
| count(*) |
+----------+
|       48 |
+----------+
1 row in set (0.00 sec)

因此,如您所见,既好又快。让我们进行 explain 比较:

mysql> explain select count(*) from myTable where score > 10;
+----+-------------+-------+-------+----------------------+----------------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys           | key                     | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------------+----------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTable | range | index_my_table_on_score | index_my_table_on_score | 5       | NULL |   24 | Using where; Using index |
+----+-------------+-------+-------+----------------------+----------------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select count(*) from myTable where previous_score > 10;
+----+-------------+-------+-------+-------------------------------+-------------------------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys                    | key                              | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+-------------------------------+-------------------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTable | index | index_my_table_on_previous_score | index_my_table_on_previous_score | 4       | NULL |   48 | Using where; Using index |
+----+-------------+-------+-------+-------------------------------+-------------------------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

作为附加信息,我正在执行的查询遍历了我 table (48) 的所有结果。

非常感谢任何解释或建议。

对于像这样的微小 tables,这里有一个比计时更好的方法:

FLUSH STATUS;
SELECT SQL_NO_CACHE ...;
SHOW SESSION STATUS LIKE 'Handler%';

汇总数字列是比较 SELECT 的一个变体与另一个变体的一个很好的指标。

两个 EXPLAINs 都说它们是 Using index -- 实际上是在构成索引的 BTree 中执行查询。没有 table 扫描。

请提供SHOW CREATE TABLE,它比DESCRIBE更具描述性。

如果 'cache' 是冷的,那可以解释 0.07 sec