引入
之前一直沒有很詳細的討論這個問題,只是將問題的解決方法歸於Explain方法和慢查詢日誌,這裏需要詳細的討論下SQL是如何慢下來的。
SQL變慢,可以大致上分爲偶爾執行很慢和一直很慢,後一種纔是上述解決方法針對的情況,而偶爾很慢實際上可能有很多原因導致。
所以這裏分類討論下兩種慢查詢的原因。
SQL偶爾執行很慢
一條 SQL 大多數情況正常,偶爾才能出現很慢的情況,針對這種情況,我覺得這條SQL語句的書寫本身可能是沒什麼問題的,而是其他原因導致的,那會是什麼原因呢?
數據庫在刷新髒頁
當我們要往數據庫插入一條數據、或者要更新一條數據的時候,我們知道數據庫會在內存中把對應字段的數據更新了,但是更新之後,這些更新的字段並不會馬上同步持久化到磁盤中去,而是把這些更新的記錄寫入到 redo log 日記中去,等到空閒的時候,在通過 redo log 裏的日記把最新的數據同步到磁盤中去。
實際上,redo log容量是有限的。那麼,如果數據庫很忙、更新很頻繁的情況下,如果redo log寫滿了,這個時候就沒辦法等到空閒的時候再把數據同步到磁盤的,只能暫停其他操作,全身心來把數據同步到磁盤中去的,而這個時候,就會導致我們平時正常的SQL語句突然執行的很慢,所以說,數據庫在在同步數據到磁盤的時候,就有可能導致我們的SQL語句執行的很慢了。
數據庫redo log日誌太小,默認單個文件只有50M,3個日誌文件循環寫,日誌的切換也需要花費時間。
數據庫緩存過期了
數據庫緩存擊穿:可能在某一時刻,緩存中的一些重要條目過期,導致大量請求落到MySQL以重新生成緩存條目。
另外,在內部刪除緩存算法的效率太低時,MySQL查詢緩存有時導致服務由短暫的停頓。
數據庫上鎖了
我們要執行的這條語句,剛好這條語句涉及到的表,別人在用,並且加鎖了,我們拿不到鎖,只能慢慢等待別人釋放鎖了。或者,表沒有加鎖,但要使用到的某個一行被加鎖了,這個時候,我也沒辦法啊。
如果要判斷是否真的在等待鎖,我們可以用 show processlist這個命令來查看當前的狀態。
針對這個問題,也可以理解成爲程序設計出了問題,也可能導致SQL語句長期慢查詢。
其他原因
其他原因發生的場景比較特殊,這裏只做一點簡單介紹。
比如:
- 從運行得很慢得外部服務來獲取數據
- DNS查詢偶爾會有超時現象
- 網絡速度慢
- 內存不足
SQL一直執行很慢
如果這條 SQL 語句每次都執行的這麼慢,那就就要好好考慮下你的 SQL 書寫了,下面我們來分析下哪些原因會導致我們的 SQL 語句執行的很不理想。
沒有用到索引或者索引失效
- 字段沒有索引
剛好你的 c 字段上沒有索引,那麼抱歉,只能走全表掃描了,你就體驗不會索引帶來的樂趣了,所以,這回導致這條查詢語句很慢。 - 字段有索引,但卻沒有用索引。
- 出現了運算(不能是表達式的一部分)或者使用了函數。
- 數據出現了隱式轉換
- 複合索引的情況下,查詢條件不滿足索引最左的原則
- 負向查詢(not , not in, not like, <>, != ,!>,!< ) 不會使用索引
- Mysql估計使用索引比全表掃描慢
- 以%開頭的LIKE查詢不能夠利用B-tree索引
沒有用到索引的情況很好想到,那麼有沒有其他情況導致變慢呢?
查詢出的數據量過大
針對這一點,可以理解爲:
- 查詢出來的行很多,可以採用多次查詢,其他的方法降低數據量。
- 查詢出來的列很多,比如返回了不必要的行和列。
硬件問題
比如說IO吞吐量的問題,或者說之前說的內存問題。