主键如何选择

1、使用业务字段作为主键,比如说对于用户表来说,可以使用手机号,email 或者身份证号作为主键。

2、使用生成的唯一 ID 作为主键。

但是第一种并不是每一张表都可以使用的,一些特殊的字段可以使用,比如说身份证,邮箱,手机号,但是这些字段可能存在变更的情况,就比较麻烦,所以最好采取第二种方案。

第二种方案在单表时,可以采用简单的自增id来实现,但是在分库分表的情况下却并不能这样,因为不同库和不同表的自增id会重复。我们需要采取一些其他的办法来实现。

基于 Snowflake 算法搭建发号器

首先一点是,为什么不采用uuid来当作主键 ?

1、id最好是有序的,某些场景会需要排序,如果按照id则效率高一点,而且id有序分区也会简单。

2、id有序时,将其作为主键,插入数据时效率也会高,如果采用无序的插入,可能会频繁的导致页分裂。

3、uuid作为主键可能会占用大量的空间。

4、uuid并不具备业务含义。

Snowflake 算法

Snowflake 的核心思想是将 64bit 的二进制数字分成若干部分,每一部分都存储有特定含义的数据,比如说时间戳、机器 ID、序列号等等,最终生成全局唯一的有序 ID。

image-20230427220731511

其中机器ID可以用来标识在不同的机房,12位序列号代表着每个节点每毫秒最多可以生成 4096 的 ID。也可以根据不同的业务来规定每一部分的长度。

具体实现方式

1、嵌入到业务代码里,也就是分布在业务服务器中。

这样实现的好处是不需要跨网络调用,性能会好一点。但是业务层的部署可能存在很多分,这就要求我们用更多的机器位,来确保唯一性。

2、作为独立的服务部署,这也就是我们常说的发号器服务。

这种部署方式需要业务层多一次网络请求来获取id,但是可以减少机器id的位数,留更多的位数给自增信息位。

该算法的一个缺点是依赖于子系统的时间戳,一旦系统时间不准,就有可能生成重复的id。所以当我们发现系统时钟不准时,就可以让发号器拒绝发号,一直到时钟准为止。

参考

《高并发系统设计 40 问》