20

我试图弄清楚为什么我的查询之一很慢以及如何解决它,但我对我的结果有点困惑。

我有一个orders大约 80 列和 775179 行的表,我正在执行以下请求:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200

在 4.5 秒内返回 38 行

删除时ORDER BY我得到了很好的改进:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL LIMIT 200

0.30s 38 行

但是,在LIMIT不接触的情况下移除时,ORDER BY我得到了更好的结果:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC

0.10 秒内 38 行 (??)

为什么我的 LIMIT 这么饿?

走得更远

我在发送答案之前尝试了一些事情,在注意到我有一个索引creation_date(即 a datetime)之后,我删除了它,第一个查询现在在 0.10 秒内运行。这是为什么 ?

编辑

很好的猜测,我在 where 的其他列上有索引。

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200;
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys          | key        | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+
|  1 | SIMPLE      | orders | index | id_state_idx,id_mp_idx | creation_date | 5       | NULL | 1719 | Using where |
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+

一组中的 1 行(0.00 秒)

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC;
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+
| id | select_type | table  | type  | possible_keys          | key       | key_len | ref  | rows  | Extra                                              |
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+
|  1 | SIMPLE      | orders | range | id_state_idx,id_mp_idx | id_mp_idx | 3       | NULL | 87502 | Using index condition; Using where; Using filesort |
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+
4

4 回答 4

14

索引不一定会提高性能。为了更好地了解正在发生的事情,如果您包括explain不同的查询,这将有所帮助。

我最好的猜测是您有一个索引,id_state甚至id_state, id_mp可以用来满足该where子句。如果是这样,没有 的第一个查询order by将使用此索引。它应该很快。即使没有索引,这也需要对表中的页面进行顺序扫描orders,这仍然可以非常快。

然后,当您在 上添加索引时creation_date,MySQL 决定使用该索引代替order by. 这需要读取索引中的每一行,然后获取相应的数据页以检查where条件并返回列(如果有匹配)。这种读取效率非常低,因为它不是按“页面”顺序而是按索引指定的顺序。随机读取可能非常低效。

更糟糕的是,即使您有一个limit,您仍然必须阅读整个表,因为需要整个结果集。尽管您保存了对 38 条记录的排序,但您创建了一个非常低效的查询。

顺便说一句,如果orders表不适合可用内存,这种情况会变得更糟。然后,您会遇到一种称为“抖动”的情况,其中每条新记录都倾向于生成新的 I/O 读取。因此,如果一个页面上有 100 条记录,则该页面可能需要被读取 100 次。

您可以通过在orders(id_state, id_mp, creation_date). 该where子句将使用前两列,而order by将使用最后一列。

于 2013-07-19T14:16:51.600 回答
8

我的项目中发生了同样的问题,我做了一些测试,发现 LIMIT 由于行查找而很慢

请参阅: MySQL ORDER BY / LIMIT 性能:延迟行查找

所以,解决方案是:

(A) 使用 LIMIT 时,不要选择所有列,而只选择 PK 列

(B)选择你需要的所有列,然后加入(A)的结果集

SQL 应该喜欢:

SELECT
    *
FROM
    orders O1   <=== this is what you want
JOIN
    (
        SELECT
            ID                         <== fetch the PK column only, this should be fast
        FROM
            orders
        WHERE
            [your query condition]     <== filter record by condition
        ORDER BY
            [your order by condition]  <== control the record order
        LIMIT 2000, 50                 <== filter record by paging condition
    ) as O2
ON
    O1.ID = O2.ID
ORDER BY
    [your order by condition]          <== control the record order

在我的数据库中,

使用“LIMIT 21560, 20”选择所有列的旧 SQL 大约需要 4.484 秒。

新的 sql 只需 0.063s。新的大约快 71 倍

于 2016-12-02T08:14:47.937 回答
2

我在 250 万条记录的表上遇到了类似的问题。删除查询的限制部分需要几秒钟。随着限制部分它永远卡住。

我用子查询解决了。在你的情况下,它会变成:

SELECT * 
FROM 
    (SELECT * 
     FROM orders 
     WHERE id_state = 2 
       AND id_mp IS NOT NULL 
     ORDER BY creation_date DESC) tmp 
LIMIT 200

我注意到当所选行数大于限制参数时,原始查询速度很快。当限制参数无用时,查询变得非常慢。


另一种解决方案是尝试强制索引。在您的情况下,您可以尝试

SELECT * 
FROM orders force index (id_mp_idx) 
WHERE id_state = 2 
  AND id_mp IS NOT NULL 
ORDER BY creation_date DESC 
LIMIT 200
于 2020-10-04T06:52:25.043 回答
0

问题是mysql被迫动态排序数据。我对深度偏移的查询如下:

ORDER BY somecol LIMIT 99990, 10

耗时 2.5 秒。

我通过创建一个新表来修复它,该表按列 somecol 对数据进行了预排序,并且仅包含 id,并且深度偏移(无需使用 ORDER BY)需要 0.09 秒。

不过,0.1s 还不够快。0.01s会更好。

我最终将创建一个将页码保存为特殊索引列的表,因此 yi 将查询 where page = Z 而不是限制 x。

我刚试了一下,速度快到 0.0013。唯一的问题是,偏移量是基于静态数字的(例如,在页面中按 10 个项目预先排序.. 虽然问题不大.. 您仍然可以获取任何页面的任何数据。)

于 2021-06-26T15:14:42.110 回答