分布式雪花算法工具类

SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。

SnowFlake算法的优点:

  • 高性能高可用:生成时不依赖于数据库,完全在内存中生成。
  • 容量大:每秒中能生成数百万的自增ID。
  • ID自增:存入数据库中,索引效率高。

SnowFlake算法的缺点:
依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复。

SnowFlakeUtil工具类

package com.gmaya.springbootrabbitmq.utils;

import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 雪花算法 分布式 id生成工具类
 * @author GMaya
 * @dateTime 2020/4/21 9:36
 */
@Slf4j
public class SnowFlakeUtil {
    private final long id;
    /**
     * 时间起始标记点,作为基准,一般取系统的最近时间
     */
    private final long epoch = 1587433423721L;
    /**
     * 机器标识位数
     */
    private final long workerIdBits = 10L;
    /**
     * 机器ID最大值: 1023
     */
    private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;
    /**
     * 0,并发控制
     */
    private long sequence = 0L;
    /**
     * 毫秒内自增位
     */
    private final long sequenceBits = 12L;

    /**
     * 12
     */
    private final long workerIdShift = this.sequenceBits;
    /**
     * 22
     */
    private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;
    /**
     * 4095,111111111111,12位
     */
    private final long sequenceMask = -1L ^ -1L << this.sequenceBits;
    /**
     * 记录产生时间毫秒数,判断是否是同1毫秒
     */
    private long lastTimestamp = -1L;

    /**
     * 传入机器id
     * @param id
     */
    private SnowFlakeUtil(long id) {
        if (id > this.maxWorkerId || id < 0) {
            throw new IllegalArgumentException(String.format("机器id不能大于%d或小于0", this.maxWorkerId));
        }
        this.id = id;
    }

    public synchronized long nextId() {
        // 获取当前时间毫秒数
        long timestamp = timeGen();
        if (this.lastTimestamp == timestamp) {
            //如果上一个timestamp与新产生的相等,则sequence加一(最大4095)
            this.sequence = this.sequence + 1 & this.sequenceMask;
            if (this.sequence == 0) {
                // 超过最大值进行按位与,结果为0,也就是当这一毫秒序号超过最大值,就会循环等待下一毫秒
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }

        // 如果时间回退,则报错或者返回-1,调用端进行判断
        if (timestamp < this.lastTimestamp) {
            log.error(String.format("时钟回退,拒绝 %d 毫秒内生成雪花id", (this.lastTimestamp - timestamp)));
            return -1;
        }

        this.lastTimestamp = timestamp;
        // 当前时间-初始时间,然后左移timestampLeftShift。
        // 将机器id左移workerIdShift
        // | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。
        return timestamp - this.epoch << this.timestampLeftShift | this.id << this.workerIdShift | this.sequence;
    }

    /**
     * 如果说几十年后id重复了,把机器id加1,再用几十年
     */
    private static SnowFlakeUtil flowIdWorker = new SnowFlakeUtil(1);


    public static long getSnowFlakeId() {
        return flowIdWorker.nextId();
    }

    /**
     * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 获得系统当前毫秒数
     */
    private static long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        //判断生成的记录是否有重复记录
        final Map<Long, Integer> map = new ConcurrentHashMap();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                for (int s = 0; s < 2000; s++) {
                    long snowFlakeId = SnowFlakeUtil.getSnowFlakeId();
                    log.info("生成雪花ID={}",snowFlakeId);
                    Integer put = map.put(snowFlakeId, 1);
                    if (put != null) {
                        throw new RuntimeException("主键重复");
                    }
                }
            }).start();

        }
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值