0

这段mysql代码

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

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

注意:表在 Id 上有索引

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
4

2 回答 2

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        |
+----+-------------+------------+--------+-------------------------------+-----------------------+---------+------+-------+---------------------------------+

同样,我们只扫描索引页。

于 2012-09-27T15:00:22.260 回答
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% 确定引擎会真正做到这一点。

于 2012-09-27T14:04:32.177 回答