我是架構(gòu)精進之路,點擊上方“關(guān)注”卜录,堅持每天為你分享技術(shù)干貨戈擒,私信我回復(fù)“01”,送你一份程序員成長進階大禮包艰毒。?
發(fā)現(xiàn)的一些問題
問題1
在過去的半年時間里筐高,研發(fā)團隊內(nèi)部嘗試抓了一波兒慢查詢SQL跟進處理率。發(fā)現(xiàn)有些同學(xué)對于慢查詢處理的思路就是看看有沒有用到索引,沒有用到就試圖加一個柑土,實在不行就甩鍋給這種情況是歷史設(shè)計問題或者自行判定為用戶特殊操作下觸發(fā)的小概率事件蜀肘,隨即便申請豁免掉...
這樣其實問題沒有根本上解決。
問題2
還有就是網(wǎng)絡(luò)上經(jīng)郴粒可以看到一些類似這樣的文章:
“慢SQL性能優(yōu)化大全”
“慢SQL性能優(yōu)化看這篇就夠了”
...... ?
其實內(nèi)容大同小異幌缝,要么建議加索引,要么建議重寫SQL....
怎么說呢诫欠?知識點是對的涵卵,但不全面,這個很容易誤導(dǎo)新同學(xué)荒叼,哈哈哈轿偎。
本文初衷
在業(yè)務(wù)項目發(fā)展過程中,我們常常會面對要處理 MySQL 慢查詢問題被廓,那我們應(yīng)該如何分析解決問題呢坏晦?
部分同學(xué)在處理MySQL慢查詢時候主要思路是加索引來解決,確實加索引是一個很好的解決問題的手段嫁乘,但不是全部昆婿。既然慢查詢作為問題,那就需要明確問題發(fā)生原因蜓斧,和解決問題路徑分析仓蛆,?授人以魚不如授人以漁,讓我們一起來解鎖 ?? 下MySQL處理慢查詢的正確姿勢挎春。
本文計劃主要讓大家搞明白查詢SQL為什么會變慢看疙,廢話不多說,直接開干~
寫在前面
在業(yè)務(wù)項目發(fā)展過程中直奋,我們常常會面對要處理 MySQL 慢查詢問題能庆,那我們應(yīng)該如何分析解決問題呢?
部分同學(xué)在處理MySQL慢查詢時候主要思路是加索引來解決脚线,確實加索引是一個很好的解決問題的手段搁胆,但不是全部。既然慢查詢是問題邮绿,那就需要明確問題發(fā)生原因渠旁,和解決問題路徑分析。我們一起來get下MySQL慢查詢的正確姿勢斯碌。
本文主要內(nèi)容包括:
1一死、查詢SQL執(zhí)行到底經(jīng)歷了什么?
2傻唾、查詢SQL為什么會慢?
1. 查詢SQL執(zhí)行到底經(jīng)歷了什么?
首先需要明確:一個查詢SQL的執(zhí)行到底經(jīng)歷了什么冠骄?
數(shù)據(jù)庫執(zhí)行SQL的大致流程如下:
1)建立與MySQL服務(wù)器連接(基礎(chǔ))
2)客戶端發(fā)送查詢SQL到數(shù)據(jù)庫伪煤,數(shù)據(jù)庫驗證是否有執(zhí)行的權(quán)限
3)MySQL服務(wù)器先檢查查詢緩存,如果命中了緩存凛辣,則立即返回存儲在緩存中的結(jié)果抱既,否則繼續(xù)流轉(zhuǎn);
4)MySQL服務(wù)器語法解析器扁誓,進行詞法與語法分析防泵,預(yù)處理
5)流轉(zhuǎn)至查詢優(yōu)化器生成執(zhí)行計劃
6)根據(jù)生成的執(zhí)行計劃,調(diào)用存儲引擎暴露的API來執(zhí)行查詢
7)將查詢執(zhí)行結(jié)果返回給客戶端
8)關(guān)閉MySQL連接
具體執(zhí)行過程可能會因MySQL服務(wù)器具體配置和執(zhí)行場景有一些差異蝗敢。
1)如未開啟應(yīng)用查詢緩存捷泞,則直接忽略查詢緩存的檢查;
2)執(zhí)行過程中寿谴,如同時對于被掃描的行可能加鎖锁右,同時也可能會被其他sql阻塞
2. 查詢SQL為什么會慢?
我們可以把查詢SQL執(zhí)行看做是一個任務(wù)的話讶泰,那它是由一些列子任務(wù)組成的咏瑟,每個子任務(wù)都存在一定的時間消耗。通常情況下痪署,導(dǎo)致慢查詢最根本的問題就是需要訪問的數(shù)據(jù)太多码泞,導(dǎo)致查詢不可避免的需要篩選大量的數(shù)據(jù)。
面對慢查詢狼犯,我們需要注意以下兩點:
1)查詢了過多不需要的數(shù)據(jù)
2)掃描了額外的記錄
2.1 查詢了過多不需要的數(shù)據(jù)
MySQL并不是只返回需要的數(shù)據(jù)浦夷,實際上會返回全部結(jié)果集再進行計算。
尤其是多表關(guān)聯(lián)查詢 select * 的情況辜王,我們是不是真的需要全部的列呢劈狐?如果不是,那我們直接指定對應(yīng)字段就好了呐馆。
例如我們要查詢用戶關(guān)聯(lián)訂單下的商品信息肥缔,如下所示:
SELECT *
FROM users
LEFT JOIN orders ON orders.user_id = users.user_id
LEFT JOIN goods ON goods.good_id = orders.good_id
WHERE users.name = 'zhangsan';
這將返回三個表的全部數(shù)據(jù)列,可以調(diào)整為僅取需要的列:
SELECT goods.title, goods.description
FROM users
LEFT JOIN orders ON orders.user_id = users.user_id
LEFT JOIN goods ON goods.good_id = orders.good_id
WHERE users.name = 'zhangsan';
取出全部列汹来,會讓優(yōu)化器無法完成索引覆蓋掃描這類優(yōu)化续膳,還會為服務(wù)器帶來額外的I/O、內(nèi)存和CPU的消耗收班。
2.2 掃描了額外的記錄
此種情況大部分屬于索引應(yīng)用不當造成的(包括:應(yīng)該建的索引沒有建坟岔,或者未應(yīng)用到最佳索引)。
示例表結(jié)構(gòu)如下:
CREATE TABLE `test_table` (
????`name` varchar(32) DEFAULT NULL,
????`desc` varchar(32) DEFAULT NULL,
????`age` int(16) DEFAULT NULL,
????`id` bigint(11) DEFAULT NULL,
????KEY `idx_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
存在索引?`idx_age`?的情況下摔桦,查詢執(zhí)行計劃結(jié)果展示如下:
EXPLAIN SELECT * FROM test_table WHERE age = 10;
預(yù)估訪問1行數(shù)據(jù)即可命中數(shù)據(jù)社付,如刪除有效索引 `idx_age`?后則會變成全表掃描(ALL)承疲,預(yù)估需要掃描121524條記錄才能完成這個查詢,如下圖所示:
總結(jié)
根據(jù)梳理 MySQL中的 SQL執(zhí)行過程我們發(fā)現(xiàn)鸥咖,任何流程的執(zhí)行都存在其執(zhí)行環(huán)境和規(guī)則燕鸽,其實產(chǎn)生慢SQL的本質(zhì)是:我們沒有按照數(shù)據(jù)庫的要求方式來執(zhí)行SQL。
主要導(dǎo)致慢查詢最根本的問題就是需要訪問的數(shù)據(jù)太多啼辣,導(dǎo)致查詢不可避免的需要篩選大量的數(shù)據(jù)啊研。
限于文章篇幅,同時為了大家更好的閱讀體驗鸥拧,后面會連續(xù)產(chǎn)出系列文章党远,最后,歡迎大家持續(xù)關(guān)注~
作者:架構(gòu)精進之路富弦,專注軟件架構(gòu)研究沟娱,技術(shù)學(xué)習(xí)與個人成長,關(guān)注并私信我回復(fù)“01”舆声,送你一份程序員成長進階大禮包花沉。
Thanks for reading!