唯一索引和普通索引怎么選擇


前段時間還在忙,終於又出點空更新了,雖然不是所有的付出都有收獲,很多時候需要我們先付出才可以看到希望。

一 前言

Mysql 類的文章看的人比較少,我想一方面可能是大家更熱衷於比較前言的技術,像 Mysql 類的已經發展很久的基礎數據庫,關注的人反而少,當時我在學習 Mysql 的過程中發現,Mysql 的很多優秀的技術在大數據很多開源框架中都能看到影子,所以還是按照筆記的方式把這個系列寫完吧,當然也會穿插些其他內容。

二 選擇標准

其實,如果業務上就要求我們數據庫的值必須是唯一的,那沒什么好討論的,就選擇唯一索引;那么如果業務上要求不嚴格,或者說不需要我們數據庫后台來保障唯一性要求,這時候我們選擇唯一索引還是普通索引就看誰在性能上更好,誰好選誰。

三 性能比較

3.1 查詢性能比較

對於數據庫查詢來說,以前介紹過,InnoDB 引擎的索引以 B+樹這種數據結構保存的,我們在利用索引查詢的時候,先從 B+樹的葉子節點進行按層搜索,定位到我們數據在的數據頁,數據頁內基本按照二分法查找我們具體要查找的數據。

  • 普通索引: 這時候,我們查找到滿足條件的值后,需要進一步查找,直到不滿足條件為止。
  • 唯一索引: 我們查找到需要查找的值后,由於唯一索引,所以只有一個值,所以可以直接返回。 從這個角度來看,唯一索引更快。但是影響並不大,是因為我們讀取數據的時候都是按照數據頁去讀取的, 一個數據頁默認大小為 64K,可以存上千個索引值。那普通索引的移動指針到下一個元素和比較的數據都是在內存中的,所以影響比較小。

3.2 更新性能比較

我們想一下,Mysql 的數據包含兩個部分,一部分在內存中,一部分在磁盤上,在內存中的不光是 Mysql 的數據還有索引。 那么我們在更新的時候,如果數據在內存里面好說,直接更新,定期刷新到磁盤,但是更多的時候可能是數據不在內存中,如果每次都從磁盤讀取數據所在的數據頁,然后去操作,就需要至少涉及到一次磁盤的隨機讀操作,比較昂貴的操作。

那么我們是否可以在內存里面中把這個更新操作紀錄下來,在合適的時候再將數據合並到磁盤上。這塊內存在 Mysql 中就叫 change buffer,(看到 buffer,可能就會認為只存在內存中,實際上 change buffer 不光在內存中也存在磁盤上,同步到數據庫的系統表空間 ibdata1),更新操作的時候,數據如果不在內存中,且不影響數據(比如不影響數據的唯一性的情況下),我們把更新操作紀錄到 change buffer 上,並不需要從磁盤上讀取數據頁。這時候,如果來了一個查詢動作,剛好要查這條數據的情況下,Mysql 會從磁盤上讀取這個數據頁,然后發現 change buffer 有修改了這個數據頁,會將這個數據頁修改的內容 merge 到這個數據頁上。 查詢配置如下:

mysql> show variables like '%innodb_change_buffer%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
| innodb_change_buffering       | all   |
+-------------------------------+-------+
2 rows in set, 1 warning (0.03 sec)
mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| innodb_buffer_pool_size | 8388608 |
+-------------------------+---------+
1 row in set, 1 warning (0.01 sec)

說明下:

  1. innodb_change_buffer 為 change_buffer 占 innodb_buffer_pool_size 中的百分比,這里面 25%最大為 50%,我這個本機配置實在是太低了。
  2. innodb_change_buffering 表示哪些場景用 change_buffer 取值:all/none/inserts/deletes

那在更新數據的時候,數據庫是如何操作的,總體來說分兩種清空,要操作的數據所在的數據頁在內存中和不在內存中。 數據頁在內存中:

  • 如果是普通索引,則直接更新內存中的數據。(這里簡化了很多還涉及到 redolog 和 binlog 等)
  • 如果是唯一索引,碰巧要做的是插入操作,則需要將需要插入的字段值和數據頁中的比較看是否存在,決定是否可以插入。 兩種操作的耗時差別很小。

數據頁不在內存中:

  • 如果是普通索引,則在 change buffer 中記錄對那個數據頁做了什么樣的修改。
  • 如果是唯一索引,則需要將數據頁讀取到內存中,判斷是否滿足唯一性約束,數據已經讀入內存了,這時候肯定不會再適用 change buffer 了,因為已經多了一次 IO 的隨機讀了。

所以從更新角度看,普通索引可以利用 change buffer 更新操作的性能比唯一索引要更好。 這里面要說明的是有些人可能會認為像插入操作,我們需要一個主鍵,主鍵是唯一索引,所以插入操作是用不到 change buffer,這樣是不對的,因為一個表一般除了主鍵還有二級索引,主鍵用不到,二級索引可以用到 change buffer。

四 Change buffer 適用場景

4.1 Change buffer 不適用場景

不是所有的場合都適合使用 change buffer 的,change buffer 的本質是通過減少磁盤的隨機 IO 讀的訪問來提升系統的性能。

  • 如果一個數據在寫入后,經常需要立刻讀出來,那么我們並不能降低隨機讀,而且還會增加 change buffer 的操作負擔,所以並不適合。
  • 如果數據庫的數據都只有主鍵,或只有唯一索引,也不合適。

4.2 Change buffer 適用場景

  • 如果我們的數據是讀少,寫多的,比如日志數據。
  • 還有就是我們系統中的歷史庫,幾乎不會再讀取數據了,卻需要隨時搬遷歷史數據到此庫中,肯定沒有唯一沖突了,可以考慮把唯一索引改成普通索引,以提升搬遷性能。
江城子·墨雲拖雨過西樓
[宋] 蘇軾
墨雲拖雨過西樓。水東流。晚煙收。
柳外殘陽,回照動簾鈎。
今夜巫山真個好,花未落,酒新篘。
美人微笑轉星眸。月花羞。捧金甌。
歌扇縈風,吹散一春愁。
試問江南諸伴侶,誰似我,醉揚州。


免責聲明!

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



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