主键的生成到底用什么生成策略合适?有什么优劣吗?

为什么要用雪花id代替数据库自增id?


在数据库设计中,我尝尝在想主键的id应该怎么设置好?为什么这样设置?

  • 先讨论一下什么时候使用数据库自增id合适

    1. 单表设计,数据量不大,不需要考虑分库分表
    2. 业务对ID连续性有较高要求:某些业务场景下可能需要ID是严格递增的,例如按时间顺序排序的需求,此时自增ID更加直观和方便。
  • 再讨论一下什么时候使用雪花id合适 (如果还不清楚什么是雪花id的话可以在下面了解下)

    1. 全局唯一:雪花ID结合时间戳、工作机器ID和序列号,可以在分布式系统中生成全局唯一的ID。

    2. 趋势有序:虽然不是严格递增,但雪花ID的时间戳部分使得生成的ID具有大致的时间趋势,有助于提升某些查询性能。

    3. 可扩展性:由于雪花ID的设计考虑到了分布式环境,所以能够轻松应对分库分表、多数据中心部署等情况下的ID生成需求。

    4. 无锁生成:雪花ID生成过程快速且无锁(时间戳序列号防重复),适合高并发场景下大量生成ID的需求。

1.什么是雪花算法


雪花 ID(Snowflake ID)是用于分布式系统中生成唯一 ID 的算法,在分布式环境下高效地生成全局唯一的 ID

雪花 ID 的结构如下所示:

一共是8个字节,64bit,对应后端的long类型的数据,这四个部分代表:

  1. 符号位:最高位是符号位,始终为 0,1 表示负数,0 表示正数,ID 都是正整数,所以固定为 0。
  2. 时间戳部分:由 41 位组成,精确到毫秒级。可以使用该 41 位表示的时间戳来表示的时间可以使用 69 年。
  3. 节点 ID 部分:由 10 位组成,用于表示机器节点的唯一标识符。在同一毫秒内,不同的节点生成的 ID 会有所不同。
  4. 序列号部分 :由 12 位组成,用于标识同一毫秒内生成的不同 ID 序列。在同一毫秒内,可以生成 4096 个不同的 ID。(无锁化)

网上有很多雪花算法的代码,这里就不再赘述了

2.雪花算法有什么问题


虽然雪花算法是一种被广泛采用的分布式唯一 ID 生成算法,但它也存在以下几个问题:

  1. 时间回拨问题 :雪花算法生成的 ID 依赖于系统的时间戳,要求系统的时钟必须是单调递增的。如果系统的时钟发生回拨,可能导致生成的 ID 重复。时间回拨是指系统的时钟在某个时间点之后突然往回走(人为设置),即出现了时间上的逆流情况。
  2. 时钟回拨带来的可用性和性能问题:由于时间依赖性,当系统时钟发生回拨时,雪花算法需要进行额外的处理,如等待系统时钟追上上一次生成 ID 的时间戳或抛出异常。这种处理会对算法的可用性和性能产生一定影响。
  3. 节点 ID 依赖问题:雪花算法需要为每个节点分配唯一的节点 ID 来保证生成的 ID 的全局唯一性。节点 ID 的分配需要有一定的管理和调度,特别是在动态扩容或缩容时,节点 ID 的管理可能较为复杂。

3.如何解决时钟回拨问题

百度 UidGenerator 框架中解决了时间回拨的问题,并且解决方案比较经典

UidGenerator 介绍 :UidGenerator 是百度开源的一个分布式唯一 ID 生成器,它是基于 Snowflake 算法的改进版本。与传统的 Snowflake 算法相比,UidGenerator 在高并发场景下具有更好的性能和可用性。它的实现源码在:github.com/baidu/uid-g...

**UidGenerator 是这样解决时间回拨问题的:UidGenerator 的每个实例中,都维护一个本地时钟缓存,用于记录当前时间戳。**这个本地时钟会定期与系统时钟进行同步,如果检测到系统时钟往前走了(出现了时钟回拨),则将本地时钟调整为系统时钟。

4.众所周知的UUID代替雪花算法可以吗?

如果单从唯一性来考虑的话,那么 UUID 和雪花 ID 的效果是一致的,二者都能保证分布式系统下的数据唯一性,但是即使这样,也不建议使用 UUID 替代雪花 ID,因为这样做的问题有以下两个:

  1. 可读性问题:UUID 内容很长,但没有业务含义,就是一堆看不懂的"字母"。
  2. Mysql底层索引页分裂问题 :UUID是无序的,在生成聚簇索引的时候,会造成索引页分裂,性能低,相比较雪花算法是唯一且增量有序,不会造成索引页分裂的问题

5.总结与思考

我在一些评论区看到一些评论,我也想讲一下我个人的思考

通过组合主键的方式确保全局唯一id,先不说这种生成方式是long类型还是String类型,

如果以后需要进行数据合并或者数据分片,这样子组合主键会麻烦到什么程度呢


雪花算法的id是不连续,但是雪花算法是增量有序,即后一个id一定比前面一个id的值要大

这样不会导致插入类似Mysql这种数据库进行索引页分裂的操作

如果一定是要连续的业务场景,那当然雪花算法并不适合

6.分享一些网友的热评

相关推荐
NiNg_1_23436 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
Ai 编码助手3 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
陈燚_重生之又为程序员3 小时前
基于梧桐数据库的实时数据分析解决方案
数据库·数据挖掘·数据分析
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
白云如幻3 小时前
MySQL排序查询
数据库·mysql
萧鼎3 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步