【问题标题】:Optimizing this piece of MYSQL Code for better execution time优化这段 MYSQL 代码以获得更好的执行时间
【发布时间】:2012-09-26 15:23:28
【问题描述】:

这段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

【问题讨论】:

  • 您是否尝试将where 子句放在子查询中?

标签: mysql sql database performance


【解决方案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        |
+----+-------------+------------+--------+-------------------------------+-----------------------+---------+------+-------+---------------------------------+

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

【讨论】:

    【解决方案2】:

    您可以将查询编写为:

    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 &gt; 100 具有高度选择性(例如,少于 1% 的记录符合此标准),这将提高性能。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多