雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID

各 bit 含义:

  • 1 bit: 符号位,0 是正数 1 是负数, ID 为正数,所以恒取 0
  • 41 bit: 时间差,我们可以选择一个参考点,用它来计算与当前时间的时间差 (毫秒数),41 bit 存储时间差,足够使用 69 年
  • 10 bit: 机器码,能编码 1024 台机器;可以手动指定含义,比如前5 bit 作为机器编号、后 5 bit 作为进程编号
  • 12 bit: 序列号,同一机器同一毫秒内产生不同的序列号,12 bit 可以支持 4096 个序列号

优点:

  • 灵活配置:机器码可以根据需求灵活配置含义
  • 无需持久化:如果序号自增往往需要持久化,本算法不需要持久化
  • ID 有含义/可逆性:ID 可以反解出来,对 ID 进行统计分析,可以很简单的分析出整个系统的繁忙曲线,还可以定位到每个机器,在某段时间承担了多少工作,分析出负载均衡情况
  • 高性能:生成速度很快
public class Snowflake {

    /**
* 每一部分所占位数
*/
private final long unusedBits = 1L;
private final long timestampBits = 41L;
private final long datacenterIdBits = 5L;
private final long workerIdBits = 5L;
private final long sequenceBits = 12L; /**
* 向左的位移
*/
private final long timestampShift = sequenceBits + datacenterIdBits + workerIdBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long workerIdShift = sequenceBits; /**
* 起始时间戳,初始化后不可修改
*/
private final long epoch = 1451606400000L; // 2016-01-01 /**
* 数据中心编码,初始化后不可修改
* 最大值: 2^5-1 取值范围: [0,31]
*/
private final long datacenterId; /**
* 机器或进程编码,初始化后不可修改
* 最大值: 2^5-1 取值范围: [0,31]
*/
private final long workerId; /**
* 序列号
* 最大值: 2^12-1 取值范围: [0,4095]
*/
private long sequence = 0L; /** 上次执行生成 ID 方法的时间戳 */
private long lastTimestamp = -1L; /*
* 每一部分最大值
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 2^5-1
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 2^5-1
private final long maxSequence = -1L ^ (-1L << sequenceBits); // 2^12-1 /**
* 生成序列号
*/
public synchronized long nextId() {
long currTimestamp = timestampGen(); if (currTimestamp < lastTimestamp) {
throw new IllegalStateException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - currTimestamp));
} if (currTimestamp == lastTimestamp) {
sequence = (sequence + 1) & maxSequence;
if (sequence == 0) { // overflow: greater than max sequence
currTimestamp = waitNextMillis(currTimestamp);
} } else { // reset to 0 for next period/millisecond
sequence = 0L;
} // track and memo the time stamp last snowflake ID generated
lastTimestamp = currTimestamp; return ((currTimestamp - epoch) << timestampShift) | //
(datacenterId << datacenterIdShift) | //
(workerId << workerIdShift) | // new line for nice looking
sequence;
} public Snowflake(long datacenterId, long workerId) {
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
} this.datacenterId = datacenterId;
this.workerId = workerId;
} /**
* 追踪调用 waitNextMillis 方法的次数
*/
private final AtomicLong waitCount = new AtomicLong(0); public long getWaitCount() {
return waitCount.get();
} /**
* 循环阻塞直到下一秒
*/
protected long waitNextMillis(long currTimestamp) {
waitCount.incrementAndGet();
while (currTimestamp <= lastTimestamp) {
currTimestamp = timestampGen();
}
return currTimestamp;
} /**
* 获取当前时间戳
*/
public long timestampGen() {
return System.currentTimeMillis();
}
}

参考:snowflake ID 生成算法

完整代码:GitHub

雪花算法 Java 版的更多相关文章

  1. 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

    常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...

  2. 排序算法系列:插入排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 和选择排序类似的是也分成“已排序”部分,和“未排 ...

  3. 排序算法系列:选择排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 从数组头元素索引i开始,寻找后面最小的值(比i位 ...

  4. 排序算法系列:快速排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 为了不误人子弟耽误时间,推荐看一些靠谱的资源,如[啊哈!算法]系列: https: ...

  5. 常用排序算法--java版

    package com.whw.sortPractice; import java.util.Arrays; public class Sort { /** * 遍历一个数组 * @param sor ...

  6. Kruskal算法java版

    /** * sample Kruskal.java Description: * kruskal算法的思想是找最小边,且每次找到的边不会和以找出来的边形成环路,利用一个一维数组group存放当前顶点所 ...

  7. 快速排序算法Java版

    网上关于快速排序的算法原理和算法实现都比较多,不过java是实现并不多,而且部分实现很难理解,和思路有点不搭调.所以整理了这篇文章.如果有不妥之处还请建议.首先先复习一些基础.    1.算法概念. ...

  8. 爬山算法 | Java版HA_TSP

    嗯哼,今天记录下采用Java编写的爬山算法(Hill Algorithm)求解TSP问题. 爬山算法与其他智能算法类似,是一种用来求解多峰函数最值的算法,爬山算法的基本思想是新解不劣于当前解则转移,否 ...

  9. A*(也叫A star, A星)寻路算法Java版

    寻路算法有非常多种,A*寻路算法被公觉得最好的寻路算法. 首先要理解什么是A*寻路算法,能够參考这三篇文章: http://www.gamedev.net/page/resources/_/techn ...

随机推荐

  1. 秒懂Dubbo接口(原理篇)

    引言 背景 单一应用架构 垂直应用架构 分布式服务架构 流动计算架构 为什么要用 Dubbo? 什么是分布式? 为什么要分布式? Dubbo 的架构 Dubbo 的架构图解 Dubbo 工作原理 Du ...

  2. css 背景图片自适应分辨率大小 兼容

    拉伸,all浏览器兼容.bg{     background:url(http://wyz.67ge.com/wp-content/uploads/qzlogo.jpg);     filter:&q ...

  3. jquery播放图片

    * { margin:0; padding:0; word-break:break-all; } body { background:#FFF; color:#333; font:12px/1.5em ...

  4. vgg学习

    LeNet-5是一个较简单的卷积神经网络.下图显示了其结构:输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后使用softmax分类作为输出层. AlexNet中包含了几个比较新的技术点, ...

  5. 打包项目成war包并部署到服务器上,项目运行一直显示加载中

    查看服务器上的Tomcat路径下的log 显示 org.apache.catalina.LifecycleException: Failed to initialize component [Conn ...

  6. Vue + WebRTC 实现音视频直播(附自定义播放器样式)

    1. 什么是WebRTC 1.1 WebRTC简介 WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频 ...

  7. 没有真实串口设备时使用"虚拟串口驱动"调试你的串口代码

    目录 前言 示例代码 总结 前言 很多时候需要编写串口代码,但是又没有真实串口设备来调试代码.以及本身就是要操作2个串口的情况,可以使用"虚拟串口驱动"工具方便的调试代码. 使用方 ...

  8. nginx&http 第三章 ngx 事件event accept epoll /init

    tcp 三次握手成功后,listen fd  可读,在process_event_timer 中调用rev->handler(rev)处理: 其回调函数为: ngx_event_accept / ...

  9. vue 使用中的小技巧 (一)

    在vue的使用过程中会遇到各种场景,当普通使用时觉得没什么,但是或许优化一下可以更高效更优美的进行开发.下面有一些我在日常开发的时候用到的小技巧 data 和 Object.freeze 每个Vue实 ...

  10. python杂乱知识点

    1. =  == is =: ==:比较 值是否相等 is:比较,比较的是内存地址 2. id(内容):得到内容的起始内存地址 3.数字,字符串,存在小数据池的概念,如果如果创建了一样的数字或者字符串 ...