项目遇到一个MySQL查询特别慢的语句:
SELECT *
FROM (
SELECT DISTINCT t.vc_date, t.c_bankno, t.vc_bankacco, t.vc_moneytype, t.en_totalbala
FROM tbankaccobala t
WHERE 1 = 1
AND t.id IN (
-- 这个查询需要3s:
-- SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1)
-- FROM tbankaccobala
-- GROUP BY vc_bankacco
-- 但改成下面这样只要0.006s:
SELECT hhhh from(
SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1) as hhhh
FROM tbankaccobala
GROUP BY vc_bankacco
) as sbstr
-- 对IN的子查询做二次查询,或者把IN改为JOIN都可以解决IN速度奇慢的问题
)
) t
WHERE 1 = 1
上面语句空行处省略了一系列的其他表和 INNER JOIN 语句。
原语句导致前端页面10秒左右才有响应(但MySQL执行显示要4.6秒,phpMyAdmin也是10秒左右响应,为何?)
一开始怀疑是多表的JOIN操作导致速度变慢,但删去JOIN变成上面这段注释掉的语句之后,速度依然非常慢,显示要3s,
于是猜测 IN 才是导致速度变慢的主要因素,把IN的子查询用二次查询包起来之后时间就降低到0.006s!
EXPLAIN 未优化的语句:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | ALL | NULL | NULL | NULL | NULL | 1713 | ||
| 2 | DERIVED | t | ALL | NULL | NULL | NULL | NULL | 1713 | Using where; Using temporary |
| 3 | DEPENDENT SUBQUERY | tbankaccobala | ALL | NULL | NULL | NULL | NULL | 1713 | Using filesort |
(相关子查询是使用外部查询中的值的子查询)
EXPLAIN 优化的语句:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | ALL | NULL | NULL | NULL | NULL | 1713 | ||
| 2 | DERIVED | ALL | NULL | NULL | NULL | NULL | 1713 | Using where; Start temporary; Using temporary | |
| 2 | DERIVED | t | eq_ref | PRIMARY | PRIMARY | 98 | sbstr.hhhh | 1 | Using where; End temporary |
| 4 | DERIVED | tbankaccobala | ALL | NULL | NULL | NULL | NULL | 1713 | Using filesort |
我的理解:优化前,子查询是相关子查询,对于外部产生的每个值,都要执行一次子查询;优化后,子查询不再是相关子查询,只需要执行一次子查询并缓存中间结果,外部查到的每个值去缓存的中间结果遍历就行了。