什么時候使用唯一索引和普通索引


  • 唯一或普通索引的選擇

  • 業務需求

    • 假設你在維護一個市民系統,每個人都有一個唯一的身份證號,而且業務代碼已經保證了不會寫入兩個重復的身份證號。如果市民系統需要按照身份證號查姓名,就會執行類似這樣的SQL語句:

      select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';
      
    • 在不考慮身份證好字段大小的情況下,需要給id_card建立索引,是選擇普通索引還是唯一索引呢?

  • 查詢過程

    • 比如 select id from T where k=5
    • 在B+樹的樹根開始,按層搜索找到葉子節點,先找到數據頁,在根據二分法,在數據頁中定位記錄。
    • 對於普通索引,查找到滿足條件的第一個記錄(5,500)后,需要查找下一個記錄,直到碰到第一個不滿足k=5條件的記錄。
    • 對於唯一索引來說,由於索引定義了唯一性,查找到第一個滿足條件的記錄后,就會停止繼續檢索。
    • 引擎是按頁讀取的,對於普通索引來說,只是多做了一次查找和判斷下一條記錄的操作,如果該記錄剛好是最后一條就會復雜一些,但是一個數據頁可以存放近千個key,這種概率也很低,所以我們可以認為差距很小。
  • 更新過程

    • change buffer概念
      • 當需要更新一個數據頁時,如果數據頁在內存中就直接更新,而如果這個數據頁還沒有在內存中的話,在不影響數據一致性的前提下,InooDB會將這些更新操作緩存在change buffer中,這樣就不需要從磁盤中讀入這個數據頁了。。在下次查詢需要訪問這個數據頁的時候,將數據頁讀入內存,然后執行change buffer中與這個頁有關的操作。通過這種方式就能保證這個數據邏輯的正確性。
      • 需要說明的是,雖然名字叫作change buffer,實際上它是可以持久化的數據。也就是說,change buffer在內存中有拷貝,也會被寫入到磁盤上。
      • 將change buffer中的操作應用到原數據頁,得到最新結果的過程稱為merge。除了訪問這個數據頁會觸發merge外,系統有后台線程會定期merge。在數據庫正常關閉(shutdown)的過程中,也會執行merge操作。
      • 顯然,如果能夠將更新操作先記錄在change buffer,減少讀磁盤,語句的執行速度會得到明顯的提升。而且,數據讀入內存是需要占用buffer pool的,所以這種方式還能夠避免占用內存,提高內存利用率。
    • 使用change buffer的條件
      • 對於唯一索引來說,所有的更新操作都要先判斷這個操作是否違反唯一性約束。比如,要插入(4,400)這個記錄,就要先判斷現在表中是否已經存在k=4的記錄,而這必須要將數據頁讀入內存才能判斷。如果都已經讀入到內存了,那直接更新內存會更快,就沒必要使用change buffer了。
      • 因此,唯一索引的更新就不能使用change buffer,實際上也只有普通索引可以使用。
      • 當要更新的記錄目標頁不再內存中,如果是普通索引就會將更新記錄在buffer上,語句執行結束,減少了磁盤讀入內存的io訪問,性能提升。
    • change buffer的使用場景
      • 對於寫多讀少的業務,使用change buffer的效果比較好,比如賬單,日志類的系統。
      • 對於讀多寫少的話,由於馬上訪問數據頁,需要將buffer中的數據更新到數據頁,會觸發merge過程,使得io次數不會減少,增加了change buffer的維護代價,反而起了副作用。
  • change buffer 和 redo log的區別

    • 例子:mysql> insert into t(id,k) values(id1,k1),(id2,k2);
    • 這里,我們假設 當前k索引樹的狀態,查找到位置后,k1所在的數據頁在內存(InnoDB bufferpool)中,k2所在的數據頁不在內存中
  • 這條語句操作如下

    1. Page 1在內存中,直接更新內存;

    2. Page 2沒有在內存中,就在內存的change buffer區域,記錄下“我要往Page 2插入一行”這個信息

      1. 將上述兩個動作記入redo log中(圖中3和4)。

      執行時寫了兩處內存,一次磁盤。

  • 接下來的讀請求:

    • 讀Page 1的時候,直接從內存返回。WAL之后如果讀數據,不要讀盤,不用從redo log里面把數據更新以后才可以返回。雖然磁盤上還是之前的數據,但是這里直接從內存返回結果,結果是正確的。
    • 要讀Page 2的時候,需要把Page 2從磁盤讀入內存中,然后應用change buffer里面的操作日志,生成一個正確的版本並返回結果。
    • 可以看到,直到需要讀Page 2的時候,這個數據頁才會被讀入內存。
  • 所以,如果要簡單地對比這兩個機制在提升更新性能上的收益的話,redo log 主要節省的是隨機寫磁盤的IO消耗(轉成順序寫),而change buffer主要節省的則是隨機讀磁盤的IO消耗。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM