如何生成分布式雪花算法ID

目录

如何生成分布式雪花算法ID

        如何生成分布式雪花算法ID

        什么是雪花算法

        Snowflake算法的优势

        Snowflake算法的劣势

        雪花算法的结构

2. 雪花算法适用场景

雪花算法生成ID重复问题

1. 标识位如何定义

2. 分配标识位

预分配

动态分配

动态分配实现方案

         开源分布式ID框架

回顾总结

如何生成分布式雪花算法ID

在分布式系统中,常常需要生成全局唯一的ID以防止冲突。尽管36位的UUID能够解决这一问题,但其缺点显而易见:UUID不仅相对较长,而且通常是无序的。

有时我们希望使用一种更为简洁的ID,并且希望这些ID能够按时间顺序生成。

什么是雪花算法

Snowflake,中文称为雪花算法,是Twitter开源的一种分布式ID生成算法。通过该算法生成的ID为64位的长整型数值,其结构中引入了时间戳,从而基本实现了自增。

Snowflake算法的优势

        1.高性能高可用:ID生成不依赖数据库,完全在内存中进行。

        2.高吞吐量:每秒可以生成数百万个自增ID。

        3.ID自增:在数据库中存储时,具有较高的索引效率。

Snowflake算法的劣势

        1.依赖系统时间的一致性:如果系统时间被回调或修改,可能会导致ID冲突或重复。

雪花算法的结构

Snowflake的结构如下图所示:

如何生成分布式雪花算法ID_第1张图片

Snowflake算法的结构由四个部分组成:

        1.不使用位:1bit,最高位是符号位,0表示正,1表示负。在Snowflake算法中,固定为0。
        2.时间戳:41bit,表示毫秒级的时间戳(41位的长度可以使用69年)。
        3.标识位:5bit的数据中心ID和5bit的工作机器ID,两个标识位组合起来最多可以支持部署 1024个节点。
        4.序列号:12bit,用于记录同一毫秒内生成的不同ID,支持每毫秒生成4096个不同的ID。

如何生成分布式雪花算法ID_第2张图片

序列号:12bit递增序列号,用于在同一毫秒内生成唯一ID。每个序列号可以在1毫秒内产生4096个ID,因此在1秒钟内可以生成4096 * 1000 = 409万(4.09M)个唯一ID。

默认情况下,雪花算法使用64bit长度,但具体长度可以根据需要进行配置。如果希望系统运行更长时间,可以增加时间戳的位数;如果需要支持更多节点部署,可以增加标识位的长度;如果并发量很高,可以增加序列号的位数

总结:雪花算法是灵活可定制的,可以根据系统的具体需求进行调整。

2. 雪花算法适用场景

由于雪花算法生成的ID是有序自增的,因此在MySQL中使用B+ Tree索引结构时可以保证高效插入性能。因此,在实际业务中,雪花算法常用于数据库主键ID和业务关联主键的生成。

雪花算法生成ID重复问题

假设:在一个订单微服务中,通过雪花算法生成ID,并部署了三个节点,且标识位一致。在这种情况下,如果有200个并发请求,均匀分布在三个节点上,且这三个节点在同一毫秒内生成的ID序列号相同,就可能产生重复的ID。

从上述假设场景可以看出,雪花算法生成ID冲突需要满足以下条件:

        1.服务通过集群方式部署,且部分机器的标识位一致。
        2.业务存在一定的并发量;没有并发量则不会触发重复问题。
        3.生成ID的时机:同一毫秒内的序列号一致。

1. 标识位如何定义

如果能保证标识位不重复,则生成的雪花ID也不会重复。通过前面的案例可以看出,ID重复的必要条件。如果要避免服务内产生重复的ID,需要从标识位上进行调整。

我们来看一下开源框架中如何使用雪花算法定义标识位。

Mybatis-Plus v3.4.2:其雪花算法实现类Sequence提供了两种构造方法:无参构造方法会自动生成dataCenterIdworkerId;有参构造方法则可以在创建Sequence实例时明确指定标识位。
Hutool v5.7.9:参考了Mybatis-Plus的dataCenterIdworkerId生成方案,并提供了默认实现。

下面,我们一起看看Sequence类的默认无参构造方法是如何生成dataCenterIdworkerId的。

public static long getDataCenterId(long maxDatacenterId) {
    long id = 1L; // 初始化数据中心ID为1L
    final byte[] mac = NetUtil.getLocalHardwareAddress(); // 获取本地硬件地址(MAC地址)
    if (null != mac) { // 如果MAC地址不为空
        // 计算数据中心ID:
        // 取MAC地址的倒数第二个字节ÿ

你可能感兴趣的:(分布式)