前言
本文只但從資料庫本身來看查詢慢的可能因素,至於記憶體不夠、網速較慢不屬於本文討論範疇。
本文內容參考自公眾號文章:
騰訊面試:一條SQL語句執行得很慢的原因有哪些?---不看後悔系列
我對公眾號中的內容作自己的梳理總結
開始
首先要分類討論一下,這條查詢語句是在偶爾的情況下查詢效率慢,還是一直都存在查詢效率慢的問題。
對於第一種情況,可能查詢語句本身沒有問題,是資料庫遇到了其他問題;
對於第二種情況,應該是查詢語句出了問題,需要優化
偶爾效率慢的情況
原因一:重新整理“髒”頁
什麼是“髒”頁
當對資料庫進行插入或者更新操作時,資料庫會立刻將記憶體的資料頁上的資訊更新,但是不會立刻將將更新的資料存到磁碟上,而是先儲存到redo log中,等合適的時機在將redo log的資訊儲存到磁碟上
針對這種記憶體中的資料頁和磁碟上的資料不同的情況我們將記憶體中的資料頁稱為“髒”頁,而記憶體中和磁碟上資料相同的情況則稱為“乾淨”頁。
重新整理“髒”頁時,系統會暫停其他的操作,全身心的將資料存到磁碟中,就會導致平常正常執行的mysql語句變慢
什麼時候會重新整理“髒”頁
case1:redo log裝滿時
case2:記憶體不夠用時
case3:mysql認為系統空閒時
case4:mysql正常關閉時
原因二:資料被鎖住
可以用show processlist命令檢視一下語句執行的狀態,檢視要查詢的資料是否被鎖住
一直都存在效率慢的情況
原因一:查詢的資料量太大
檢視是否查詢了不必要的行與列,避免用select * from table這樣的語句
原因二:沒有用到索引
當資料量很大時,若沒有用索引採用全表索引是很耗費時間的。而這裡沒有用到索引由可以分多鐘情況
case1:沒有建索引
case2:索引失效
引起索引失效的可能原因
1)在索引列上用了內建函式或者其他+-*/運算
2)用萬用字元開頭
3)多列索引違背最佳最匹配原則
4)or操作符容器造成索引失效,除非or的每個操作列都有索引
5)字串不加單引號
case3:系統選錯索引
系統選錯索引其實是索引失效的一種形式,但是由於涉及到的知識點較多,所以單獨拿出來分析。
系統選錯索引導致索引失效時系統將全表掃描與用索引要掃描的行數進行比較,若是覺得運用索引反而要複雜,則系統就會放棄索引採用全表掃描的方式。
那麼什麼時候會出現運用索引反而比全表掃描效率更低的情況呢?
首先我們都知道主鍵索引儲存的是整行資料,而非主鍵索引儲存的是主鍵的值。所以運用非主鍵索引時要先定位到滿足條件的行的主鍵值,在由主鍵值拿到整行資料的資訊,要經過兩次索引的過程。
極端情況下,當索引尋找的資料條件全表都滿足時,則此時索引尋找相比於全表掃描反而多了一系列索引過程。
所以系統在判斷是否需要應用索引時會先判斷如果運用索引大致需要掃描多少行,如果系統預測要掃描的行數很多,則系統會選擇放棄索引採取全表掃描的方式。
系統如何判斷運用索引需要掃描的行數?這就需要用到索引的區分度。索引的區分度又稱為基數,一個索引上不同的值越多,意味著出現相同數值的索引越少,意味著索引的區分度越高。
區分度越高,則滿足索引查詢條件的資料就越少,則系統預測掃描的函式不多。
那麼索引的區分度又是如何得來的呢?
取樣。系統通過取樣的方式來推測索引的區分度。既然是取樣則就會有誤差,如果你想避免這種誤差,不想要系統進行這種它認為的人性化的選擇方式,你可以強制運用索引
select * from t index(a) where c>100 and c<1000;
你也可用下面的第一行命令檢視索引的基數,如果基數和實際不符合的話你也可用第二行命令讓系統重新取樣計算索引基數
1 show index from t; 2 analyze table t;
可以用explain+SQL查詢語句來檢視SQL語句的執行過程,檢視是否用到了預期索引
explain命令可以參考我的另一篇博文mysql優化一之查詢優化