SQL 我為什麼慢你心裡沒數嗎?

2022-03-17 13:44:02 字數 3330 閱讀 9457

sql 語句執行慢的原因是面試中經常會被問到的,對於服務端開發來說也是必須要關注的問題。

在生產環境中,sql 執行慢是很嚴重的事件。那麼如何定位慢 sql、慢的原因及如何防患於未然。接下來帶著這些問題讓我們開啟本期之旅!

圖注:思維導圖

作為後端開發,日常運算元據庫最常用的是寫操作和讀操作。讀操作我們下邊會講,這個分類裡我們主要來看看寫操作時為什麼會導致 sql 變慢。

髒頁的定義是這樣的:記憶體資料頁和磁碟資料頁不一致時,那麼稱這個記憶體資料頁為髒頁。

那為什麼會出現髒頁,刷髒頁又怎麼會導致 sql 變慢呢?那就需要我們來看看寫操作時的流程是什麼樣的。

對於一條寫操作的 sql 來說,執行的過程中涉及到寫日誌,記憶體及同步磁碟這幾種情況。

圖注:mysql 架構圖

這裡要提到乙個日誌檔案,那就是 redo log,位於儲存引擎層,用來儲存物理日誌。在寫操作的時候,儲存引擎(這裡討論的是 innodb)會將記錄寫入到 redo log 中,並更新快取,這樣更新操作就算完成了。後續操作儲存引擎會在適當的時候把操作記錄同步到磁碟裡。

看到這裡你可能會有個疑問,redo log 不是日誌檔案嗎,日誌檔案就儲存在磁碟上,那寫的時候豈不很慢嗎?

其實,寫redo log 的過程是順序寫磁碟的,磁碟順序寫減少了尋道等時間,速度比隨機寫要快很多( 類似kafka儲存原理),因此寫 redo log 速度是很快的。

寫操作時 sql 慢的另一種情況是可能遇到了鎖,這個很容易理解。舉個例子,你和別人合租了一間屋子,只有乙個衛生間,你們倆同時都想去,但對方比你早了一丟丟。那麼此時你只能等對方出來後才能進去。

對應到 mysql 中,當某一條 sql 所要更改的行剛好被加了鎖,那麼此時只有等鎖釋放了後才能進行後續操作。

但是還有一種極端情況,你的室友一直占用著衛生間,那麼此時你該怎麼整,總不能尿褲子吧,多丟人。對應到mysql 裡就是遇到了死鎖或是鎖等待的情況。這時候該如何處理呢?

mysql 中提供了檢視當前鎖情況的方式:

通過在命令列執行圖中的語句,可以檢視當前執行的事務情況,這裡介紹幾個查詢結果中重要的引數:

當前事務如果等待時間過長或出現死鎖的情況,可以通過 「kill 執行緒id」 的方式釋放當前的鎖。

說完了寫操作,讀操作大家可能相對來說更熟悉一些。sql 慢導致讀操作變慢的問題在工作中是經常會被涉及到的。

在講讀操作變慢的原因之前我們先來看看是如何定位慢 sql 的。mysql 中有乙個叫作慢查詢日誌的東西,它是用來記錄超過指定時間的 sql 語句的。預設情況下是關閉的,通過手動配置才能開啟慢查詢日誌進行定位。

具體的配置方式是這樣的:

注意這裡只是臨時開啟了慢查詢日誌,如果 mysql 重啟後則會失效。可以 my.cnf 中進行配置使其永久生效。

知道了如何檢視執行慢的 sql 了,那麼我們接著看讀操作時為什麼會導致慢查詢。

(1)未命中索引

sql 查詢慢的原因之一是可能未命中索引,關於使用索引為什麼能使查詢變快以及使用時的注意事項,網上已經很多了,這裡就不多贅述了。

(2)髒頁問題

另一種還是我們上邊所提到的刷髒頁情況,只不過和寫操作不同的是,是在讀時候進行刷髒頁的。

是不是有點懵逼,別急,聽我娓娓道來:

為了避免每次在讀寫資料時訪問磁碟增加 io 開銷,innodb 儲存引擎通過把相應的資料頁和索引頁載入到記憶體的緩衝池(buffer pool)中來提高讀寫速度。然後按照最近最少使用原則來保留緩衝池中的快取資料。

那麼當要讀入的資料頁不在記憶體中時,就需要到緩衝池中申請乙個資料頁,但緩衝池中資料頁是一定的,當資料頁達到上限時此時就需要把最久不使用的資料頁從記憶體中淘汰掉。但如果淘汰的是髒頁呢,那麼就需要把髒頁刷到磁碟裡才能進行復用。

你看,又回到了刷髒頁的情況,讀操作時變慢你也能理解了吧?

知道了原因,我們如何來避免或緩解這種情況呢?

首先來看未命中索引的情況:

不知道大家有沒有使用 mysql 中 explain 的習慣,反正我是每次都會用它來檢視下當前 sql 命中索引的情況。避免其帶來一些未知的隱患。

這裡簡單介紹下其使用方式,通過在所執行的 sql 前加上 explain 就可以來分析當前 sql 的執行計畫:

執行後的結果對應的字段概要描述如下圖所示:

這裡需要重點關注以下幾個字段: 

1、type

表示 mysql 在表中找到所需行的方式。其中常用的型別有:all、index、range、 ref、eq_ref、const、system、null 這些型別從左到右,效能逐漸變好。

2、possible_keys

查詢時可能使用到的索引(但不一定會被使用,沒有任何索引時顯示為 null)。

3、key

實際使用到的索引。

4、rows

估算查詢到對應的記錄所需要的行數。

5、extra

比較常見的是下面幾種: 

對於刷髒頁的情況,我們需要控制髒頁的比例,不要讓它經常接近 75%。同時還要控制 redo log 的寫盤速度,並且通過設定 innodb_io_capacity 引數告訴 innodb 你的磁碟能力。

寫操作

讀操作

面試系列05 我的SQL為什麼這麼慢?

sql執行偶爾很慢 sql執行一直很慢 create index nameindex on tb user name 10 在tb user表以name欄位建立乙個名為nameindex的索引 explain select from tb user where name 鞠婧禕 通過關鍵字expla...

為什麼我建議你去寫作

閱讀本文大約需要 2.3 分鐘 我很早就有了寫作的打算,奈何自己一直下不了決心。內化知識 我們每天都在學習新的東西,但怎麼做才能真正的把這些知識內化呢?寫作便是最好的方式。某位詩人說 我寫的東西迫使我思考,原來我遠遠不是在思考我當時正在思考的東西 寫作可以為我們的思維開啟乙個新的方向,我們思考的東西...

為什麼我的並查集這麼慢!!!

在我剛開始使用並查集這種演算法的時候,我一直搞不明白為什麼別人總是說這種演算法很好,在做題的時候會經常用到。但是我自己用起來的時候時間總是會超,一直很納悶。今天重新做到一道別人都是使用並查集來解決的題目時,我才發現了我一直以來的錯誤!是真蠢呀!寫這篇部落格來吐槽一下自己,順便給自己留下乙個更深刻的印...