Twitter的分布式系统中ID生成方法——Snowflake
/**
*
*/
package utility; /**
* @author Lumin(At Home)
* Twitter's Concurrent Id Generator -- SnowFlake
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0;
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker
* 类的startTime属性)。41位的时间截,可以使用69年,即 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69.
* 10位的数据机器位,可以部署在1024个节点,包括:
* 5位datacenter Id:数据中心机器号
* 5位worker Id:工作机器号,预分配不重复的ID
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),
* 并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowFlakeIdGenerator {
//开始时间戳:2015-01-01
private final long twitEpoch = 1420041600000L; //机器ID的位数:5
private final long workerIdBits = 5L; //数据中心ID的位数:5
private final long datacenterIdBits = 5L; //机器ID的最大值:31
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); //数据中心ID的最大值:31
private final long maxDatacenterId = -1L ^ ( - 1L << datacenterIdBits); //序列所占的位数:12
private final long sequenceBits = 12L; //机器ID的偏移位数:12
private final long workerIdShift = sequenceBits; //数据中心的偏移位数:12 + 5 = 17
private final long datacenterIdShift = sequenceBits + workerIdBits; //时间戳的偏移位数:12 + 5 + 5 = 22
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //生成序列的掩码,这里为:0b111111111111=0xfff=4095
private final long sequenceMask = -1L ^ (-1L << sequenceBits); //工作机器的ID
private long workerId; //数据中心的ID
private long datacenterId; //毫秒内的序列值
private long sequence = 0L; //上一次生成序列的时间戳
private long lastTimestamp = -1L; /***
* 构造器
* @param workerId 工作机器的ID
* @param datacenterId 数据中心的ID
*/
public SnowFlakeIdGenerator(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id cannot be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id cannot be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
} /***
* 获取下一个ID(线程安全的)
* @return SnowFlakeID
*/
public synchronized long getNextId() {
//获取时间戳
long timestamp = getTimeStamp(); //当前时间戳小于上一次分配时的时间戳,证明系统时钟经过了回退,此时直接抛出异常即可
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("clock moved backwards. Refused to generate id for %d milliseconds", lastTimestamp));
} //如果是同一个时间戳内,则进行毫秒内的序列生成
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
//当前毫秒内的序列号用完了,等待至下一秒再分配
timestamp = waitForNextMillis(lastTimestamp);
}
// 时间戳改变,重置毫秒内的序列为0
} else {
sequence = 0L;
} lastTimestamp = timestamp; //拼接ID
return ((timestamp - twitEpoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
} /***
* 用循环进行当前毫秒的阻塞,直至新的时间戳
* @param lastTimestamp 上一次生成ID的时间戳
* @return 新的时间戳
*/
protected long waitForNextMillis(long lastTimestamp) {
long timestamp = getTimeStamp();
while(timestamp <= lastTimestamp) {
timestamp = getTimeStamp();
}
return timestamp;
} /***
* 当前时间
* @return 以毫秒为单位的当前时间
*/
protected long getTimeStamp() {
return System.currentTimeMillis() / 1000; //除以1000是为了将毫秒放大到秒,便于观察ID溢出
}
}
package test;
import utility.SnowFlakeIdGenerator;
public class SnowFlakeTest {
/***
* 测试Snowflake
* @param args
*/
public static void main(String[] args) {
SnowFlakeIdGenerator snowFlaker = new SnowFlakeIdGenerator(22, 11);
for(int i = 0; i < 100000; i++) {
System.out.println(Long.toBinaryString(snowFlaker.getNextId()));
}
}
}
1010110101101110001011111011001011100001110101110110101101000011
1010110101101110001011111011001011100001110101110110101101000100
1010110101101110001011111011001011100001110101110110101101000101
1010110101101110001011111011001011100001110101110110101101000110
1010110101101110001011111011001011100001110101110110101101000111
1010110101101110001011111011001011100001110101110110101101001000
1010110101101110001011111011001011100001110101110110101101001001
1010110101101110001011111011001011100001110101110110101101001010
1010110101101110001011111011001011100001110101110110101101001011
1010110101101110001011111011001011100001110101110110101101001100
1010110101101110001011111011001011100001110101110110101101001101
1010110101101110001011111011001011100001110101110110101101001110
1010110101101110001011111011001011100001110101110110101101001111
1010110101101110001011111011001011100001110101110110101101010000
1010110101101110001011111011001011100001110101110110101101010001
1010110101101110001011111011001011100001110101110110101101010010
1010110101101110001011111011001011100001110101110110101101010011
1010110101101110001011111011001011100001110101110110101101010100
1010110101101110001011111011001011100001110101110110101101010101
...
Twitter的分布式系统中ID生成方法——Snowflake的更多相关文章
- 关于全局唯一ID生成方法
引:最近业务开发过程中需要涉及到全局唯一ID生成.之前零零总总的收集过一些相关资料,特此整理以便后用 本博客已经迁移至:http://cenalulu.github.io/ 本篇博文已经迁移,阅读全文 ...
- 细聊分布式ID生成方法
细聊分布式ID生成方法 https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=403837240&idx=1&sn=ae9 ...
- 分布式系统唯一ID生成方案汇总【转】
转自:http://www.cnblogs.com/haoxinyue/p/5208136.html 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结.生成ID的方法有很 ...
- 分布式ID生成方法-趋势有序的全局唯一ID
一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...
- 【58沈剑架构系列】细聊分布式ID生成方法
一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...
- 160302、细聊分布式ID生成方法
一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...
- 【转载】细聊分布式ID生成方法
一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...
- 分布式系统唯一ID生成方案汇总
系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结.生成ID的方法有很多,适应不同的场景.需求以及性能要求.所以有些比较复杂的系统会有多个ID生成的策略.下面就介绍一些常见 ...
- 分布式环境下的id生成方法
分布式环境下的id生成方法 前几天研究数据库分表分库的问题,其中有一个关键的地方就是生成唯一键的问题,假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash ...
随机推荐
- python---统计列表中数字出现的次数
import collections a = [1,2,3,1,2,3,4,1,2,5,4,6,7,7,8,9,6,2,23,4,2,1,5,6,7,8,2] b = collections.Coun ...
- mysql 数据库安装步骤个人总结
1.mysql-5.7.19-winx64.zip(此为免安装版,318兆左右,还有一种是安装版,380兆左右mysql-installer-community-5.7.19.0.msi)将此安装包解 ...
- ios 初体验<UILabel控件>
创建控件: UILabel *label = [[UILabel alloc]init]; //设置控件大小 label.frame = CGRectMake(50,100,300,40);//分别为 ...
- mysql varchar和char的根本区别深度详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt337 VARCHAR 和 CHAR 是两种最主要的字符串类型 .不幸的是,很 ...
- 玩一把JS的链式调用
链式调用我们平常用到很多,比如jQuery中的$(ele).show().find(child).hide(),再比如angularjs中的$http.get(url).success(fn_s).e ...
- 八,ESP8266 文件保存数据
应该是LUA介绍8266的最后一篇,,,,,,下回是直接用SDK,,然后再列个12345.......不过要等一两个星期,先忙完朋友的事情 前面几篇 用AT指令版本的 一, http://www.c ...
- Tomca软件介绍和安装
Web开发入门 软件的结构: C/S (Client - Server 客户端-服务器端) 典型应用:QQ软件 ,飞秋,红蜘蛛. 特点: 1)必须下载特定的客户端程序. 2)服务器端升级,客户端升级 ...
- 【Alpha】——First scrum Meeting
一.今日站立式会议照片 二.每个人的工作 成员 昨天已完成的工作 今天计划完成的工作 · 李永豪 编写测试计划 学习JAVA编程及UI设计 · 郑靖涛 Alpha任务分配计划 学习JAVA编程及UI设 ...
- 201521123050 《Java程序设计》第5周学习总结
1. 本周学习总结 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出结果. 答:不 ...
- evak购物车--团队博客
1. 团队名称.团队成员介绍(需要有照片) 团队名称:evak 团队成员介绍:陈凯欣,计算机工程学院网络工程1512,学号为201521123034:邱晓娴,计算机工程学院网络工程1512,学号为20 ...