你真的懂數據庫的索引嗎(下篇)

===============================================

普通索引和唯一索引的選擇

查詢過程

SELECT id FROM T WHERE A=1;對於上述這條select語句,首先從B+樹的樹根開始,按層搜索到葉子節點,然後再根據二分法來定位記錄。

  • 對於普通索引來說,查到滿足條件的記錄後,還會繼續查找下一個記錄,直到碰到第一個不滿足條件的記錄。
  • 對於唯一索引來說,由於索引具有唯一性,查找到第一個滿足條件的記錄後,就會停止繼續檢索。

只能說兩者的查詢效率差別不大,InnoDB的數據是按照頁爲單位來讀寫的,當讀到一條記錄的時候,並不是把這個數據從磁盤讀出來,而是以頁爲單位,讀到內存中,對於在內存中多檢索幾條記錄,消耗很低。但是有一種特殊情況,就是這個檢索的key剛好位於數據頁的最後一個數,所以讀取下一個記錄的時候,需要把另一個數據頁加載進內存。

更新過程

這裏需要先引入一個概念:change buffer。當更新一個數據頁的時候,如果數據在內存中就會直接更新,如果數據不在內存中的話,在不影響一致性的前提下,InnoDB會把這些更新操作緩存在change buffer中,這樣就不需要從磁盤中讀入數據頁。當這個數據頁被讀進內存的時候,這個時候就會執行change buffer中與這個頁有關的操作,通過這種方式來保證這個數據邏輯的準確性。
change buffer的數據和數據頁數據合併的過程叫做merge,系統的後臺線程會定期把數據merge到磁盤中的數據頁,在數據庫關閉的時候,也會進行merge操作。

  • 對於唯一索引來說,更新操作需要判斷這個操作是否滿足唯一性約束,所以需要把整個數據頁讀入到內存中才能判斷,而數據頁加載到內存中,就沒必要使用change buffer,直接更新還來得快一些。
    實際上change buffer也只有普通索引能用。

瞭解完這個change buffer的概念,接下來看看兩種索引更新的過程
數據頁在內存中的情況

  • 唯一索引,直接找到對應位置,判斷有無衝突,並插入值,語句執行結束
  • 普通索引,找到對應位置,插入值,語句結束

數據頁不在內存中的情況

  • 唯一索引,需要把數據頁加載進內存中,判斷有無衝突,插入值,語句結束
  • 普通索引,把數據記錄到change buffer,語句結束

如何選擇這兩種索引

普通索引和唯一索引在查詢上區別不大,主要區別在於更新操作。普通索引具有chang buffer,而唯一索引沒有。對於寫多讀少的場景,change buffer能夠極大的提高效率。所以考慮業務場景,一般來說選擇普通索引的可能性比較大

關於change buffer 一開始是寫內存但是機器突然故障重啓,這時候的情況分析:
change buffer有一部分數據在內存中,有一部分數據在ibdata中,做清除的時候,會把change buffer裏面的數據持久化到ibdata中。
redo log日誌記錄了數據頁的修改和change buffer中寫入的消息。
如果這個時候故障,分情況來分析:
1、change buffer裏面的數據已經持久化到磁盤中,這個時候就不用管了
2、沒持久化到磁盤中,這個時候就分爲以下幾種情況

  • change buffer寫入了,redo log已經fsync但是沒有commit,binlog沒有fsync到磁盤,這部分數據就會出現丟失
  • change buffer寫入,redo log寫入但是沒有commit,binlog已經fsync到磁盤,這個時候從binlog恢復redo log,再從redo log回覆change buffer
  • change buffer寫入,redo log和binlog都已經fsync,那麼直接從redo log恢復

MySQL對索引的選擇

一張數據庫表可能有多個索引,但是你寫SQL語句的時候,並沒有主動指定使用哪個索引,說明這個索引是MySQL來確定的。
選擇索引是優化器的工作,找到最優執行方案。並用最小代價去執行語句。優化器的判斷標準由臨時表、是否排序,掃描行數等因素決定的。
這裏講講MySQL是怎麼判斷掃描行數的
在還沒執行語句之前,MySQL會根據統計信息來估計記錄數,這個統計信息其實就是索引的區分度
那何爲索引的區分度呢?
一個索引上不同值個數被稱之爲基數,這個基數越大,索引的區分度就越好。
那這裏又有一個問題了,就是MySQL怎麼得到索引的基數呢?
這裏採用的是採樣統計的方式,InnoDB默認選擇N個數據頁,統計頁面上的不同值,得到一個平均值,將這個平均值乘索引的頁面數,就可以得到其基數。數據表會不斷更新,索引統計信息也不是一層不變,當變更的數據行達到一定量的時候,就會重新觸發一次索引統計。因爲採用的是採樣統計,所以這個數據是不準確的,只是一個大概的值。
當然這個基數只是優化器的一個考慮因素,普通索引的話會有一個回表操作,這個性能消耗優化器也是會考慮在內的。當然優化器的選擇不一定是正確的,但是大部分情況還是能夠做出正確選擇的

索引選擇異常和處理

  • 方法一:採用force index強行選擇一個索引
  • 方法二:修改SQL語句,引導MySQL使用我們期待的索引
  • 方法三:新建更爲合適的索引,來給優化器做選擇,或者刪除誤用的索引

給字符串加索引

  1. 直接創建完整索引,這樣比較佔用空間
  2. 創建前綴索引,節省空間,但是覆蓋索引將失效
  3. 倒序存儲,再創建索引,可以繞開正序字符串區分度不夠的情況
  4. 創建hash字段索引。

注:3、4點都不支持範圍掃描

你真的懂數據庫的索引嗎(上篇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章