實戰5:如何優化你的SQL查詢
1. 前言
在前面的小節和實戰中,我們一直在學習和討論如何寫 SQL,如何用 SQL 完成一個業務功能點。本小節,我們將以優化
的角度來探討一下如何優化 SQL,讓 SQL 更加高效的執行。
SQL 優化是一個很大的專題,本節會介紹幾種常見的 SQL 優化手段和一些好用的優化工具。
2. 工具
SQL 優化並不簡單
,因此我們可以利用一些工具來幫助我們。
2.1 soar
soar
是小米開源的一款 SQL 優化和改寫的工具,它使用簡單而且特性十分豐富,你可以點選此連結來安裝 soar,安裝成功後,我們來一起使用一下 soar。
2.1.1 soar 例項
舉個簡單的例子:
soar -query 'SELECT * FROM imooc_user WHERE id=1;'
soar 的使用十分簡單,通過query
引數指定一條需要分析的SQL語句即可,呼叫成功後,soar
會自動在控制檯打印出分析結果,如下:
# Query: 93A5517F0971C47A ★ ★ ★ ★ ☆ 95分 ```sql SELECT * FROM imooc_user WHERE id= 1 ``` ## 不建議使用 SELECT * 型別查詢 * **Item:** COL.001 * **Severity:** L1 * **Content:** 當表結構變更時,使用 \* 萬用字元選擇所有列將導致查詢的含義和行為會發生更改,可能導致查詢返回更多的資料。
soar 分析的結果預設以markdown
的格式展現,且分析結果十分豐富,不僅給出了格式化後易讀的 SQL 和建議,還打了分。
其中Item
是規則程式碼,每個規則都有相應的代號,Severity
是等級,等級越高代表越危險,越需要優化,L1
是較低的等級,Content
指明瞭優化原因。
2.1.2 soar 優化 SQL
上面的語句中,建議不使用*
,因為欄位變更將導致資料發生變化,按照 soar 的提示我們優化一下 SQL:
soar -query 'SELECT id,username,age FROM imooc_user WHERE id=1;' > profile.md
我們不僅優化了*
profile.md
檔案,內容如下:
# Query: 54BE4DEFF01C4432
★ ★ ★ ★ ★ 100分
```sql
SELECT
id, username, age
FROM
imooc_user
WHERE
id= 1
```
## OK
優化後,直接獲得了 100 分(滿分)。
soar 是一款簡單且好用的工具,它還有很多特性值得大家去挖掘和探索,你可以點開它的文件去觀閱一番,對於它的介紹這裡也將告一段落了。
2.2 EXPLAIN
explain
是資料庫自帶的 SQL 分析工具,簡單、實用且強大。下面我們以 MySQL 的explain
工具為例來介紹一下它的使用。
請先執行一下語句方便進行測試:
DROP TABLE IF EXISTS imooc_user;
CREATE TABLE imooc_user
(
id int PRIMARY KEY,
username varchar(20),
age int
);
INSERT INTO imooc_user(id,username,age)
VALUES (1,'peter',18),(2,'pedro',24),(3,'jerry',22),(4,'mike',18),(5,'tom',20);
2.2.1 使用 explain
explain
的使用很簡單,在它的後面接上需要分析的 SQL 語句即可,如下:
EXPLAIN SELECT * FROM imooc_user WHERE id=1;
執行成功後,得到如下結果:
+----+-------------+------------+-------+---------------+---------+-------+------+----------+--------+
| id | select_type | table | type | possible_keys | key | ref | rows | filtered | Extra |
+----+-------------+------------+-------+---------------+---------+-------+------+----------+--------+
| 1 | SIMPLE | imooc_user | const | PRIMARY | PRIMARY | const | 1 | 100.0 | <null> |
+----+-------------+------------+-------+---------------+---------+-------+------+----------+--------+
我們並未貼上全部結果,而是選取了其中重要的部分。id
是SELECT
語句的 id,select_type
代表這次查詢僅僅是一條簡單的查詢,table
無需贅言,possible_keys
表示可能用到的索引,extra
是一些額外資訊。
而剩下的就是一些比較重要的資訊了:
type
是針對單表的訪問方法型別,const
是常數型別,表示查詢速度極快,在常數時間內即可返回;key
表示使用到的索引,PRIMARY
表示用到了主鍵索引;ref
意思是使用索引等值查詢時,與索引列比較的物件資訊,這個比較抽象,大致的意思是,索引使用了何種型別進行比較,const
即使用常數比較,id 1 就是常數;rows
是預估需要讀取記錄的條數,1
代表只需要讀取一行,rows 越小越好;filtered
表示查詢過濾後未搜尋到的記錄百分比,100.0
表示未搜尋到的幾乎佔100%
,filtered 越大越好。
因此從分析結果可以看出,這條語句效能極好,除非資料庫波動,否則完全不用擔心查詢速度問題。
2.2.2 explain 優化 SQL
那麼什麼樣的語句查詢效率比較低了,我們看一下這個語句:
EXPLAIN SELECT * FROM imooc_user WHERE age=22;
分析結果如下:
+----+-------------+------------+------+---------------+--------+--------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | ref | rows | filtered | Extra |
+----+-------------+------------+------+---------------+--------+--------+------+----------+-------------+
| 1 | SIMPLE | imooc_user | ALL | <null> | <null> | <null> | 5 | 20.0 | Using where |
+----+-------------+------------+------+---------------+--------+--------+------+----------+-------------+
我們仍然截取了部分資訊,我們將目光聚焦在type
和rows
上,這裡的type
不再是const
而是ALL
,ALL
表示全表掃描,是最慢的一個級別,rows
為5
,表示這次查詢將會掃描5
條記錄,而我們總共才5
條記錄。
這個查詢的效能是極為糟糕的,試想一下,如果該表的資料是幾萬行乃至幾十萬行,一次查詢得掃描全部,那得多慢啊。
既然這麼慢,可以優化嗎?當然可以,如果你有相關的經驗,第一個想到的就是建索引。
CREATE INDEX age_index ON imooc_user(age);
索引建立完畢後,我們再次分析:
EXPLAIN SELECT * FROM imooc_user WHERE age=22;
+----+-------------+------------+------+---------------+-----------+-------+------+----------+--------+
| id | select_type | table | type | possible_keys | key | ref | rows | filtered | Extra |
+----+-------------+------------+------+---------------+-----------+-------+------+----------+--------+
| 1 | SIMPLE | imooc_user | ref | age_index | age_index | const | 1 | 100.0 | <null> |
+----+-------------+------------+------+---------------+-----------+-------+------+----------+--------+
type
從ALL
變成了ref
,rows
也僅僅只有1
行;ref
也是一種速度很快的型別,即查詢使用到了常數匹配索引,在結果中key
欄位也指明瞭,該次查詢有使用到我們新建的索引age_index
。
explain 的內容很多,而且不同的資料庫的實現也不同,如果你需要使用它,請按照你使用的資料庫查閱該資料庫權威的文件來學習。
3. 實踐
接下來,我們以實踐的角度來看一個面試題——一條SQL語句執行的很慢,導致慢的原因有哪些了?
。
首先,考慮到資料庫可能會有波動,我們分類來談論這個問題。
3.1 偶爾很慢,平時都 OK
一條語句在檢測的情況下,大部分時間都比較快,只是偶爾會突然很慢,那麼造成它慢的原因有很多種,我們挑幾個常見的:
- 資料庫在重新整理資料,寫磁碟:資料庫是以頁的形式來讀、寫資料的,突然有時候頁需要更新或者刪除了,資料庫就必須執行它,於是查詢就慢了下來。
- 資料庫在同步、備份:有時候資料庫會找個特定的時間備份那麼一次,剛好被你給撞到了,當然這個概率很低。
- 沒有鎖,我要等待別人釋放鎖:查詢的資料被別人鎖住了,我需要等待,自然就慢了。
3.2 一直很慢
如果出現某條語句一直都很慢的情況,那麼大概率是語句本身或者資料表索引的問題了。
- 沒有索引:如上面
age
欄位沒有索引,全表掃描,當然很慢。 - 沒走索引:有索引,可是因為使用函式或者模糊搜尋導致查詢沒有走索引;有索引,可是SQL語句不明確,導致資料庫走錯索引,應該優化SQL語句,或者
USING INDEX
強制使用索引。 - 語句本身:使用了
POW
,CONTACT
等函式使資料庫沒法走索引。
正如小節開頭所說,SQL 優化是一個很大的專題,一本極厚的書可能也無法全部囊括。不過這也不代表你無法學習,先熟練掌握幾個好用的工具,如本小節提到的兩個工具,然後慢慢的學習和實踐,相信你能在優化的路上走的很遠。
4. 小結
-
一般情況下,SQL 優化的落腳點其實就是
使用索引
,索引能夠大幅加快查詢速度,提高效能。 -
對於 SQL 語句本身的優化,除了
soar
以外,你也可以查閱相關的資料獲取經驗。