前段時間還在忙,終於又出點空更新了,雖然不是所有的付出都有收獲,很多時候需要我們先付出才可以看到希望。
一 前言
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)
說明下:
- innodb_change_buffer 為 change_buffer 占 innodb_buffer_pool_size 中的百分比,這里面 25%最大為 50%,我這個本機配置實在是太低了。
- 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 適用場景
- 如果我們的數據是讀少,寫多的,比如日志數據。
- 還有就是我們系統中的歷史庫,幾乎不會再讀取數據了,卻需要隨時搬遷歷史數據到此庫中,肯定沒有唯一沖突了,可以考慮把唯一索引改成普通索引,以提升搬遷性能。
江城子·墨雲拖雨過西樓
[宋] 蘇軾
墨雲拖雨過西樓。水東流。晚煙收。
柳外殘陽,回照動簾鈎。
今夜巫山真個好,花未落,酒新篘。
美人微笑轉星眸。月花羞。捧金甌。
歌扇縈風,吹散一春愁。
試問江南諸伴侶,誰似我,醉揚州。