SnowFlake 生成全局唯一id
public class SnowFlakeUtil {
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT 标记时间 用来计算偏移量,距离当前时间不同,得到的数据的位数也不同
private long workerIdBits = 5L; // 物理节点ID长度
private long datacenterIdBits = 5L; // 数据中心ID长度
private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大支持机器节点数0~31,一共32个
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 最大支持数据中心节点数0~31,一共32个
private long sequenceBits = 12L; // 序列号12位, 4095,同毫秒内生成不同id的最大个数
private long workerIdShift = sequenceBits; // 机器节点左移12位
private long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心节点左移17位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间毫秒数左移22位
private long sequenceMask = -1L ^ (-1L << sequenceBits); // 用于和当前时间戳做比较,以获取最新时间
private long lastTimestamp = -1L;
/**
* 成员类,SnowFlakeUtil的实例对象的保存域
*
*/
private static class IdGenHolder {
private static final SnowFlakeUtil instance = new SnowFlakeUtil();
}
/**
* 外部调用获取SnowFlakeUtil的实例对象,确保不可变
*
* @return
*/
public static SnowFlakeUtil get() {
return IdGenHolder.instance;
}
/**
* 初始化构造,无参构造有参函数,默认节点都是0
*/
public SnowFlakeUtil() {
this(0L, 0L);
}
/**
* 设置机器节点和数据中心节点数,都是 0-31
*
* @param workerId
* @param datacenterId
*/
public SnowFlakeUtil(long workerId, long datacenterId) {
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;
}
/**
* 线程安全的id生成方法
*
* @return
*/
@SuppressWarnings("all")
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));
}
// 如果上次生成时间和当前时间相同,在同一毫秒内
if (lastTimestamp == timestamp) {
// sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位
sequence = (sequence + 1) & sequenceMask;
// 判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0
if (sequence == 0) {
// 自旋等待到下一毫秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,每个毫秒时间内,都是从0开始计数,最大4095
sequence = 0L;
}
lastTimestamp = timestamp;
// 最后按照规则拼出ID 64位
// 000000000000000000000000000000000000000000 00000 00000 000000000000
// 1位固定整数 time datacenterId workerId sequence
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
}
/**
* 比较当前时间和过去时间,防止时钟回退(机器问题),保证给的都是最新时间/最大时间
*
* @param lastTimestamp
* @return
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 获取当前的时间戳(毫秒)
*
* @return
*/
protected long timeGen() {
return System.currentTimeMillis();
}
/**
* 获取全局唯一id
*/
public static String getId() {
Long id = SnowFlakeUtil.get().nextId();
return id.toString();
}
}
SnowFlake 生成全局唯一id的更多相关文章
- 如何在高并发分布式系统中生成全局唯一Id
月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1. ...
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ...
- 面试官:如何在分布式场景下生成全局唯一 ID?
在分布式系统中,有一些场景需要使用全局唯一 ID ,可以和业务场景有关,比如支付流水号,也可以和业务场景无关,比如分库分表后需要有一个全局唯一 ID,或者用作事务版本号.分布式链路追踪等等,好的全局唯 ...
- 如何在高并发分布式系统中生成全局唯一Id(转)
http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文, ...
- (转)如何在高并发分布式系统中生成全局唯一Id
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ...
- 生成全局唯一ID
在实际业务处理中,有时需要生成全局唯一ID来区别同类型的不同事物,介绍一下几种方式及其C++实现 //获取全局唯一ID //server_id为服务的id,因当同一个服务部署在多个服务器上时,需要区别 ...
- 高并发分布式系统中生成全局唯一Id汇总
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
- 游戏服务器生成全局唯一ID的几种方法
在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...
- 雪花算法生成全局唯一ID
系统中某些场景少不了全局唯一ID的使用,来保证数据的唯一性.除了通过数据库自带的自增id来保证 id 的唯一性,通常为了保证的数据的可移植性会选择通过程序生成全局唯一 id.百度了不少php相关的生成 ...
随机推荐
- java tomcat报错: Starting Tomcat v7.0 Server at localhost' has encountered a problem问题
运行web项目的时候出现下面错误: 出现这个问题的原因是 这个tomcat在其他项目中正在运行 关掉即可.
- Unix进程特性
本篇文章主要总结分享记录一下运维工作中经常打交道的Unix进程.程序是代码的集合,而进程是运行中的程序产生的.那么进程都有那些特性呢?且看下文,部分经典且难懂的地方,使用python代码实现,可以让读 ...
- 如何申请新浪SAE,发布自己的网站
你是否会看见诸如(***.sinaapp.com)类的域名?是否和新浪有什么关系?抑或想要一个免费的空间展示自己的个人主页;没问题,下面我来分享一下SAE的申请流程吧! 首先,打开SAE(http:/ ...
- 764. Largest Plus Sign最大的dfs十字架
[抄题]: 求挖掉一些区域后,能允许出现的最大十字架 In a 2D grid from (0, 0) to (N-1, N-1), every cell contains a 1, except t ...
- eclipse 使用 scons 编译的配置说明
eclipse版本: eclipse-cpp-kepler-SR1-win32.zip 创建项目必须选择“Makefile Project” 然后进入“Projects Properities” 先 ...
- Python 与 Javascript 比较
最近由于工作的需要开始开发一些Python的东西,由于之前一直在使用Javascript,所以会不自觉的使用一些Javascript的概念,语法什么的,经常掉到坑里.我觉得对于从Javascript转 ...
- vim在linux下中如何设置显示行数
在.vimrc(或/etc/vimrc)文件中输入如下文本: set tabstop=4 set softtabstop=4 set shiftwidth=4 set noexpandtab ...
- [原创]Linux下使用Daemon实现服务器永久存活
很多服务器端设计者,很少会考虑到服务器永久存活或热替换的问题,貌似真的很少 其中永久存活最大的理论支持就是,我们如何真正的保证一组服务器持续的存在,即便宕机? 其实没有人能保证自己写的代码那么完美无瑕 ...
- Redhat9.0+Apache1.3.29+Mysql3.23.58+PHP4.3.4
Redhat9.0+Apache1.3.29+Mysql3.23.58+PHP4.3.4 TAG标签: 摘要:红帽创建于1993年,是目前世界上最资深的Linux和开放源代码提供商,同时也是最获认可的 ...
- UDP问题
这两天使用C#的UdpClient,本机的服务是采用MFC的socket发的,用C#做客户端,然后客户端启动时,出现该条错误信息 ==通常每个套接字地址(协议/网络地址/端口)只允许使用一次. 笔记的 ...