mysql in优化_MySQL的一次优化记录 (IN子查询和索引优化)
這兩天實(shí)習(xí)項(xiàng)目遇到一個(gè)網(wǎng)頁(yè)加載巨慢的問題(10多秒),然后定位到是一個(gè)MySQL查詢特別慢的語(yǔ)句引起的:
SELECT *
FROM (
SELECT DISTINCT t.vc_date, t.c_bankno, t.vc_bankacco, t.vc_moneytype, t.en_totalbala
, t.en_usablebala, t1.vc_nameinbank, date_format(t.D_IMPORTTIME, '%Y-%m-%d %H:%i:%S') AS D_IMPORTTIME
, t.vc_fundcode, t.c_datamode, t.vc_taskid, t.id, t.vc_projectname
, t.vc_projectcode, t1.c_accotype
, (
SELECT IF(vc_occurtime IS NULL, DATE_FORMAT(vc_occurdate, '%Y-%m-%d'), DATE_FORMAT(CONCAT(vc_occurdate, vc_occurtime), '%Y-%m-%d %H:%i:%S')) AS tradeTime
FROM tbanktradedetail_view
WHERE vc_bankacco = t.vc_bankacco
) AS d_tradetime, t3.vc_entry_caption AS C_ACCOTYPE_STR, t5.vc_entry_caption AS VC_BANKNAME, t4.vc_entry_caption AS VC_MONEYTYPE_STR
FROM tbankaccobala t
INNER JOIN tbankaccoinfo t1 ON t.vc_bankacco = t1.vc_bankacco
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '5087'
) t3
ON t1.c_accotype = t3.vc_entry_value
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '1004'
) t4
ON t1.VC_MONEYTYPE = t4.vc_entry_value
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '1014'
) t5
ON t.c_bankno = t5.vc_entry_value
WHERE 1 = 1
AND t.id IN (
-- this query will take 4.6s:
-- SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1)
-- FROM tbankaccobala
-- GROUP BY vc_bankacco
-- but the following query only takes 1.1s:
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
-- 對(duì)IN的子查詢做二次查詢
)
) t
WHERE 1 = 1
ORDER BY t.D_IMPORTTIME DESC
抽出查詢慢關(guān)鍵部分:
SELECT *
FROM (
SELECT DISTINCT t.vc_date, t.c_bankno, t.vc_bankacco, t.vc_moneytype, t.en_totalbala
-- 此處省略選擇多個(gè)列語(yǔ)句
FROM tbankaccobala t
-- 此處省略多張表連表查詢語(yǔ)句
WHERE 1 = 1
AND t.id IN (
-- 這個(gè)查詢需要3s:
SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1)
FROM tbankaccobala
GROUP BY vc_bankacco
)
) t
這個(gè)語(yǔ)句導(dǎo)致前端頁(yè)面10多秒才有響應(yīng)(但MySQL執(zhí)行顯示要4.6秒,phpMyAdmin也是10秒左右響應(yīng),為何?)
IN子查詢語(yǔ)句優(yōu)化
把IN語(yǔ)句里面的內(nèi)容改成下面這樣,只在外層再加一個(gè)select,就把3s的查詢縮短為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
-- 對(duì)IN的子查詢做二次select,或者把IN改為JOIN都可以解決速度奇慢的問題
原語(yǔ)句空行處省略了一系列的其他表和 INNER JOIN 語(yǔ)句。一開始懷疑是多表的JOIN操作導(dǎo)致速度變慢,但刪去JOIN變成上面這段注釋掉的語(yǔ)句之后,速度依然非常慢,顯示要3s,于是猜測(cè) IN 才是導(dǎo)致速度變慢的主要因素,改后只要0.006s,嘖…
EXPLAIN 未優(yōu)化的語(yǔ)句:
(相關(guān)子查詢是使用外部查詢中的值的子查詢)
EXPLAIN 優(yōu)化的語(yǔ)句:
我的理解:優(yōu)化前,子查詢是相關(guān)子查詢,對(duì)于外部產(chǎn)生的每個(gè)值,都要執(zhí)行一次子查詢;優(yōu)化后,子查詢不再是相關(guān)子查詢,只需要執(zhí)行一次子查詢并緩存中間結(jié)果,外部查到的每個(gè)值去緩存的中間結(jié)果里比對(duì)一下就行了。
(有人說(shuō)是能不能用索引的原因——這么說(shuō)應(yīng)該是不對(duì)的)
完整查詢的后端響應(yīng)速度對(duì)比:
前:
后:
索引優(yōu)化
對(duì)于這么小的數(shù)據(jù)規(guī)模,時(shí)間還是太長(zhǎng)了… 看前面explain執(zhí)行計(jì)劃的截圖,嗯,沒有索引…
給t1的vc_bankacco加上索引之后
解釋執(zhí)行計(jì)劃:
查詢和網(wǎng)頁(yè)響應(yīng)用時(shí)大幅縮短:
再看sql里還有三個(gè)join:
用的都是ot_dic_tdictionaryentry這張表的t4.vc_entry_value字段,那么試著給這個(gè)字段也加上索引吧,然后用時(shí)如下:
是的,時(shí)間反而變長(zhǎng)了!
explain執(zhí)行計(jì)劃:
所以變慢原因是:
沒加vc_entry_value的索引時(shí),會(huì)先用vc_entry_no選出一個(gè)數(shù)量很小的表,再和t1做join,
而加了vc_entry_value的索引之后,MySQL就把這個(gè)索引用了起來(lái),join語(yǔ)句被優(yōu)化為先FirstMatch(ot_dic_tdictionaryentry),這產(chǎn)生了一個(gè)1713*1713=2934369行的中間結(jié)果(笛卡爾乘積),然后才使用vc_entry_no進(jìn)行where過濾。
所以索引不能亂加啊,加錯(cuò)了反而會(huì)導(dǎo)致性能下降!這個(gè)示例里的查詢要加索引只能在vc_entry_no上索引,而不能在vc_entry_value上!
這個(gè)示例中主要提升是IN子查詢語(yǔ)句的優(yōu)化。在使用索引的情況下,對(duì)IN子查詢做優(yōu)化前后的查詢時(shí)間分別是3.1s和0.16s
總結(jié)
以上是生活随笔為你收集整理的mysql in优化_MySQL的一次优化记录 (IN子查询和索引优化)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: adb.exe是什么进程 adb.exe
- 下一篇: linux cmake编译源码,linu