优化MySQL索引以在不到一秒的时间内进行查询

时间:2015-08-13 01:20:37

标签: mysql indexing explain sql-execution-plan

非常简单的min-max no-join无嵌套SQL的查询时间超过2秒。

桌子结构:::

> DESCRIBE tbl;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| ...         | ...          | ...  | ... | ...     | ...            |
| created_at  | datetime     | YES  | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

该表包含10,000,000多行

表格中的指数:::

> SHOW INDEX IN tbl;
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tbl   |          0 | PRIMARY    |            1 | id          | A         |    10000545 |     NULL | NULL   |      | BTREE      |         |               |
| tbl   |          1 | created_at |            1 | created_at  | A         |          18 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)

SQL UNDER CONCERN ::: 查找最近10k条目的最小 - 最大日期时间

SELECT 
    min(created_at), 
    max(created_at) 
FROM tbl 
ORDER BY id DESC 
LIMIT 10000

THE CONCERN ::: 首先查询总是需要2秒多才能完成。在第一次选择之后,除非将新行插入表中,否则所有后续查询调用都需要不到0.001秒的时间。

首次拨打2.06秒:

> SELECT min(created_at), max(created_at) FROM tbl USE INDEX (created_at) ORDER BY id DESC LIMIT 10000;
+---------------------+---------------------+
| min(created_at)     | max(created_at)     |
+---------------------+---------------------+
| 2010-01-01 00:00:00 | 2015-12-28 00:00:00 |
+---------------------+---------------------+
1 row in set (2.06 sec)

0.00秒内的后续调用:

> SELECT min(created_at), max(created_at) FROM tbl USE INDEX (created_at) ORDER BY id DESC LIMIT 10000;
+---------------------+---------------------+
| min(created_at)     | max(created_at)     |
+---------------------+---------------------+
| 2010-01-01 00:00:00 | 2015-12-28 00:00:00 |
+---------------------+---------------------+
1 row in set (0.00 sec)

一旦新行添加到表中,它再次需要2秒以上才能完成,然后所有后续查询调用都需要不到0.001秒才能完成。

据我所知,每次插入新行时索引都会被重新洗牌。这样就可以了。但是,我的目标是 将第一个查询时间提高到不到几毫秒 ,因为在频繁更新的系统中,每个查询花费2秒以上的时间会降低性能。

查询计划的解释::: 说明语句显示查询几乎遍历表的所有行。所以我猜我有空间通过索引来改进。但是我应该索引什么?

> EXPLAIN SELECT min(created_at), max(created_at) FROM tbl ORDER BY id DESC LIMIT 10000;
+----+-------------+-------+-------+---------------+------------+---------+------+----------+-------------+
| id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows     | Extra       |
+----+-------------+-------+-------+---------------+------------+---------+------+----------+-------------+
|  1 | SIMPLE      | tbl   | index | NULL          | created_at | 9       | NULL | 10000545 | Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+----------+-------------+
1 row in set (0.00 sec)

1 个答案:

答案 0 :(得分:2)

您的原始查询未返回"最后10000个条目的最小/最大日期&#34 ;;在处理聚合函数后适用LIMIT,因此您要求"给我最大/最小日期,然后将其限制为前10k" ...并且只有一排。

您必须使用子查询:

SELECT min(created_at), max(created_at)
FROM (SELECT created_at
      FROM my_table
      ORDER BY id
      LIMIT 10000) subtable;

最好的选择是(id, created_at)上的索引,因为子查询只需要遍历索引,然后最小/最大查询只需要对10k个元素进行排序。