优化这段MYSQL代码以获得更好的执行时间

时间:2012-09-26 15:23:29

标签: mysql sql database performance

这段mysql代码

SELECT  id, value, LENGTH(stuffing)
FROM  t_limit ORDER BY id LIMIT 150000, 10

可以通过像这样重写来优化性能以获得更好的性能

注意:表格上有指数

SELECT  l.id, value, LENGTH(stuffing)
FROM    (
    SELECT  id
    FROM    t_limit
    ORDER BY
            id
    LIMIT 150000, 10
    ) o
JOIN    t_limit l
ON      l.id = o.id
ORDER BY
    l.id

价:HTTP://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/

现在如何以类似的方式优化这段代码

SELECT  id, value, LENGTH(stuffing)
FROM  t_limit where value>100 ORDER BY id LIMIT 150000, 10

2 个答案:

答案 0 :(得分:1)

上述帖子中提出的优化背后的基本思想是仅查询索引页面而不触及数据页面。如果查看非优化查询的查询计划:

SELECT  id, value, LENGTH(stuffing) AS len
FROM    t_limit
ORDER BY
        id
LIMIT 150000, 10

它将是:

+----+-------------+---------+------+---------------+------+---------+------+--------+----------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra
+----+-------------+---------+------+---------------+------+---------+------+--------+----------------+
|  1 | SIMPLE      | t_limit | ALL  | NULL          | NULL | NULL    | NULL | 200000 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+--------+----------------+

所以这是一个简单的表扫描。通过子查询优化,我们收到:

+----+-------------+------------+--------+-------------------------------+---------+---------+------+--------+---------------------------------+
| id | select_type | table      | type   | possible_keys                 | key     | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+--------+-------------------------------+---------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                          | NULL    | NULL    | NULL |     10 | Using temporary; Using filesort |
|  1 | PRIMARY     | l          | eq_ref | PRIMARY                       | PRIMARY | 4       | o.id |      1 |                                 |
|  2 | DERIVED     | t_limit    | index  | NULL                          | PRIMARY | 4       | NULL | 150010 | Using index                     |
+----+-------------+------------+--------+-------------------------------+---------+---------+------+--------+---------------------------------+

查看key列,其中显示最内层语句使用PRIMARY索引。我略微修改了您的查询,因此值类型兼容:

SELECT  l.id, value, LENGTH(stuffing) AS len
FROM    (
        SELECT  id
        FROM    t_limit
        where value like 'Value 1%'
        ORDER BY
                id
        LIMIT 30000, 10
        ) o
JOIN    t_limit l
ON      l.id = o.id
ORDER BY
        l.id

您需要考虑where条件的作用。如果你把它放在外部查询中,你只会过滤内部查询返回的10行 - 我想这不是你要求的。现在在所提出的情况下(内部语句中的where条件),您将最终得到一个表扫描,因为没有可以满足您查询的索引:

+----+-------------+------------+--------+---------------+---------+---------+------+--------+--------------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+--------+---------------+---------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL |     10 | Using temporary; Using filesort |
|  1 | PRIMARY     | l          | eq_ref | PRIMARY       | PRIMARY | 4       | o.id |      1 |                                 |
|  2 | DERIVED     | t_limit    | ALL    | NULL          | NULL    | NULL    | NULL | 200000 | Using filesort                  |
+----+-------------+------------+--------+---------------+---------+---------+------+--------+---------------------------------+

要从博客文章中提供的相同优化中获利,您需要一个额外的非聚集索引,例如。

create index NCIX_t_limit_id_value on t_limit(id, value)

现在,当您运行上述查询时,计划将是:

+----+-------------+------------+--------+-------------------------------+-----------------------+---------+------+-------+---------------------------------+
| id | select_type | table      | type   | possible_keys                 | key                   | key_len | ref  | rows  | Extra                           |
+----+-------------+------------+--------+-------------------------------+-----------------------+---------+------+-------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                          | NULL                  | NULL    | NULL |    10 | Using temporary; Using filesort |
|  1 | PRIMARY     | l          | eq_ref | PRIMARY,NCIX_t_limit_id_value | PRIMARY               | 4       | o.id |     1 |                                 |
|  2 | DERIVED     | t_limit    | index  | NULL                          | NCIX_t_limit_id_value | 66      | NULL | 30010 | Using where; Using index        |
+----+-------------+------------+--------+-------------------------------+-----------------------+---------+------+-------+---------------------------------+

我们再次只扫描索引页面。

答案 1 :(得分:0)

您可以将查询编写为:

SELECT l.id, value, LENGTH(stuffing)
FROM (SELECT  id
      FROM t_limit
      WHERE value > 100
      ORDER BY id
      LIMIT 150000, 10
     ) o JOIN
     t_limit l
     ON l.id = o.id
ORDER BY l.id

但是,这将 提高性能。 MySQL必须读取数据页以获得具有正确值的行。

您还可以使用其他索引:(value),(value,id)和(id,value)。这些会对性能产生不同的影响。

第一个将使用索引来满足WHERE子句,然后基本上忽略索引的“id”部分。如果value > 100具有高度选择性(例如,只有不到1%的记录符合此标准),这将提高性能。

第二个可能有所帮助。说实话,我不知道MySQL是否会从索引读取ID然后进行排序。或者,如果它将读取原始数据。如果是第一个,这将有所帮助。

第三种可能是最好的选择。我认为MySQL将读取索引以查找匹配的值,然后使用索引进行排序和限制。我的意思是,它应该,但我不是100%确定引擎实际上会这样做。