无分布式锁的ID生成

起因

TEAM GARDEN 本来ID是自增的,后面发现自增ID比较麻烦,有问题:

不可控的间隔: 如果你在插入数据时,中途删除了一些行,导致自增的ID出现间隔,那么新插入的行会填充这些间隔,可能会导致ID序列不连续,不利于数据分析和理解。

不适用于批量插入: 在批量插入数据时,自增主键可能会导致性能问题。因为每次插入都需要锁定表,以获取下一个自增ID。这可能导致大量的表锁等待,从而影响性能。

主键冲突: 在某些情况下,例如数据导入或数据同步,可能会出现主键冲突的情况。如果数据源中的主键与目标数据库中的自增ID冲突,就会导致插入失败。

难以预测的ID值: 自增ID的值通常是由数据库管理的,这意味着你不能预测下一个ID是什么。在某些情况下,你可能需要对生成的ID值进行控制或预测。

不支持外部数据源: 如果需要将外部数据源(例如其他数据库或数据文件)与数据库中的表关联,自增主键可能不太适合。你无法为外部数据源生成有效的自增ID。

所以决定自己写一个ID生成的工具

代码


public class SnowflakeIdGenerator {
    private static final long START_TIMESTAMP = 1630435200000L; // 2021-09-01 00:00:00
    private static final long MACHINE_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

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

    public SnowflakeIdGenerator(long machineId) {
        if (machineId < 0 || machineId > MAX_MACHINE_ID) {
            throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
        }
        this.machineId = machineId;
    }

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

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

        lastTimestamp = currentTimestamp;

        long id = ((currentTimestamp - START_TIMESTAMP) << (MACHINE_ID_BITS + SEQUENCE_BITS))
                | (machineId << SEQUENCE_BITS)
                | sequence;

        return id;
    }

    private long nextTimestamp(long lastTimestamp) {
        long currentTimestamp = System.currentTimeMillis();
        while (currentTimestamp <= lastTimestamp) {
            currentTimestamp = System.currentTimeMillis();
        }
        return currentTimestamp;
    }
}

使用

 resumeEntity.setId(new SnowflakeIdGenerator(1).generateId());

结论

这样生成的ID是有序的、适合大数据量的、简单、可预测且不依赖外部资源的。

你可能感兴趣的:(JAVA基础,分布式)