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滿了需要同步到磁盤
- 執行的時候遇到鎖:表鎖、行鎖
- 一直很慢:
- 沒有用上索引:
- 該字段沒有索引
- 由於對字段進行運算或者函數操作導致無法使用索引
- 數據庫選錯了索引
- 沒有用上索引: