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 ...
随机推荐
- OC
一,字符串 1创建一个字符串 1) NSString *str2=[[NSString alloc]initWithString:str1]; 2) NSString *string2=[[NSSt ...
- Python验证码通过pytesser识别
Python安装包: 需要安装的包主要有两个: PIL 和 pytesser .tesseract (1).安装PIL:下载地址:http://www.pythonware.com/products/ ...
- Spring详解(五)------AOP
这章我们接着讲 Spring 的核心概念---AOP,这也是 Spring 框架中最为核心的一个概念. PS:本篇博客源码下载链接:http://pan.baidu.com/s/1skZjg7r 密码 ...
- 最近找java实习面试被问到的东西总结(Java方向)
时间,就是这么很悄悄的溜走了将近两个年华,不知不觉的,研二了,作为一个一般学校的研究生,不知道该说自己是不学无术,还是说有过努力,反正,这两年里,有过坚持,有过堕落,这不,突然间,有种开窍的急迫感,寻 ...
- 七,UDP
那天朋友问我为什么有UDP Sever 和 UDP Client ,,我说:每个人想的不一样,设计上不一样...... 既然是面向无连接的,那么模块发数据就指定IP和端口号,,,为了能和多个UDP ...
- 201521145048《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 1.2 List<Map.Entry<String, In ...
- 201521123074 《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 Q1.List中指定元素的删除(题目4-1) 1.1 实验总结 用Arrar ...
- 201521123045 《Java程序设计》第7周学习总结
Java 第七周总结 1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Obj ...
- 201521123108《Java程序设计》第3周学习总结
1. 本章学习总结 2. 书面作业 Q1. 代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; ...
- 201521123116 《java程序设计》第十三周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 Q1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jm ...