MySQL latency caused by using index; Using temporary; Using filesort

时间:2015-07-28 22:25:55

标签: mysql

I have a query that joins two tables and orders the data on the primary key. This is resulting in the very popular problem of MySQL "Using index; Using temporary; Using filesort."

The issue is causing a severe latency problem in my production tables with about 400k records.

Here's more info:

I have two tables: Doctor and Area. The Doctor table has a foreign key pointing to Area.

Doctor:

+-----------------------------+---------------+------+-----+---------+----------------+
| Field                       | Type          | Null | Key | Default | Extra          |
+-----------------------------+---------------+------+-----+---------+----------------+
| id                          | int(11)       | NO   | PRI | NULL    | auto_increment |
| area_id                     | int(11)       | NO   | MUL | NULL    |                |
+-----------------------------+---------------+------+-----+---------+----------------+

Doctor indexes:

+---------------+------------+------------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table         | Non_unique | Key_name               | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------------+------------+------------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| doctor        |          0 | PRIMARY                |            1 | id               | A         |        5546 |     NULL | NULL   |      | BTREE      |         |               |
| doctor        |          1 | doctor_dfd0e917        |            1 | area_id          | A         |          29 |     NULL | NULL   |      | BTREE      |         |               |
+---------------+------------+------------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

Area:

+------------------------+-------------+------+-----+---------+----------------+
| Field                  | Type        | Null | Key | Default | Extra          |
+------------------------+-------------+------+-----+---------+----------------+
| id                     | int(11)     | NO   | PRI | NULL    | auto_increment |
+------------------------+-------------+------+-----+---------+----------------+

And the Area indexes:

+---------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table         | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| area          |          0 | PRIMARY  |            1 | id          | A         |          24 |     NULL | NULL   |      | BTREE      |         |               |
+---------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

I'm trying to run the following query:

SELECT `doctor`.`id`, 
       `area`.`id` 
FROM 
       `doctor` 
INNER JOIN 
       `area` ON (`doctor`.`area_id` = `area`.`id`) 
ORDER BY 
      `doctor`.`id` DESC LIMIT 100;

The EXPLAIN returns the following (with the problematic Using index; Using temporary; Using filesort):

+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+
| id | select_type | table         | type  | possible_keys          | key                    | key_len | ref          | rows | Extra                                        |
+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+
|  1 | SIMPLE      | area          | index | PRIMARY                | PRIMARY                | 4       | NULL         |   24 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | doctor        | ref   | doctor_dfd0e917        | doctor_dfd0e917        | 4       | area.id      |  191 | Using index                                  |
+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+

If I remove the ORDER BY clause, I get the desired effect:

+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+
| id | select_type | table         | type  | possible_keys          | key                    | key_len | ref          | rows | Extra                                        |
+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+
|  1 | SIMPLE      | area          | index | PRIMARY                | PRIMARY                | 4       | NULL         |   24 | Using index                                  |
|  1 | SIMPLE      | doctor        | ref   | doctor_dfd0e917        | doctor_dfd0e917        | 4       | area.id      |  191 | Using index                                  |
+----+-------------+---------------+-------+------------------------+------------------------+---------+--------------+------+----------------------------------------------+

Why is the ORDER BY clause causing problems here even though I'm using the primary key?

Thank you in advance.

1 个答案:

答案 0 :(得分:0)

It seems that you only have one area per doctor. See how this query works:

SELECT d.id,
       (SELECT a.id FROM area a ON a.id = d.area_id) as area_id
FROM doctor d 
ORDER BY d.id DESC
LIMIT 100;

If you are using inner join to test for the presence of a doctor in the table, then add:

SELECT d.id,
       (SELECT a.id FROM area a ON a.id = d.area_id) as area_id
FROM doctor d 
WHERE EXISTS (SELECT 1 FROM area a ON a.id = d.area_id)
ORDER BY d.id DESC
LIMIT 100;

There is a good chance that both of these will scan the doctors table in order, picking up the information from area as needed.