背景
设计 MySQL 主键 的背景在于 问题定位:业务使用自增ID作主键存在什么问题?。
淘宝主键设计推测

从上图可以发现,订单号不是自增 ID!
我们详细看下上述 4 个订单号:
订单号是 19 位的长度,且订单的最后 5 位都是一样的,都是 08113。
且订单号的前面 14 位部分是单调递增的。
大胆猜测,淘宝的订单 ID 设计应该是:
订单 ID = 时间 + 去重字段 + 用户 ID 后 6 位尾号
这样的设计能做到全局唯一,且对分布式系统查询及其友好。
主键设计补充
非核心业务 :对应表的主键自增 ID,如告警、日志、监控等信息。
核心业务 :主键设计至少应该是全局唯一且是单调递增。全局唯一保证在各系统之间都是唯一的,单调递增是希望插入时不影响数据库性能。
这里推荐最简单的一种主键设计:UUID。
MySQL 数据库的 UUID 组成如下所示:
UUID = 时间 + UUID 版本(16 字节) - 时钟序列(4 字节) - MAC 地址(12 字节)

为什么 UUID 是全局唯一的?
在 UUID 中时间部分占用 60 位,存储的类似 TIMESTAMP 的时间戳,但表示的是从 1582-10-15 00:00:00.00 到现在的 100ns 的计数。可以看到 UUID 存储的时间精度比 TIMESTAMPE 更高,时间维度发生重复的概率降低到 1/100ns。
时钟序列是为了避免时钟被回拨导致产生时间重复的可能性。MAC 地址用于全局唯一。
为什么 UUID 占用 36 个字节?
UUID 根据字符串进行存储,设计时还带有无用”-“字符串,因此总共需要 36 个字节。
为什么 UUID 是随机无序的呢?
因为 UUID 的设计中,将时间低位放在最前面,而这部分的数据是一直在变化的,并且是无序。
改造 UUID
若将时间高低位互换,则时间就是单调递增的了,也就变得单调递增了。MySQL 8.0 可以更换时间低位和时间高位的存储方式,这样 UUID 就是有序的 UUID 了。
MySQL 8.0 还解决了 UUID 存在的空间占用的问题,除去了 UUID 字符串中无意义的”-“字符串,并且将字符串用二进制类型保存,这样存储空间降低为了 16 字节。
可以通过 MySQL8.0 提供的 uuid_to_bin 函数实现上述功能,同样的,MySQL 也提供了 bin_to_uuid 函数进行转化:
SET @uuid = UUID();
SELECT @uuid,uuid_to_bin(@uuid),uuid_to_bin(@uuid, TRUE);通过函数 uuid_to_bin(@uuid,true) 将 UUID 转化为有序 UUID 了。全局唯一+单调递增,这不就是我们想要的主键!