当下流行的分布式唯一ID生成算法-雪花算法

在分布式系统中,唯一ID的生成对于数据的溯源和分布式环境下的数据管理至关重要。传统的自增长ID或UUID在某些场景下存在一些问题,如性能、不连续等。为了解决这些问题,雪花算法(Snowflake Algorithm)应运而生。雪花算法是一种高效、趋势递增、分布式场景友好的唯一ID生成算法。本文将深入探讨雪花算法的原理、结构、优势以及在实际应用中的使用,同时增加一个章节详细介绍雪花算法的实现。

 

雪花算法的原理

雪花算法的核心思想是使用一个64位的整数作为全局唯一ID。这64位被划分为不同的部分,每一部分代表不同的信息。

1.1 符号位

第一位是符号位,固定为0,表示生成的ID为正数。

1.2 时间戳

接下来的41位表示当前时间戳,精确到毫秒级。由于使用的是毫秒级时间戳,所以这部分可以表示的时间范围是
 

Image

 年,超过这个时间范围,ID生成器将停止工作。

1.3 数据中心ID

接下来的10位是数据中心ID,用于标识不同的数据中心。通过这一部分,雪花算法可以在多数据中心的环境中保证生成的ID的唯一性。

1.4 机器ID

再接下来的12位是机器ID,用于标识同一数据中心中的不同机器。通过这一部分,雪花算法可以在同一数据中心内保证生成的ID的唯一性。

1.5 序列号

最后的12位是序列号,用于保证同一机器、同一时间戳下生成的ID的唯一性。在同一毫秒内,可以生成 

Image

 个不同的序列号。

 

雪花算法的结构

根据上述原理,雪花算法的64位整数的结构可以表示为:

0 | timestamp(ms) | data center id | machine id | sequence

雪花算法的优势

雪花算法在分布式系统中具有多方面的优势,使得它成为广泛应用的唯一ID生成算法。

3.1 高性能

雪花算法生成ID的过程简单高效,只需要进行位运算和简单的数学运算,不依赖于外部存储和网络通信,因此具有较高的性能。

3.2 唯一性

雪花算法生成的ID具有趋势递增的特性,且通过合理配置数据中心ID和机器ID,可以在分布式环境中保证ID的唯一性。

3.3 稳定性

雪花算法的生成过程不依赖于外部系统的支持,不会受到单点故障的影响,保证了算法的稳定性。

3.4 可调整性

雪花算法的位数分配合理,可以根据实际需求对时间戳、数据中心ID、机器ID和序列号进行灵活调整,适应不同规模和需求的系统。

 

雪花算法的实现

雪花算法的实现相对简单,以下是一个简单的Java实现示例:

public class SnowflakeIdGenerator {

    private static final long START_TIMESTAMP = 1636147200000L; // 起始时间戳,设置为2021-11-06 00:00:00

    private static final long DATA_CENTER_ID_BITS = 10L;
    private static final long MACHINE_ID_BITS = 12L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
    private static final long MAX_MACHINE_ID = -1L ^ (-1L << MACHINE_ID_BITS);

    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATA_CENTER_ID_BITS;

    private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

    private long dataCenterId;
    private long machineId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException("Data center ID can't be greater than " + MAX_DATA_CENTER_ID + " or less than 0");
        }
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("Machine ID can't be greater than " + MAX_MACHINE_ID + " or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public synchronized long generateId() {
        long currentTimestamp = System.currentTimeMillis();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID for " + (lastTimestamp - currentTimestamp) + " milliseconds.");
        }

        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                currentTimestamp = waitNextMillis(currentTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = currentTimestamp;

        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
                | (dataCenterId << DATA_CENTER_ID_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);

        // 生成10个ID进行演示
        for (int i = 0; i < 10; i++) {
            System.out.println("Generated ID: " + idGenerator.generateId());
        }
    }
}

上述示例中,SnowflakeIdGenerator类封装了雪花算法的实现。通过调用generateId方法,可以获取一个新的唯一ID。在实际应用中,需要根据具体的场景和需求进行配置。

 

雪花算法的考虑事项

在使用雪花算法时,需要注意一些潜在的问题和考虑事项:

5.1 时钟回拨

由于雪花算法依赖于时间戳,如果系统时钟发生回拨,可能导致生成的ID不唯一。因此,需要确保系统时钟的稳定性。

5.2 数据中心ID和机器ID分配

在多数据中心和大规模集群的情况下,需要合理分配数据中心ID和机器ID,以保证ID的唯一性。

5.3 序列号的并发

在同一毫秒内,如果并发生成的ID数量超过序列号的范围,可能导致ID重复。因此,需要根据实际情况调整序列号的位数。

5.4 长整型溢出

雪花算法生成的ID是一个64位的长整型,在一些编程语言中可能需要特殊处理长整型的溢出问题。

 实战场景

在MyBatis Plus中使用雪花算法生成ID的步骤如下:

  1. 导入相关依赖:在pom.xml文件中添加MyBatis Plus的依赖。


 com.baomidou

 mybatis-plus-boot-starter

 最新版本号

  1. 创建实体类:创建需要使用雪花算法生成ID的实体类。
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

@Data
public class YourEntity {

 @TableId(type = IdType.ASSIGN_ID)

 private Long id;

 // 其他属性...
}
  1. 配置MyBatis Plus:在Spring Boot的配置文件中增加MyBatis Plus的相关配置。
mybatis-plus:

 mapper-locations: classpath*:mapper/*.xml

 global-config:

 db-config:

 id-type: ASSIGN_ID
  1. 使用雪花算法生成ID:当插入新数据时,自动使用雪花算法生成ID。
//
通过Spring注入实体类的Mapper对象
@Autowired
private YourEntityMapper yourEntityMapper;

// 创建实体对象并设置其他属性
YourEntity yourEntity = new YourEntity();
yourEntity.setName("John Doe");
// 设置其他属性...

// 插入数据,ID会自动生成
yourEntityMapper.insert(yourEntity);

以上就是使用MyBatis Plus中如何使用雪花算法生成ID的步骤。通过配置@TableId(type = IdType.ASSIGN_ID)和在配置文件中设置id-type: ASSIGN_ID,即可使用雪花算法生成唯一的ID。

总 结

雪花算法作为一种高效、趋势递增、分布式场景友好的唯一ID生成算法,在分布式系统中得到了广泛的应用。通过合理配置参数,雪花算法可以适应不同规模和需求的系统,保证生成的ID的唯一性和有序性。在实际应用中,开发者可以根据系统的特点和需求选择合适的参数配置,以便更好地利用雪花算法的优势。

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