寫在前面
優(yōu)化慢SQL,這是在工作或者面試中都不可避免的問題,。這篇文章我們就來講講慢SQL的優(yōu)化的一些方法!
1. 升配
最簡單的一步就是升配!!當(dāng)然在降本增效的當(dāng)下,很難能將這種單子審批下來了!
2. 索引
MySQL一般會使用innodb作為存儲引擎,而innodb存儲引擎的索引結(jié)構(gòu)為B+樹,索引有啥用?
mysql的數(shù)據(jù)是存儲在磁盤里的,而磁盤的io的隨機(jī)讀寫是很慢的,因?yàn)樾枰粩嗟膶さ?#xff0c;而索引是為了加速查詢的速度,。
B+樹的結(jié)構(gòu)如下:葉子結(jié)點(diǎn)存儲數(shù)值,非葉子結(jié)點(diǎn)存儲指針和鍵值
。
如果我們不加索引,MySQL就會進(jìn)行全表掃描!如果加了索引,那么B+樹就會進(jìn)行走索引!那么就會少走很多的磁盤io,。
現(xiàn)在我們知道了為什么建索引,但并不是所有的字段都需要或者都需要建立索引的,因?yàn)樗饕彩钦伎臻g的,而且索引是有時候也會失效的!
也會影響到更新速度,這被稱為過度索引了,那么什么字段適合建立索引?什么字段不適合建立索引呢?
3. 什么字段適合建立索引?
大體可有以下四種情況:
- 針對查詢比較頻繁字段做索引,并且盡量選擇區(qū)分度高的列作為索引,
盡量建立唯一索引,區(qū)分度越高,使用索引的效率越高,。
- 盡量使用
聯(lián)合索引,減少單列索引,查詢時,聯(lián)合索引很多時候可以索引覆蓋,避免回表,提高查詢效率
。 - 索引應(yīng)該建立在小字段上,對于大的文本字段不要建立索引,。
- 要控制索引的數(shù)量,索引過多,維護(hù)索引結(jié)構(gòu)的代價很大,會影響
增刪改的效率
B+樹的本質(zhì)是一顆平衡樹,當(dāng)?shù)讓拥臄?shù)據(jù)量過大的時候,索引就會向上分裂,再合并,從而形成層,。
如果我們對性別這一類的字段建立索引,就會出現(xiàn)以下這種情況,這種情況其實(shí)和掃全表沒啥區(qū)別了,反而還浪費(fèi)了空間。
而如果是對一些重復(fù)度不高,、唯一性很強(qiáng)
的字段,比如用戶唯一id這種,建立索引,那么能很快速的找到這個數(shù)據(jù),。比如以下這種索引
3. 什么時候索引會失效?為什么?
3.1 使用了不等號(<> 或 !=)
SELECT id,username FROM user WHERE age <> 18;
索引適合用于查找具體值或范圍值
,如 =, <, >, BETWEEN 等操作,而 <> 或 != 運(yùn)算符要求 MySQL 查找所有不等于某個值的記錄,范圍模糊且不能明確定位
,索引不能高效定位這些記錄。`
3.2 OR 連接多個條件
SELECT id,username FROM user WHERE user_id = 1 OR age = 18;
當(dāng)使用 OR 連接多個條件時,MySQL 可能
無法有效地使用索引,。為什么是可能?因?yàn)镺R走索引與否,還和優(yōu)化器的預(yù)估有關(guān),就算連接條件都設(shè)置了索引,也可能因?yàn)榛乇韺?dǎo)致索引失效
,。
- 索引 + OR + 無索引的列:會先走索引列,但無索引的列會進(jìn)行全表掃描,所以還不如不走索引,直接都全表掃描完事。
- 索引 + OR + 索引,那么可能走索引,也可能不走索引,。
MySQL 會嘗試分別使用每個條件的索引,然后將結(jié)果合并,往往效率較低,。解決方法其實(shí)很簡單,只需要將OR拆開,用UNION、UNION ALL拼接起來,。
改成
SELECT id,username FROM user WHERE user_id = 1
UNION
SELECT id,username FROM user WHERE age = 18;
3.3 對索引列進(jìn)行函數(shù)操作
當(dāng)對索引列應(yīng)用函數(shù)(如 UPPER(), YEAR(), CONCAT() 等),MySQL 不能直接在索引中查找優(yōu)化后的數(shù)據(jù),而是必須先計算函數(shù)結(jié)果,然后再去查找匹配的行
,。這會使得索引失效。
eg:
SELECT id,username FROM user WHERE YEAR(birthdate) = 2024;
這里YEAR(birthdate) 是一個函數(shù),MySQL 不能直接利用 birthdate 列上的索引進(jìn)行查詢,而需要先計算 YEAR(birthdate),導(dǎo)致索引失效,。
3.4 隱式類型轉(zhuǎn)換
當(dāng)查詢條件中的數(shù)據(jù)類型與表字段的數(shù)據(jù)類型不匹配時,MySQL 會進(jìn)行隱式類型轉(zhuǎn)換,。在這個過程中,可能導(dǎo)致索引失效,因?yàn)樗饕腔谔囟ǖ臄?shù)據(jù)類型創(chuàng)建的,類型轉(zhuǎn)換后,可能無法高效利用索引。
eg:假設(shè)age是整數(shù)類型,但是卻使用字符串類型
SELECT * FROM user WHERE age = '20';
MySQL 需要在查詢時轉(zhuǎn)換 '20’ 為整數(shù)類型,可能會導(dǎo)致索引無法使用,。 某個電商平臺就有這么一個類似的bug,導(dǎo)致下單超時崩盤30分鐘,。
3.5 范圍查詢與其他條件混合
如果一個復(fù)合索引包含多個列,并且查詢中有范圍條件,如 BETWEEN, >, <,MySQL 會優(yōu)先根據(jù)范圍條件來 定位數(shù)據(jù),而忽略其他條件。這種情況下,索引可能只會應(yīng)用于范圍條件的列,其他列無法有效利用索引,。
示例:
SELECT id,username FROM user WHERE birthdate > '2024-01-01' AND age = 18;
假設(shè)有一個復(fù)合索引 (birthdate, age),由于 birthdate > '2024-01-01’ 是一個范圍查詢,MySQL 只會使用 birthdate 列的索引,而 age 列則可能無法使用索引,。
解決方法:盡量避免范圍查詢和精確查詢混合在同一個條件中,或者重新設(shè)計索引順序以適應(yīng)查詢的特點(diǎn)。
eg:改成復(fù)合索引為 (age,birthdate) 并且SQL語句如下:
SELECT id,username FROM user WHERE age = 18 AND birthdate > '2024-01-01';