Why does MySQL not always use index for select query?

旧巷老猫 提交于 2020-06-01 06:21:31

问题


I have two tables in my database users and articles.

Records in my users and articles table are given below:

+----+--------+
| id | name   |
+----+--------+
|  1 | user1  |
|  2 | user2  |
|  3 | user3  |
+----+--------+


+----+---------+----------+
| id | user_id | article  |
+----+---------+----------+
|  1 |       1 | article1 |
|  2 |       1 | article2 |
|  3 |       1 | article3 |
|  4 |       2 | article4 |
|  5 |       2 | article5 |
|  6 |       3 | article6 |
+----+---------+----------+

Given below the queries and the respected EXPLAIN output.

EXPLAIN SELECT * FROM articles WHERE user_id = 1;

+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | articles | NULL       | ALL  | user_id       | NULL | NULL    | NULL |    6 |    50.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+



EXPLAIN SELECT * FROM articles WHERE user_id = 2;
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | articles | NULL       | ref  | user_id       | user_id | 5       | const |    2 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+


EXPLAIN SELECT * FROM articles WHERE user_id = 3;
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | articles | NULL       | ref  | user_id       | user_id | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+---------+---------+-------+------+----------+-------+

Looking at the EXPLAIN plans for my select queries, it seems that queries are not always using the indexes.

In case,

when user_id is 1, it doesn't use the key and scans the complete table.

otherwise, it uses the user_id key and scans only few rows.

Could you please explain why queries don't always use the index here?


回答1:


There are (probably) two BTrees involved in the queries you show. One BTree for the data, sorted by the PRIMARY KEY, which I assume is id. The other for the INDEX on user_id (again, I am guessing). When InnoDB (which I assume you are using) builds a "secondary index", such as INDEX(user_id), it silently tacks on the PK of the table. So, effectively it becomes a BTree containing two columns: (user_id, id) and sorted by that pair.

When the Optimizer looks at SELECT * FROM t WHERE user_id=?, it probed the table and discovered that "a lot" of rows had user_id = 1 and not many rows had the other values you tried.

The Optimizer has two (or more) ways to evaluate the queries like that --

Plan A (use the index): Here's what it does:

  1. Drill down the Index's BTree to find the first occurrence of user_id=2.
  2. There it will find an id.
  3. Use that id to drill down the data's BTree to find * (as in SELECT *).
  4. Move on to the next entry in the Index BTree. (This is actually rather efficient since it is really a "B+Tree"; see Wikipedia.)
  5. If found, loop back to step 2. If not found (no more index entries with user_id=2), exit.

Plan B (don't use the index -- useful for your user_id=1):

  1. Simply walk through the data BTree in whatever order.
  2. Skip any row that does not have user_id=1.

The bouncing back and forth between the two BTrees costs something. The Optimizer decided your =1 case would need to look at more than about 20% of the table and decided that plan B would be faster. That is, it deliberately ignored the INDEX.

There are a lot of factors that the Optimizer can't or doesn't estimate correctly, but generally picking between these two Plans leads to faster execution. (Your table is too small to reliably measure a difference.)

Other "Plans" -- If the index is "covering", there is no need to use the data BTree. If there is an ORDER BY that can be used, then the Optimizer will probably use Plan A to avoid the "filesort". (See EXPLAIN SELECT ...) Etc.



来源:https://stackoverflow.com/questions/62045770/why-does-mysql-not-always-use-index-for-select-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!