一條SQL語句執行得很慢的原因有哪些?

2021-09-25 17:57:03 字數 2746 閱讀 3499

這個問題可以涉及到 mysql 的很多核心知識,我們從兩個方面進行詳解。

1、大多數情況是正常的,只是偶爾會出現很慢的情況。

2、在資料量不變的情況下,這條sql語句一直以來都執行的很慢。

針對這種情況,這條sql的書寫本身可能是沒有什麼問題的,而是其它原因導致的:

當我們要往資料庫插入一條資料、或者要更新一條資料的時候,我們知道資料庫會在記憶體中把對應欄位的資料更新了,但是更新之後,這些更新的字段並不會馬上同步持久化到磁碟中去,而是把這些更新的記錄寫入到 redo log 日記中去,等到空閒的時候,在通過redo log裡的日記把最新的資料同步到磁碟中去。

當記憶體資料頁跟磁碟資料頁內容不一致的時候,我們稱這個記憶體頁為「髒頁」。記憶體資料寫入到磁碟後,記憶體和磁碟上的資料頁的內容就一致了,稱為「乾淨頁」。

刷髒頁有下面4種場景(後兩種不用太關注「效能」問題):

我們要執行的這條語句,剛好這條語句涉及到的,別人在用,並且加鎖了,我們拿不到鎖,只能慢慢等待別人釋放鎖了。或者,表沒有加鎖,但要使用到的某個一行被加鎖了。

如果要判斷是否真的在等待鎖,我們可以用show processlist這個命令來檢視當前的狀態。

如果在資料量一樣大的情況下,這條 sql 語句每次都執行的這麼慢,那就就要好好考慮下你的 sql 書寫了。

沒有索引,只能走全表掃瞄,這回導致這條查詢語句很慢。

對索引列進行運算,如: select * from tb_a where sum - 1 = 1000; 雖然 sum 欄位上有索引,但是mysql並沒有使用索引,還是走全表掃瞄。

如果我們在查詢的時候,對欄位進行了函式操作,也是會導致沒有用上索引的,如: select * from tb_a where pow(sum, 2) = 1000; 假設函式 pow 是求 sum的 n 次方(實際並沒有pow這個函式),所以,語句並沒有使用索引。

4、選錯索引

我們知道,主鍵索引和非主鍵索引是有區別的,主鍵索引存放的值是整行字段的資料,而非主鍵索引上存放的值不是整行字段的資料,而且存放主鍵欄位的值

如: select * from tb_a where 100 < sum and sum < 100000;

語句走 sum 這個欄位的索引的話,最後先會查詢到對應主鍵的值,然後,再根據主鍵的值走主鍵索引,查詢到整行資料返回。

就算在 sum 欄位上有索引,系統也並不一定會走 sum 這個欄位上的索引,而是有可能會直接掃瞄掃瞄全表(查詢優化器),找出所有符合 100 < c and c < 100000 的資料。

分析:系統在執行這條語句的時候,會進行**:究竟是走 sum 索引掃瞄的行數少,還是直接掃瞄全表掃瞄的行數少,顯然,掃瞄行數越少當然越好了,因為掃瞄行數越少,意味著i/o操作的次數越少。如果是掃瞄全表的話,那麼掃瞄的次數就是這個表的總行數了,假設為 n;而如果走索引 sum 的話,我們通過索引 sum 找到主鍵之後,還得再通過主鍵索引來找我們整行的資料,也就是說,需要走兩次索引。而且,我們也不知道符合 100  < c and c < 10000 這個條件的資料有多少行,萬一這個表是全部資料都符合呢?這個時候意味著,走 sum 索引不僅掃瞄的行數是 n,同時還得每行資料走兩次索引。

重點1:系統是通過索引的區分度來判斷走全表掃瞄還是索引的,乙個索引上不同的值越多,意味著出現相同數值的索引越少,意味著索引的區分度越高。我們也把區分度稱之為基數,即區分度越高,基數越大。所以呢,基數越大,意味著符合 100 < c and c < 10000 這個條件的行數越少。所以,乙個索引的基數越大,意味著走索引查詢越有優勢。

重點2:系統是怎麼知道這個索引的基數的?系統當然是不會遍歷全部來獲得乙個索引的基數的,代價太大了,索引系統是通過遍歷部分資料,也就是通過取樣的方式,來**索引的基數的。有可能出現失誤的情況,也就是說,sum 這個索引的基數實際上是很大的,但是取樣的時候,卻很不幸地把這個索引的基數**成很小。例如你取樣的那一部分資料剛好基數很小,然後就誤以為索引的基數很小。系統就不走 sum 索引了,直接走全部掃瞄了。

結論:由於統計的失誤,導致系統沒有走索引,而是走了全表掃瞄,也是導致我們 sql 語句執行的很慢的原因。

注意:系統判斷是否走索引,掃瞄行數的**其實只是原因之一,這條查詢語句是否需要使用使用臨時表、是否需要排序等也是會影響系統的選擇的。

不過,我們可以通過以下方式強制走索引的方式來查詢,如:select * from tb_a force index(sum) where sum < 100 and sum < 100000; 

如果我們想查詢索引的基數是否符合實際,用 show index from tb_a; 語句來查詢;如果基數很不符合實際的話,可以用

analyze table tb_a; 這條命令來重新統計索引的基數。

既然會**錯索引的基數,這也意味著,當我們的查詢語句有多個索引的時候,系統有可能也會選錯索引,這也可能是 sql 執行的很慢的乙個原因。

四、總結

1、sql執行偶爾很慢的情況:

2、sql執行一直很慢的情況:

一條SQL語句執行得很慢的原因有哪些

一條 sql 語句執行的很慢,那是每次執行都很慢呢?還是大多數情況下是正常的,偶爾出現很慢呢?所以我覺得,我們還得分以下兩種情況來討論。1 大多數情況是正常的,只是偶爾會出現很慢的情況。2 在資料量不變的情況下,這條sql語句一直以來都執行的很慢。針對這兩種情況,我們來分析下可能是哪些原因導致的。一...

一條SQL語句執行得很慢的原因有哪些?

一條 sql 語句執行的很慢,那是每次執行都很慢呢?還是大多數情況下是正常的,偶爾出現很慢呢?所以我覺得,我們還得分以下兩種情況來討論。1 大多數情況是正常的,只是偶爾會出現很慢的情況。2 在資料量不變的情況下,這條sql語句一直以來都執行的很慢。針對這兩種情況,我們來分析下可能是哪些原因導致的。一...

一條SQL語句執行得很慢的原因有哪些?

說實話,這個問題可以涉及到 mysql 的很多核心知識,可以扯出一大堆,就像要考你計算機網路的知識時,問你 輸入url回車之後,究竟發生了什麼 一樣,看看你能說出多少了 一條 sql 語句執行的很慢,那是每次執行都很慢呢?還是大多數情況下是正常的,偶爾出現很慢呢?所以我覺得,我們還得分以下兩種情況來...