分布式id生成-雪花算法(SnowFlake)实现

1 雪花算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

  • 1bit,不用,二进制中最高位是符号位,0表示正数,固定是0
  • 41bit,毫秒时间戳,2^41-1毫秒时间戳表示可使用69年
  • 10bit,工作机器id,前5位表示机房id,后5位表示机器id,最大支持2^5 * 2^5=1024个机器
  • 12bit,序列号,表示同一机器同一时间戳内可以产生的4096个ID序号

2 算法实现(java)

package com.demo.utils;

/**
 * @author huwenlong
 * @version 1.0
 * @date 2020/9/17 8:40 下午
 */
public class SnowFlake {

    /**
     * 时间偏移量,从2020-09-17 20:41:42开始算起
     */
    private final long twepoch = 1600346502216L;

    /**
     * 时间戳偏移量
     */
    private final int timestampOffset = 22;

    /**
     * 机房id
     */
    private final long datacenterId;

    /**
     * 机房id偏移量
     */
    private final int datacenterIdOffset = 17;

    /**
     * 机器id
     */
    private final long workerId;

    /**
     * 机器id偏移量
     */
    private final int workerIdOffset = 12;

    /**
     * 当前序号
     */
    private long currentSequence = 0L;

    /**
     * 最大序号
     */
    private final int maxSequence = 4095;

    /**
     * 最后一次生成id的时间
     */
    private long lastTimestamp = 0L;

    public SnowFlake(long datacenterId, long workerId) {
        this.datacenterId = datacenterId;
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            // 时钟回调抛异常
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                    lastTimestamp - timestamp));
        }
        if (lastTimestamp == timestamp) {
            // 如果同一毫秒内生成id则currentSequence自增
            currentSequence = (currentSequence + 1) & maxSequence;
            if (currentSequence == 0) {
                //当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 进入下一毫秒,从0开始
            currentSequence = 0;
        }
        // 更新lastTimestamp
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampOffset) | (datacenterId << datacenterIdOffset) | (workerId << workerIdOffset) | currentSequence;
    }

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

3 算法测试

3.1 效果测试

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(1, 1);
        for (int i = 0; i < 10; i++) {
            System.out.println(snowFlake.nextId());
        }
    }

输出

13181447704576
13181451898880
13181451898881
13181451898882
13181451898883
13181451898884
13181451898885
13181451898886
13181451898887
13181451898888

3.2 正确性测试

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(1, 1);
        Set<Long> set = new HashSet<>(10000);
        for (int i = 0; i < 3000000; i++) {
            set.add(snowFlake.nextId());
        }
        Assert.isTrue(set.size() == 3000000, "error");
    }

3.3 效率测试

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(1, 1);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 3000000; i++) {
            snowFlake.nextId();
        }
        System.out.println("cost:" + (System.currentTimeMillis() - startTime));
    }

输出:cost:734,300w个id仅需要734ms,效率还是不错的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值