MySQL查詢數據慢緣由

針對一直都這麼慢的狀況mysql


 

若是在數據量同樣大的狀況下,這條 SQL 語句每次都執行的這麼慢,那就就要好好考慮下你的 SQL 書寫了,下面咱們來分析下哪些緣由會致使咱們的 SQL 語句執行的很不理想。sql

 

咱們先來假設咱們有一個表,表裏有下面兩個字段,分別是主鍵 id,和兩個普通字段 c 和 d。數據庫

mysql> CREATE TABLE `t` (  `id` int(11) NOT NULL,  `c` int(11) DEFAULT NULL,  `d` int(11) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB;

 

1. 沒用到索引函數

 

沒有用上索引,我以爲這個緣由是不少人都能想到的,例如你要查詢這條語句post

select * from t where 100 <c and c < 100000;

 

a. 字段沒有索引性能

 

恰好你的 c 字段上沒有索引,那麼抱歉,只能走全表掃描了,你就體驗不會索引帶來的樂趣了,因此,這回致使這條查詢語句很慢。優化

 

b. 字段有索引,但卻沒有用索引code

 

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

select * from t where c - 1 = 1000;

 

我想問你們一個問題,這樣子在查詢的時候會用索引查詢嗎?排序

 

不會,若是咱們在字段的左邊作了運算,那麼很抱歉,在查詢的時候,就不會用上索引了,因此呢,你們要注意這種字段上有索引,但因爲本身的疏忽,致使系統沒有使用索引的狀況了。

 

正確的查詢應該以下

select * from t where c = 1000 + 1;

 

有人可能會說,右邊有運算就能用上索引?難道數據庫就不會自動幫咱們優化一下,自動把 c - 1=1000 自動轉換爲 c = 1000+1。

 

c. 函數操做致使沒有用上索引

 

若是咱們在查詢的時候,對字段進行了函數操做,也是會致使沒有用上索引的,例如

select * from t where pow(c,2) = 1000;

 

這裏我只是作一個例子,假設函數 pow 是求 c 的 n 次方,實際上可能並無 pow(c,2)這個函數。其實這個和上面在左邊作運算也是很相似的。

 

因此呢,一條語句執行都很慢的時候,多是該語句沒有用上索引了,不過具體是啥緣由致使沒有用上索引的呢,你就要會分析了,我上面列舉的三個緣由,應該是出現的比較多的吧。

 

2. 數據庫本身選錯索引了

 

咱們在進行查詢操做的時候,例如

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,同時還得每行數據走兩次索引。

 

       系統是經過索引的區分度來判斷的,一個索引上不一樣的值越多,意味着出現相同數值的索引越少,意味着索引的區分度越高。咱們也把區分度稱之爲基數,即區分度越高,基數越大。因此呢,基數越大,意味着符合 100 < c and c < 10000 這個條件的行數越少。

 

因此呢,一個索引的基數越大,意味着走索引查詢越有優點。

索引的基數,索引系統是經過遍歷部分數據,也就是經過採樣的方式,來預測索引的基數的。

 

重點來了,竟然是採樣,那就有可能出現失誤的狀況,也就是說,c 這個索引的基數其實是很大的,可是採樣的時候,卻很不幸,把這個索引的基數預測成很小。例如你採樣的那一部分數據恰好基數很小,而後就誤覺得索引的基數很小。而後系統就不走 c 索引了,直接走所有掃描了。

 

因此呢,說了這麼多,得出結論:因爲統計的失誤,致使系統沒有走索引,而是走了全表掃描,而這,也是致使咱們 SQL 語句執行的很慢的緣由。

這裏聲明一下,系統判斷是否走索引,掃描行數的預測其實只是緣由之一,這條查詢語句是否須要使用使用臨時表、是否須要排序等也是會影響系統的選擇的。

 

不過呢,咱們有時候也能夠經過強制走索引的方式來查詢,例如

select * from t force index(a) where c < 100 and c < 100000;

 

咱們也能夠經過

show index from t;

 

來查詢索引的基數和實際是否符合,若是和實際很不符合的話,咱們能夠從新來統計索引的基數,能夠用這條命令

analyze table t;

 

來從新統計分析。

 

既然會預測錯索引的基數,這也意味着,當咱們的查詢語句有多個索引的時候,系統有可能也會選錯索引,這也多是 SQL 執行的很慢的一個緣由。

 

下面作一個總結。

總結


 

以上是個人總結與理解,最後一個部分,我怕不少人不大懂數據庫竟然會選錯索引,因此我詳細解釋了一下,下面我對以上作一個總結。

 

一個 SQL 執行的很慢,咱們要分兩種狀況討論:

 

1. 大多數狀況下很正常,偶爾很慢,則有以下緣由

 

a. 數據庫在刷新髒頁,例如 redo log 寫滿了須要同步到磁盤。

 

b. 執行的時候,遇到鎖,如表鎖、行鎖。

 

2. 這條 SQL 語句一直執行的很慢,則有以下緣由。

 

a. 沒有用上索引:例如該字段沒有索引;因爲對字段進行運算、函數操做致使沒法用索引。

 

b. 數據庫選錯了索引。

 

按鍵查找仍是比較常見的性能提高手段。 我的總結的優化原則是: 作事快(索引、並行、快盤、高效錶鏈接等) 作事少(數據拆分減小訪問數據量、過濾性強的條件、去除或下降沒必要要執行的語句量等) 無等待(熱塊打散、鎖爭用避免等)

 

出處:https://juejin.im/post/5cc84cb6518825250e146c61

相關文章
相關標籤/搜索