雪花算法生成ID及其在MyBatis中的应用

引言

在分布式系统中,唯一标识符是非常重要的。一种常见的需求是生成全局唯一的ID,以便在分布式环境中进行数据操作。Java雪花算法是一种广泛应用的ID生成算法,它可以生成全局唯一的64位长整型ID。本文将介绍Java雪花算法的原理、代码实现,并探讨其在MyBatis中的应用。

Java雪花算法原理

雪花算法的核心思想是将一个64位的长整型ID划分成不同的部分,每个部分表示不同的含义。根据这些部分的取值范围和位数来保证ID的唯一性。

雪花算法ID的结构

雪花算法生成的ID可以分为以下几个部分:

  1. 1位标识符:由于Java的long类型是有符号的,所以为了保证ID为正整数,第一位固定为0。
  2. 41位时间戳:表示生成ID的时间戳,精确到毫秒级别。可以支持69年的时间戳。
  3. 10位工作机器ID:表示机器的唯一标识符。可以支持1024台机器。
  4. 12位序列号:表示在同一毫秒内生成的不同ID的序号。可以支持同一毫秒内的4096个ID。

Java代码实现

下面是Java雪花算法生成ID的代码实现:

public class SnowflakeIdGenerator {
    
    private static final long START_TIMESTAMP = 1609430400000L;  // 2021-01-01 00:00:00
    
    private static final long WORKER_ID_BITS = 10L;
    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
    
    private static final long SEQUENCE_BITS = 12L;
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long TIMESTAMP_SHIFT = WORKER_ID_BITS + WORKER_ID_SHIFT;
    
    private long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    
    public SnowflakeIdGenerator(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("Worker ID can't be greater than " + MAX_WORKER_ID + " or less than 0");
        }
        this.workerId = workerId;
    }
    
    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) & ((1 << SEQUENCE_BITS) - 1);
            if (sequence == 0) {
                currentTimestamp = waitNextMillis(currentTimestamp);
            }
        } else {
            sequence = 0L;
        }
        
        lastTimestamp = currentTimestamp;
        
        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }
    
    private long waitNextMillis(long currentTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= currentTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

MyBatis中的应用

MyBatis是一个流行的Java持久层框架,提供了方便的数据库操作功能。在MyBatis中,我们可以使用雪花算法生成的ID作为数据库表的主键。

创建数据库表

首先,我们需要创建一个包含雪花算法ID的数据库表。假设我们有一个用户表,包含idname两个字段。

CREATE TABLE `user` (
  `id` BIGINT UNSIGNED NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`)
);

使用雪花算法生成ID插入数据

在MyBatis中,我们可以使用雪花算法生成ID并插入数据。

@Mapper
public interface UserMapper {
    
    @Insert("INSERT INTO `user` (`id`, `name`) VALUES (#{id}, #{name})")
    void insert(User user);