Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的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
...

参考:分布式系统中唯一ID的生成方法

Twitter的分布式系统中ID生成方法——Snowflake的更多相关文章

  1. 关于全局唯一ID生成方法

    引:最近业务开发过程中需要涉及到全局唯一ID生成.之前零零总总的收集过一些相关资料,特此整理以便后用 本博客已经迁移至:http://cenalulu.github.io/ 本篇博文已经迁移,阅读全文 ...

  2. 细聊分布式ID生成方法

    细聊分布式ID生成方法 https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=403837240&idx=1&sn=ae9 ...

  3. 分布式系统唯一ID生成方案汇总【转】

    转自:http://www.cnblogs.com/haoxinyue/p/5208136.html 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结.生成ID的方法有很 ...

  4. 分布式ID生成方法-趋势有序的全局唯一ID

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  5. 【58沈剑架构系列】细聊分布式ID生成方法

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  6. 160302、细聊分布式ID生成方法

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  7. 【转载】细聊分布式ID生成方法

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  8. 分布式系统唯一ID生成方案汇总

    系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结.生成ID的方法有很多,适应不同的场景.需求以及性能要求.所以有些比较复杂的系统会有多个ID生成的策略.下面就介绍一些常见 ...

  9. 分布式环境下的id生成方法

    分布式环境下的id生成方法   前几天研究数据库分表分库的问题,其中有一个关键的地方就是生成唯一键的问题,假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash ...

随机推荐

  1. 共享Visio和project的下载链接

    好东西就应该共享  下面的是最新版的Visio和project的百度云链接 Visio的链接:http://pan.baidu.com/s/1o8UJq4M 密码:sltu project的链接:ht ...

  2. java HashSet改用

    写的一个Student类如下: 上面是直接使用的HashSet集合,系统会把new Student()  当做地址不用来出来,所以结果如下: 然后我在Student类中重写了hashCode()和eq ...

  3. adb 安装apk 报错:Failure [INSTALL_FAILED_ALREADY_EXISTS]

    遇到INSTALL_FAILED_ALREADY_EXISTS问题,直接通过adb install -r xxx.apk命令安装apk即可

  4. MVC配置伪静态

    提出需求 伪静态能提高搜索引擎收录,还不影响硬盘寿命,是一个不错的选择,但是会增加CPU和内存开销,由于时候也需要实现伪静态. web.config配置 <system.webServer> ...

  5. Web.py 框架学习笔记 - URL处理

    最近由于工作需要开始学习基于python的web应用框架web.py.为了方便学习,将学习心得逐日记下以便日后复习. URL 模板: web.py提供了一套url处理的模板,在python工程中,只需 ...

  6. https加密实现

    author:JevonWei 版权声明:原创作品 在httpd安装完成的基础上实现https加密 安装mod_ssl软件包 yum -y install mod_ssl http -M 显示mod_ ...

  7. js 中采用词法作用域

    所谓的 词法( 代码 )作用域, 就是代码在编写过程中体现出来的作用范围. 代码一旦写好, 不用执行, 作用范围就已经确定好了. 这个就是所谓词法作用域. 在 js 中词法作用域规则: 1.函数允许访 ...

  8. ASP.NET 控制器

    1.继承Controller public class TestController : Controller2.编写控制器方法    // URL  :   test/Edit/1        [ ...

  9. pip install python 如何快速安装模块

    之前python安装模块要在网络上下载,从python2.7.9之后,以及python3,python就自带pip 这个命令,能够快速的安装模块 1,  首先打开python的主文件夹 2.在主文件夹 ...

  10. 电商Banner设计背后的12个人性的秘密