一個(gè) 20 秒 SQL 慢查詢(xún)優(yōu)化處理方案
1.背景
頁(yè)面無(wú)法正確獲取數(shù)據(jù),經(jīng)排查原來(lái)是接口調(diào)用超時(shí),而最后發(fā)現(xiàn)是因?yàn)镾QL查詢(xún)長(zhǎng)達(dá)到20多秒而導(dǎo)致了問(wèn)題的發(fā)生。
這里,沒(méi)有高深的理論或技術(shù),只是備忘一下經(jīng)歷和解讀一些思想誤區(qū)。
2.復(fù)雜SQL語(yǔ)句的構(gòu)成
這里不過(guò)多對(duì)業(yè)務(wù)功能進(jìn)行描述,但為了突出問(wèn)題所在,會(huì)用類(lèi)比的語(yǔ)句來(lái)描述當(dāng)時(shí)的場(chǎng)景
復(fù)雜的SQL語(yǔ)句可以表達(dá)如下:
SELECT * FROM a_table AS a? LEFT JOIN b_table AS b ON a.id=b.id? WHERE a.id IN ( SELECT DISTINCT id FROM a_table? WHERE user_id IN (100,102,103) GROUP BY user_id HAVING count(id) > 3 )
3.關(guān)聯(lián)查詢(xún)
從上面簡(jiǎn)化的SQL語(yǔ)句,可以看出,首先進(jìn)行的是關(guān)聯(lián)查詢(xún)。
4.子查詢(xún)
其次,是嵌套的子查詢(xún)。此子查詢(xún)是為了找出多個(gè)用戶(hù)共同擁有的組ID。所以語(yǔ)句中的“100,102,103”是根據(jù)場(chǎng)景來(lái)定的,并且需要和后面“count(id) > 3”的個(gè)數(shù)對(duì)應(yīng)。簡(jiǎn)單來(lái)說(shuō),就是找用戶(hù)交集的組ID。
5.耗時(shí)在哪?
假設(shè)現(xiàn)在a_table
表的數(shù)據(jù)量為20W,而b_table的數(shù)據(jù)量為2000W。大家可以想一下,你覺(jué)得主要的耗時(shí)是在關(guān)聯(lián)查詢(xún)部分,還是在子查詢(xún)部分?
(思考空間。。。。)
(思考空間。。。。。。。)
(思考空間。。。。。。。。。。)
6.問(wèn)題定位
對(duì)于SQL底層的原理和高深的理論,我暫時(shí)掌握不夠深入。但我知道可以通過(guò)類(lèi)比和簡(jiǎn)單的測(cè)試來(lái)驗(yàn)證是哪一塊環(huán)節(jié)出了問(wèn)題。
7.初步斷定
首先,對(duì)于只有一個(gè)用戶(hù)ID時(shí),我會(huì)把上面的語(yǔ)句簡(jiǎn)化成:
ELECT * FROM a_table AS a? LEFT JOIN b_table AS b ON a.id=b.id? WHERE user_id IN (100)
所以,初步斷定應(yīng)該是嵌套的子查詢(xún)部分占用了大部分的時(shí)間。
9.再進(jìn)一步驗(yàn)證
既然定位到了是嵌套的子查詢(xún)語(yǔ)句的問(wèn)題,那又要分為兩塊待排查的區(qū)域:是子查詢(xún)本身耗時(shí)大,還是嵌套而導(dǎo)致慢查詢(xún)?
結(jié)果很容易發(fā)現(xiàn),當(dāng)我把子查詢(xún)單獨(dú)在DB中執(zhí)行時(shí),是非常快的。所以排除。
剩下的不言而喻,20秒的慢查詢(xún)是嵌套引起的。
但因?yàn)樘幱谏暇€(xiàn)緊急的過(guò)程中,為了確保,我快速地驗(yàn)證了我的結(jié)論:
- 1、將子查詢(xún)的ID單獨(dú)執(zhí)行,并把得到的結(jié)果序列手動(dòng)拼成一段ID,如:1,2,3,4, … , 999
- 2、將上面得到的序列ID,手動(dòng)替換到原來(lái)的SQL語(yǔ)句
- 3、執(zhí)行,發(fā)現(xiàn),很快!只用了約150 ms
Well Done! 準(zhǔn)備修復(fù)上線(xiàn)!
10.解決方案
線(xiàn)上的問(wèn)題,很多時(shí)間都是在定位問(wèn)題和分析原因,既然問(wèn)題找到了,原因也找到了,解決方案不言而喻。代碼簡(jiǎn)單處理即可。
11.另外一個(gè)需要注意的點(diǎn)
當(dāng)前,實(shí)際的SQL語(yǔ)句,會(huì)比這個(gè)更為復(fù)雜,但已足以表達(dá)問(wèn)題所在。但在前期,筆者也做了一些SQL的代碼。
因?yàn)?code>b_table比a_table
大,所以一開(kāi)始b_table
左關(guān)聯(lián)a_table
時(shí),很慢,大概是1秒多,而且數(shù)據(jù)量是很少的;但若反過(guò)來(lái),a_table
左關(guān)聯(lián)b_table
時(shí),則很快,大概是100毫秒。
所以,又發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:
大表 左關(guān)聯(lián) 小表,很慢;小表 左關(guān)聯(lián) 大表,很快。
當(dāng)然,這些我們理論上都知道,但實(shí)際開(kāi)發(fā)會(huì)忘卻。又或者一開(kāi)始兩個(gè)表都為空時(shí),而又沒(méi)考慮到后期這兩個(gè)表增長(zhǎng)的速度時(shí),日后就會(huì)埋下坑了。
總結(jié):
首先,嵌套的子查詢(xún)是很慢的。
原因,我還沒(méi)仔細(xì)去研究,但在下班的路上和我的同事交流時(shí),他說(shuō)曾經(jīng)看過(guò)這方面相關(guān)的書(shū)籍,是說(shuō)每一次的子查詢(xún)都會(huì)產(chǎn)生一個(gè)SQL語(yǔ)句,所以就N次查詢(xún)了。而另外一位資深的QA同事則跟我說(shuō),應(yīng)該是M*N的問(wèn)題。
其次,我一開(kāi)始使用嵌套子查詢(xún),是存在這樣一個(gè)誤區(qū):我覺(jué)得將這些操作交給MySQL自身來(lái)處理會(huì)更高效,畢竟DB內(nèi)部會(huì)有良好的機(jī)制來(lái)執(zhí)行這些查詢(xún)由。
然后,實(shí)際表白,我錯(cuò)了。因?yàn)檫@不是簡(jiǎn)單的合并MC批量查詢(xún)。
當(dāng)我們決定使用一些底層的技術(shù)時(shí),只有當(dāng)我們理解透徹了,才能使用更為恰當(dāng)。而因?yàn)闊o(wú)知就斷定工具、框架、底層無(wú)所不能時(shí),往往就會(huì)中招。
到此這篇關(guān)于一個(gè) 20 秒 SQL 慢查詢(xún)優(yōu)化的經(jīng)歷與處理方案的文章就介紹到這了,更多相關(guān) SQL 慢查詢(xún)優(yōu)化的經(jīng)歷與處理方案內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MySQL慢查詢(xún)優(yōu)化解決問(wèn)題
- Mysql慢查詢(xún)優(yōu)化方法及優(yōu)化原則
- mysql慢查詢(xún)優(yōu)化之從理論和實(shí)踐說(shuō)明limit的優(yōu)點(diǎn)
- 通過(guò)MySQL慢查詢(xún)優(yōu)化MySQL性能的方法講解
- MySQL慢查詢(xún)優(yōu)化之慢查詢(xún)?nèi)罩痉治龅膶?shí)例教程
- 美團(tuán)網(wǎng)技術(shù)團(tuán)隊(duì)分享的MySQL索引及慢查詢(xún)優(yōu)化教程
- 通過(guò)緩存+SQL修改優(yōu)雅地優(yōu)化慢查詢(xún)
相關(guān)文章
MySQL for update鎖表還是鎖行校驗(yàn)(過(guò)程詳解)
在MySQL中,使用for update子句可以對(duì)查詢(xún)結(jié)果集進(jìn)行行級(jí)鎖定,以便在事務(wù)中對(duì)這些行進(jìn)行更新或者防止其他事務(wù)對(duì)這些行進(jìn)行修改,這篇文章主要介紹了MySQL for update鎖表還是鎖行校驗(yàn),需要的朋友可以參考下2024-02-02Mysql刪除數(shù)據(jù)以及數(shù)據(jù)表的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Mysql刪除數(shù)據(jù)以及數(shù)據(jù)表的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11專(zhuān)業(yè)級(jí)的MySQL開(kāi)發(fā)設(shè)計(jì)規(guī)范及SQL編寫(xiě)規(guī)范
這篇文章主要介紹了專(zhuān)業(yè)級(jí)的MySQL開(kāi)發(fā)設(shè)計(jì)規(guī)范及SQL編寫(xiě)規(guī)范,需要的朋友可以參考下2020-11-11MySQL出現(xiàn)2003錯(cuò)誤的三種解決方法
本文主要介紹了MySQL出現(xiàn)2003錯(cuò)誤的解決方法,主要介紹了3種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09詳解MySQL的limit用法和分頁(yè)查詢(xún)語(yǔ)句的性能分析
本篇文章主要介紹了詳解MySQL的limit用法和分頁(yè)查詢(xún)語(yǔ)句的性能分析,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03mysql存儲(chǔ)過(guò)程之if語(yǔ)句用法實(shí)例詳解
這篇文章主要介紹了mysql存儲(chǔ)過(guò)程之if語(yǔ)句用法,結(jié)合實(shí)例形式詳細(xì)分析了mysql存儲(chǔ)過(guò)程中if語(yǔ)句相關(guān)原理、使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2019-12-12