引言
之前有段时间用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