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