网上收集的一个java版的雪花算法,可以直接用
By:Roy.LiuLast updated:2021-06-01
在分布式环境下,生成唯一ID有很多解决方案,比如美团的雪花算法,百度的雪花算法,还可以用redis, zookeeper 等中间件的帮助,都可以实现,但感觉都显得麻烦了一点,比如,百度还需要你部署一个mysql,太笨重了,因此想找一个简单的轻量级的雪花算法代码。在网上收集了一个,分享在这里:
public class SnowFlake { //工作id private long workerId; //数据中心 id private long datacenterId; //12位的序列号 private long sequence; //初始时间戳 private long twepoch = 1288834974657L; //长度为5位 private long workerIdBits = 5L; private long datacenterIdBits = 5L; //最大值 private long maxWorkerId = -1L ^ (-1L << workerIdBits); private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //序列号id长度 private long sequenceBits = 12L; //序列号最大值 private long sequenceMask = -1L ^ (-1L << sequenceBits); //工作id需要左移的位数,12位 private long workerIdShift = sequenceBits; //数据id需要左移位数 12+5=17位 private long datacenterIdShift = sequenceBits + workerIdBits; //时间戳需要左移位数 12+5+5=22位 private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //上次时间戳,初始值为负数 private long lastTimestamp = -1L; public SnowFlake(long workerId, long datacenterId, long sequence){ // sanity check for workerId if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; this.sequence = sequence; } public long getWorkerId(){ return workerId; } public long getDatacenterId(){ return datacenterId; } public long getTimestamp(){ return System.currentTimeMillis(); } //下一个ID生成算法 public synchronized long nextId() { long timestamp = timeGen(); //获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常 if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } //将上次时间戳值刷新 lastTimestamp = timestamp; /** * 返回结果: * (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数 * (datacenterId << datacenterIdShift) 表示将数据id左移相应位数 * (workerId << workerIdShift) 表示将工作id左移相应位数 * | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。 * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id */ return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } //获取时间戳,并与上次时间戳比较 private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } //获取系统时间戳 private long timeGen(){ return System.currentTimeMillis(); } }
使用也很简单:
// 参数需要自己定义,workId, 数据中心,sequence, 在分布式场景,workId 一定是不一样的 SnowFlake sf = new SnowFlake(1,1,1); long id = sf.nextId();
如果需要在 spring boot 中使用这个雪花算法,也很简单,注入这个bean 就好了。
SnowFlake getIDGenerate() { SnowFlake(,,); }
然后在其他类里,就可以用 @Autowired 注入使用了。
From:一号门
Previous:在 ubuntu 上安装MySQL 之后可能遇到的坑
Next:Redis跑lua脚本的两种方式
COMMENTS