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

2021-09-25 21:13:05 字數 4406 閱讀 7105

一直很慢的情況

資料庫自己選錯索引了

總結一條 sql 語句執行的很慢,那是每次執行都很慢呢?還是大多數情況下是正常的,偶爾出現很慢呢?所以我覺得,我們還得分以下兩種情況來討論。

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

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

針對這兩種情況,我們來分析下可能是哪些原因導致的。

一條sql在大多數情況下正常,偶爾出現很慢的情況,則多半不是這條sql語句的問題,而是其他原因導致的,可能有如下幾種原因

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

當記憶體資料頁跟磁碟資料頁內容不一致的時候,我們稱這個記憶體頁為「髒頁」。

記憶體資料寫入到磁碟後,記憶體和磁碟上的資料頁的內容就一致了,稱為「乾淨頁」。

刷髒頁有下面4種場景:

1.redo log寫滿了:redo log 裡的容量是有限的,如果資料庫一直很忙,更新又很頻繁,這個時候 redo log 很快就會被寫滿了,這個時候就沒辦法等到空閒的時候再把資料同步到磁碟的,只能暫停其他操作,全身心來把資料同步到磁碟中去的,而這個時候,就會導致我們平時正常的sql語句突然執行的很慢,所以說,資料庫在在同步資料到磁碟的時候,就有可能導致我們的sql語句執行的很慢了。

2.記憶體不夠用了:如果一次查詢較多的資料,恰好碰到所查資料頁不在記憶體中時,需要申請記憶體,而此時恰好記憶體不足的時候就需要淘汰一部分記憶體資料頁,如果是乾淨頁,就直接釋放,如果恰好是髒頁就需要刷髒頁。

3.mysql 認為系統「空閒」的時候:這時系統沒什麼壓力。

4.mysql 正常關閉的時候:這時候,mysql 會把記憶體的髒頁都 flush 到磁碟上,這樣下次 mysql 啟動的時候,就可以直接從磁碟上讀資料,啟動速度會很快。

這四種場景中會影響我們使用的主要是前兩種。

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

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

如果在資料量一樣大的情況下,這條sql語句每次都執行的這麼慢,那就要好好考慮下sql書寫的問題了,下面我們來分析下哪些原因會導致我們的sql語句執行的不理想。

我們先來假設我們有乙個表,表裡有下面兩個字段,分別是主鍵 id,和兩個普通字段 c 和 d。

create

table

`t`(

`id`

int(11)

notnull

,`c`

int(11)

default

null

,`d`

int(11)

default

null

,primary

key(

`id`))

engine

=innodb

;

沒有用上索引,這個原因應該是很多人都能想到的,例如你要查詢這條語句

select

*from t where

100100000

;

剛好你的 c 欄位上沒有索引,那麼抱歉,只能走全表掃瞄了,你就體驗不到索引帶來的樂趣了,所以,這條查詢語句很慢。

好吧,這個時候你給 c 這個字段加上了索引,然後又查詢了一條語句

select

*from t where c -1=

1000

;

這樣子在查詢的時候會用索引查詢嗎?

答是不會,如果我們在字段的左邊做了運算,那麼很抱歉,在查詢的時候,就不會用上索引了,所以呢,大家要注意這種欄位上有索引,但由於自己的疏忽,導致系統沒有使用索引的情況了。

正確的查詢應該如下

select

*from t where c =

1000+1

;

可能有人會說,右邊運算就能用上索引?難道資料庫就不會自動優化一下,自動把 c - 1=1000 自動轉換為 c = 1000+1?

不好意思,確實不會幫你,所以,你要注意了。

關於會導致索引失效的各種情況可以參考另一篇文章——mysql高效能優化建議

我們在進行查詢操作的時候,例如

select

*from t where

100< c and c <

100000

;

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

也就是說,我們如果走 c 這個欄位的索引的話,最後會查詢到對應主鍵的值,然後,再根據主鍵的值走主鍵索引,查詢到整行資料返回。

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

為什麼會這樣呢?

是這樣的,系統在執行這條語句的時候,會進行**:究竟是走 c 索引掃瞄的行數少,還是直接掃瞄全表掃瞄的行數少呢?顯然,掃瞄行數越少當然越好了,因為掃瞄行數越少,意味著i/o操作的次數越少。

如果是掃瞄全表的話,那麼掃瞄的次數就是這個表的總行數了,假設為 n;而如果走索引 c 的話,我們通過索引 c 找到主鍵之後,還得再通過主鍵索引來找我們整行的資料,也就是說,需要走兩次索引。而且,我們也不知道符合 100 c < and c < 10000 這個條件的資料有多少行,萬一這個表是全部資料都符合呢?這個時候意味著,走 c 索引不僅掃瞄的行數是 n,同時還得每行資料走兩次索引。

所以呢,系統是有可能走全表掃瞄而不走索引的。那系統是怎麼判斷呢?

判斷**於系統的**,也就是說,如果要走 c 字段索引的話,系統會**走 c 字段索引大概需要掃瞄多少行。如果**到要掃瞄的行數很多,它可能就不走索引而直接掃瞄全表了。

那麼問題來了,系統是怎麼**判斷的呢?

系統是通過索引的區分度來判斷的,乙個索引上不同的值越多,意味著出現相同數值的索引越少,意味著索引的區分度越高。我們也把區分度稱之為基數,即區分度越高,基數越大。所以呢,基數越大,意味著符合100 < c and c < 10000 這個條件的行數越少。

所以呢,乙個索引的基數越大,意味著走索引查詢越有優勢。

那麼問題來了,怎麼知道這個索引的基數呢?

系統當然是不會遍歷全部來獲得乙個索引的基數的,代價太大了,索引系統是通過遍歷部分資料,也就是通過取樣的方式,來**索引的基數的。

既然是取樣,那就有可能出現失誤的情況,也就是說,c 這個索引的基數實際上是很大的,但是取樣的時候,卻很不幸,把這個索引的基數**成很小。例如你取樣的那一部分資料剛好基數很小,然後系統就誤以為索引的基數很小。然後就不走 c 索引,直接走全部掃瞄了。

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

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

不過呢,我們有時候也可以通過強制走索引的方式來查詢,例如

select

*from t force

index

(a)where c <

100and c <

100000

;

我們也可以通過

show

index

from t;

來查詢索引的基數和實際是否符合,如果和實際很不符合的話,我們可以重新來統計索引的基數,可以用這條命令

analyze

table t;

來重新統計分析。

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

乙個 sql 執行的很慢,我們要分兩種情況討論:

1、大多數情況下很正常,偶爾很慢,則有如下原因

(1)、資料庫在重新整理髒頁,例如 redo log 寫滿了需要同步到磁碟。

(2)、執行的時候,遇到鎖,如表鎖、行鎖。

2、這條 sql 語句一直執行的很慢,則有如下原因。

(1)、沒有用上索引:例如該欄位沒有索引;由於對欄位進行運算、函式操作導致無法用索引。

(2)、資料庫選錯了索引。

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

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

一條Sql執行很慢的原因有哪些

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

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

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