为什么要用雪花id代替数据库自增id?
在数据库设计中,我尝尝在想主键的id应该怎么设置好?为什么这样设置?
-
先讨论一下什么时候使用数据库自增id合适
- 单表设计,数据量不大,不需要考虑分库分表
- 业务对ID连续性有较高要求:某些业务场景下可能需要ID是严格递增的,例如按时间顺序排序的需求,此时自增ID更加直观和方便。
-
再讨论一下什么时候使用雪花id合适 (如果还不清楚什么是雪花id的话可以在下面了解下)
-
全局唯一:雪花ID结合时间戳、工作机器ID和序列号,可以在分布式系统中生成全局唯一的ID。
-
趋势有序:虽然不是严格递增,但雪花ID的时间戳部分使得生成的ID具有大致的时间趋势,有助于提升某些查询性能。
-
可扩展性:由于雪花ID的设计考虑到了分布式环境,所以能够轻松应对分库分表、多数据中心部署等情况下的ID生成需求。
-
无锁生成:雪花ID生成过程快速且无锁(时间戳序列号防重复),适合高并发场景下大量生成ID的需求。
-
1.什么是雪花算法
雪花 ID(Snowflake ID)是用于分布式系统中生成唯一 ID 的算法,在分布式环境下高效地生成全局唯一的 ID
雪花 ID 的结构如下所示:
一共是8个字节,64bit,对应后端的long类型的数据,这四个部分代表:
- 符号位:最高位是符号位,始终为 0,1 表示负数,0 表示正数,ID 都是正整数,所以固定为 0。
- 时间戳部分:由 41 位组成,精确到毫秒级。可以使用该 41 位表示的时间戳来表示的时间可以使用 69 年。
- 节点 ID 部分:由 10 位组成,用于表示机器节点的唯一标识符。在同一毫秒内,不同的节点生成的 ID 会有所不同。
- 序列号部分 :由 12 位组成,用于标识同一毫秒内生成的不同 ID 序列。在同一毫秒内,可以生成 4096 个不同的 ID。(无锁化)
网上有很多雪花算法的代码,这里就不再赘述了
2.雪花算法有什么问题
虽然雪花算法是一种被广泛采用的分布式唯一 ID 生成算法,但它也存在以下几个问题:
- 时间回拨问题 :雪花算法生成的 ID 依赖于系统的时间戳,要求系统的时钟必须是单调递增的。如果系统的时钟发生回拨,可能导致生成的 ID 重复。时间回拨是指系统的时钟在某个时间点之后突然往回走(人为设置),即出现了时间上的逆流情况。
- 时钟回拨带来的可用性和性能问题:由于时间依赖性,当系统时钟发生回拨时,雪花算法需要进行额外的处理,如等待系统时钟追上上一次生成 ID 的时间戳或抛出异常。这种处理会对算法的可用性和性能产生一定影响。
- 节点 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,因为这样做的问题有以下两个:
- 可读性问题:UUID 内容很长,但没有业务含义,就是一堆看不懂的"字母"。
- Mysql底层索引页分裂问题 :UUID是无序的,在生成聚簇索引的时候,会造成索引页分裂,性能低,相比较雪花算法是唯一且增量有序,不会造成索引页分裂的问题
5.总结与思考
我在一些评论区看到一些评论,我也想讲一下我个人的思考
通过组合主键的方式确保全局唯一id,先不说这种生成方式是long类型还是String类型,
如果以后需要进行数据合并或者数据分片,这样子组合主键会麻烦到什么程度呢
雪花算法的id是不连续,但是雪花算法是增量有序,即后一个id一定比前面一个id的值要大
这样不会导致插入类似Mysql这种数据库进行索引页分裂的操作
如果一定是要连续的业务场景,那当然雪花算法并不适合