1. 程式人生 > >Mysql主鍵選擇之UUID和自增主鍵

Mysql主鍵選擇之UUID和自增主鍵

引言

之前有段時間用postgresql 資料庫,在上雲之後,從自增主鍵變為uuid,感覺uuid全球唯一,很方便。

最近用mysql,發現mysql主鍵都是選擇自增主鍵,仔細比較一下,為什麼mysql選擇自增主鍵,有什麼不同。

在mysql5.0之前,如果是多個master複製的環境,無法用自增主鍵,因為可能重複。在5.0以及之後的版本通過配置自增偏移量解決了整個問題。

什麼情況下我們希望用uuid

1. 避免重複,便於scale,這就是我們做cloud service的時候選擇uuid的主要原因

2. 入庫之前可以知道id

3.相對安全,不能簡單的從uuid獲取資訊,但是如果自增,則容易暴露資訊,如果一個客戶id是123456,很容易猜到有客戶id是123456.

UUID有什麼問題

1.uuid有16個位元組,比int(4 byte)和bigint(8 byte)佔用更多儲存空間

2.由於size和無序性,可能引起效能問題

Mysql的uuid原理

mysql的innodb儲存引擎處理storage的方式是靠聚集索引。

聚集索引是指資料庫錶行中資料的物理順序與鍵值的邏輯(索引)順序相同。一個表只能有一個聚集索引,因為一個表的物理順序只有一種情況

由於 UUID 在 insert 的時候,不一定會比先前的鍵值更大,也因此 InnoDB 必須要計算出適當的位置來安插新的資料。 而目標頁 (Page) 很有可能已經寫入到磁碟中,且從快取中移除,或尚未在快取內,而 InnoDB 必須在每次寫入時去做頁查詢,如此行為會消耗大量的 random I/O.

不連續的插入行為,也會導致 InnoDB 必須不斷的分頁 (split pages) 來創造更多的空間來安插新的資料,也因此創建出來的 pages 會是相當稀疏且有許多碎片。

在 InnoDB 的 index 中,index 的 leaf node 不儲存 row pointer,取而代之,他儲存的是 primary key 的值,找到值之後,再用這個 primary key value 去 clustered index 找出對應的 row data。

也就是說,secondary index 的 left node 中不僅儲存了 index 本身的鍵值,還會把 primary key columns 也儲存進去。

這樣講就很明瞭了,primary key 儲存的值越大,同時會影響到所有 secondary index 的大小。 把 BINARY(16) UUID 設定為 primary key,所有的 secondary index 也都要將 BINARY(16) UUID 儲存進 index 才行。

這樣聽起來好像很無感,如果 primary key 要多存 16Bytes , secondary index 也要多存 16Bytes,這樣隨便一個 10M 個 rows 加起來就多 320MB 了,你如果用大整數 BIGINT 儲存,不僅速度快,還能省下不少空間。

如果你曾搜尋過一些 benchmarking 做 bigint auto_inc 跟 uuid insertion speed 的比較,雖能看出 I/O 效能,卻沒有確切的解釋。

而透過瞭解 clustered index 應該就能明暸為何 bigint auto_inc 做 insertion 速度是最快最穩。 因為,即使是透過時間序列產生的 UUID 在 insertion 的效能表現上也未如 unsigned int + auto increment 佳。

所以,即使在表格內已有 UUID,一般還是會建議在 MySQL 中另外建立一個 column ,使用 unsigned int + auto_increment 做 primary key,一方面可以穩定 insertion speed,另一方面讓索引的查詢更快、分佈平均、也讓索引更小。

原理引用:

https://medium.com/corneltek/innodb-primary-key-with-uuid-and-auto-increment-integer-508d427688dc