雪花算法(SnowFlake)

组成结构图

代码实现(基于上图)

public class SnowFlakeGenerator {

    private long roomId; //机房id

    private long workerId; //机器id

    private long roomIdBit = 5l; //占用5个bit位

    private long workerIdBit = 5l;//占用5个bit位

    //声明roomId最大的正整数 32
    private long maxRoomId = -1l ^ (-1l << roomIdBit);
    private long maxWorkerId = -1l ^ (-1l << workerIdBit);

    private long sequenceBits = 12l; //12bit的递增序列

    private long sequence; //递增开始的序列

    public SnowFlakeGenerator(long roomId, long workerId, long sequence) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id错误");
        }
        if (roomId > maxRoomId || roomId < 0) {
            throw new IllegalArgumentException("roomId Id错误");
        }
        this.roomId = roomId;
        this.workerId = workerId;
        this.sequence = sequence;
    }

    private long sequenceMask = -1l ^ (-1l << sequenceBits);
    private long lastTimeStamp = -1l; //存储上一次生成的id的时间戳

    private long twepoch = 1596372483166L;//初始的时间值

    private long workerIdShift = sequenceBits;

    private long roomIdShift = sequenceBits + workerIdBit;
    private long timeStampShift = sequenceBits + workerIdBit + roomIdBit;

    public synchronized long nextVal() {

        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimeStamp) {
            throw new RuntimeException("时间戳异常");
        }
        if (lastTimeStamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = waitToNextMills(lastTimeStamp);
            }
        } else {
            //如果进入到了新的时间毫秒,sequence从0开始
            sequence = timestamp & 1;
        }
        lastTimeStamp = timestamp;
        return ((timestamp - twepoch) << timeStampShift | (roomId << roomIdShift) | (workerId << workerIdShift) | sequence);
    }

    public long waitToNextMills(long lastTimeStamp) {
        long timeStamp = System.currentTimeMillis();
        while (timeStamp <= lastTimeStamp) {
            timeStamp = System.currentTimeMillis();
        }
        return timeStamp;
    }

    public static void main(String[] args) throws InterruptedException {
        SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator(1,1,1);
        for (int i = 0; i < 100; i++) {
            Thread.sleep(1);
            System.out.println(snowFlakeGenerator.nextVal());
        }

    }
}

这种方式的优缺点是:

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
posted @ 2020-12-16 01:02  snail灬  阅读(261)  评论(0编辑  收藏  举报