爲啥你這個sql怎麼慢呢

sql執行慢

​ 有些一句簡單的sql看起來平平無奇,但是卻執行很慢,一般存在這麼幾種可能性:

  • 大多數情況下還可以,只是偶爾出現慢的情況
  • 在數據量不變的情況下,這條sql語句一直執行很慢

偶爾很慢

​ 這種情況下,由於大多數場合下還是比較快的,具有偶發性,一般來說這條sql的書寫本身沒什麼問題,更多的問題在於其他的外部原因導致的

數據庫在刷新髒頁

​ 要知道,我們往數據庫裏插入一條數據,或者更新一條數據的時候,數據庫會在內存中把相應字段的數據給更新了,但是更新以後,數據不會立馬同步持久化到磁盤中去,而是把這些記錄寫到redo log日誌中去。等到空閒的時候,再通過redo log裏的日誌把最新的數據同步到磁盤中去。

​ 不過容易造成一個問題,就是redo log裏面的容量是有限的,如果數據庫一直很忙,更新又很頻繁,這個時候redo log被塞滿了,這個時候就只能暫停其他的操作,先把數據同步到磁盤中去,這個時候就導致我們平時執行很快的sql變得很慢。

拿不到鎖

​ 我們執行的這個SQL語句的時候,正好這條sql語句涉及到的表,被人加鎖了,我們暫時拿不到鎖,就只好等待。或者是表沒有加鎖,而是要使用到的某一行被加鎖了。

​ 所以我們要判斷一下,是否是因爲鎖問題導致的,可以使用show processlist這個來查看當前的狀態(mysql)。

一直很慢

​ 先假設一個表,其中只有主鍵id和兩個普通字段age和salary

CREATE TABLE `t` (
`id` INT ( 11 ) NOT NULL,
`age` INT ( 11 ) DEFAULT NULL,
`salary` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB;

沒用上索引

  • 字段沒有索引

  • 字段有索引卻沒有用上索引

    • 例如:
    select * from t where age-1=19;
    

    因爲我們在字段的左邊做了運算,導致在查詢的時候就用不上。

  • 函數操作導致沒有用上索引

    我們在查詢的時候對字段進行了函數操作,這也會導致沒有用上索引。

數據庫選錯索引

例子:

select * from t where 1<age and age<100;

​ 我們知道主鍵索引和給主鍵索引時有區別的,也就是說如果走age這個字段的索引的話最後會查詢到對應主鍵的值,然後再根據主鍵的值走主鍵索引,查詢到整行數據返回。

​ 即就算age有索引,系統也不一定會走age這個字段的索引,而是可以會直接掃描,掃描全表。

  • 主鍵索引(聚簇索引):葉子節點存放的值是整行字段的數據。
  • 非主鍵索引(二級索引):葉子節點存放的是主鍵的值

​ 在執行這條語句的時候系統會進行預測到底是走age的索引行數少,還是直接全表掃描的行數少,畢竟age索引找到主鍵後還需要通過主鍵索引來找整行數據。

​ 所以系統就通過索引的區分度來判斷:一個索引上不同的值越多,意味着出現相同數值的索引越少,意味着索引的區分度越高。我們也把區分度稱之爲基數,即區分度越高,基數越大。

​ 即索引的基數越大就走索引查詢。

​ 但系統通過採樣來判斷索引基數的大小,所以存在誤差判斷失誤。

即由於統計失誤,導致系統沒有走索引而是走了全表掃描,導致我們sql執行緩慢

可以通過SQL語句強制走索引:

select * from t force index(a) where 1<age and age<100;

也可以通過 show index from t ;來查詢索引的基數和實際是否符合,如果不符合可以重新統計:analyze table t

總結

一條sql 執行很慢的原因:

  • 偶爾很慢:
    • 數據庫在刷新髒頁,例如redo log滿了需要同步到磁盤
    • 執行的時候遇到鎖:表鎖、行鎖
  • 一直很慢:
    • 沒有用上索引:
      • 該字段沒有索引
      • 由於對字段進行運算或者函數操作導致無法使用索引
    • 數據庫選錯了索引
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章