回答

收藏

为什么在查询中使用LIMIT时MySQL变慢?

技术问答 技术问答 561 人阅读 | 0 人回复 | 2023-09-14

我试图弄清楚为什么我的查询之一变慢以及如何解决它,但是我对结果有些困惑。
2 o7 o- |, d2 c( P# _  k% r# @& V我有一个orders约80列和775179行的表,并且正在执行以下请求:% s$ Y( P) J! d) E" s2 f$ O6 _
SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY5 J' j. t2 O- r) M# t9 p, H
creation_date DESC LIMIT 2007 f: z. `" ^3 }: r5 v
在4.5秒内返回38行
9 {! N) N2 H2 S1 e删除时,ORDER BY我得到了很好的改进:
( c4 E: y( a6 b& NSELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL LIMIT 200. z' f4 g9 }' C1 G6 s
0.30秒内38行
$ D" b; A/ c# {, I5 D但是,如果在LIMIT不触摸的情况下删除,ORDER BY我会得到更好的结果:1 T; q0 t$ E1 x. O' M
SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY
* u  v$ D5 N1 ?9 |' X9 ~9 B  Gcreation_date DESC
: @3 F- g  ^/ ~4 }" l/ h3 t5 v0.10s(??)中的38行5 s5 j# R- Y/ g5 J
为什么我的LIMIT这么饿?! a8 X3 U" m+ I& J; L' T
继续前进
4 g3 R4 ^$ f( N" f; m. g  k/ v在尝试发送答案之前,我尝试了一些尝试,并注意到我有一个索引creation_date(是datetime),因此我删除了它,并且第一个查询现在以0.10s的速度运行。这是为什么
4 A1 J0 w' r# o/ \+ d* E6 q/ V* d  y% U0 z8 I# Q: J% V) @
编辑) \+ }# P: P( Z* ]! z
很好的猜测,我在where的其他列上有索引。) i( `5 e! p0 `
mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200;& M4 @! y, k7 m2 a
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+
& i$ ?5 Z% i( j* y| id | select_type | table  | type  | possible_keys          | key        | key_len | ref  | rows | Extra       |
# O+ _/ u' D( e& v( F+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+2 |# p- H) g* ^" H9 }
|  1 | SIMPLE      | orders | index | id_state_idx,id_mp_idx | creation_date | 5       | NULL | 1719 | Using where |( B! \/ B4 N, e2 |5 g  ?/ [
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+
1 o0 Y- ^4 ?. W6 \1 n设置1行(0.00秒)$ v$ h7 z* Q  y& e
mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC;
7 J( {# d) s6 X2 u, O! M# L, u+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+; E! Z  X/ ^% b3 K
| id | select_type | table  | type  | possible_keys          | key       | key_len | ref  | rows  | Extra                                              |
; P& d; p3 t) a$ D+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+' J" g2 F5 b3 j9 b; x! ~
|  1 | SIMPLE      | orders | range | id_state_idx,id_mp_idx | id_mp_idx | 3       | NULL | 87502 | Using index condition; Using where; Using filesort |
/ V  j2 u7 y7 X  J; d7 u! {7 D) d+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+
$ V8 w0 C; Y1 A& x2 @  k9 T' Q  H% ?                ; L% m- @6 f- o2 _7 }- K6 T0 H
解决方案:# b: t9 N5 z& s
                5 S9 G; h0 y, n, v
6 m0 o) J* r2 ]# j: y
) ^5 F* W: B; f9 I
                索引不一定能提高性能。为了更好地了解正在发生的事情,如果您explain为不同的查询包括了,这将有所帮助。& \9 u$ V0 h* E; m  Z. E" I
我最好的猜测是您有一个索引,id_state甚至id_state, id_mp可以用来满足该where子句。如果是这样,则不order
) F$ Q+ o- C; K/ Lby使用的第一个查询将使用此索引。它应该很快。即使没有索引,这也需要对orders表中的页面进行顺序扫描,这仍然可以非常快。
" D: C2 }, N) G" V然后当您添加索引时creation_date,MySQL决定使用该索引代替order
/ W/ J# f% x# c  f( y& q; q' Mby。这需要读取索引中的每一行,然后获取相应的数据页以检查where条件并返回列(如果存在匹配项)。该读取效率极低,因为它不是按“页面”顺序,而是按索引指定的顺序。随机读取可能效率很低。! g0 J; \8 j5 ^( X$ ?
更糟糕的是,即使您有一个limit,您仍然必须读取 整个0 y0 R0 m, r! k2 @
表,因为需要整个结果集。尽管您已经保存了38条记录的排序,但是您创建了一个效率非常低下的查询。
" M% T0 R' ]" o# d+ _$ M6 e- T顺便说一句,如果orders表无法容纳在可用内存中,这种情况将变得更加严重。然后,您有一个称为“崩溃”的条件,其中每个新记录都倾向于生成一个新的I /
, v8 o! ?2 R2 KO读取。因此,如果一个页面上有100条记录,则该页面可能必须被读取100次。1 L- @: B3 T! n4 e3 ?
通过在上添加索引,可以使所有这些查询的运行速度更快orders(id_state, id_mp,, c9 x$ Z$ L1 T* F* [6 t2 ^
creation_date)。该where子句将使用前两列,而order by则将使用最后两列。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则