Java架構直通車——一條SQL語句執行很慢的原因有哪些?

引入

之前一直沒有很詳細的討論這個問題,只是將問題的解決方法歸於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語句長期慢查詢。

其他原因

其他原因發生的場景比較特殊,這裏只做一點簡單介紹。

比如:

  1. 從運行得很慢得外部服務來獲取數據
  2. DNS查詢偶爾會有超時現象
  3. 網絡速度慢
  4. 內存不足

SQL一直執行很慢

如果這條 SQL 語句每次都執行的這麼慢,那就就要好好考慮下你的 SQL 書寫了,下面我們來分析下哪些原因會導致我們的 SQL 語句執行的很不理想。

沒有用到索引或者索引失效

  1. 字段沒有索引
    剛好你的 c 字段上沒有索引,那麼抱歉,只能走全表掃描了,你就體驗不會索引帶來的樂趣了,所以,這回導致這條查詢語句很慢。
  2. 字段有索引,但卻沒有用索引。
    • 出現了運算(不能是表達式的一部分)或者使用了函數。
    • 數據出現了隱式轉換
    • 複合索引的情況下,查詢條件不滿足索引最左的原則
    • 負向查詢(not , not in, not like, <>, != ,!>,!< ) 不會使用索引
    • Mysql估計使用索引比全表掃描慢
    • 以%開頭的LIKE查詢不能夠利用B-tree索引

沒有用到索引的情況很好想到,那麼有沒有其他情況導致變慢呢?

查詢出的數據量過大

針對這一點,可以理解爲:

  1. 查詢出來的行很多,可以採用多次查詢,其他的方法降低數據量。
  2. 查詢出來的列很多,比如返回了不必要的行和列。

硬件問題

比如說IO吞吐量的問題,或者說之前說的內存問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章