Java每天唯一递增序列

在很多实际应用中,我们需要生成一个每天唯一递增的序列,用来作为订单号、流水号等标识。这里我们将介绍如何使用Java来实现这样一个功能。

为什么需要每天唯一递增序列

在很多业务中,我们需要生成一个唯一的序列号来标识每一次操作或者交易。而每天唯一递增的序列号对于日志记录、数据统计等方面都具有很大的价值。通过每天唯一递增序列,我们可以更加方便地对数据进行归类和统计,使得系统更加健壮和稳定。

实现方法

日期+序号

一种简单的实现方式是将日期和序号结合起来生成唯一序列。每天开始时序号从1开始递增,直到当天结束。第二天再次从1开始递增。这种方式简单易懂,但是在高并发情况下可能存在性能瓶颈。

分布式ID生成器

另一种方式是使用分布式ID生成器,比如Snowflake算法。这种算法生成的ID是一个64位的整数,其中包含了时间戳、机器ID、序列号等信息,具有全局唯一性并且递增有序。这种方式适合高并发场景下的ID生成需求。

代码示例

日期+序号实现

public class DailySerialNumberGenerator {

    private static LocalDate currentDate;
    private static int currentNumber = 0;

    public synchronized static String generateSerialNumber() {
        LocalDate today = LocalDate.now();
        if (!today.equals(currentDate)) {
            currentDate = today;
            currentNumber = 0;
        }
        currentNumber++;
        return today.toString() + String.format("%04d", currentNumber);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(generateSerialNumber());
        }
    }
}

Snowflake算法实现

public class SnowflakeIdWorker {

    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;

    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("workerId can't be greater than " + maxWorkerId + " or less than 0");
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than " + maxDatacenterId + " or less than 0");
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

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

    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
        for (int i =