1.snowflake简介

互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并发巨大的业务要求ID生成效率高,吞吐大;比如某些银行类业务,需要按每日日期制定交易流水号;又比如我们希望用户的ID是随机的,无序的,纯数字的,且位数长度是小于10位的。等等,不同的业务场景需要的ID特性各不一样,于是,衍生了各种ID生成器,但大多数利用数据库控制ID的生成,性能受数据库并发能力限制,那么有没有一款不需要依赖任何中间件(如数据库,分布式缓存服务等)的ID生成器呢?本着取之于开源,用之于开源的原则,今天,特此介绍Twitter开源的一款分布式自增ID算法snowflake,并附上算法原理推导和演算过程!

snowflake算法是一款本地生成的(ID生成过程不依赖任何中间件,无网络通信),保证ID全局唯一,并且ID总体有序递增,性能每秒生成300w+。

2.snowflake算法原理

snowflake生产的ID是一个18位的long型数字,二进制结构表示如下(每部分用-分开):

0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000 - 00000 - 00000000 0000

第一位未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年,从1970-01-01 08:00:00),然后是5位datacenterId(最大支持2^5=32个,二进制表示从00000-11111,也即是十进制0-31),和5位workerId(最大支持2^5=32个,原理同datacenterId),所以datacenterId*workerId最多支持部署1024个节点,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生2^12=4096个ID序号).

所有位数加起来共64位,恰好是一个Long型(转换为字符串长度为18).

单台机器实例,通过时间戳保证前41位是唯一的,分布式系统多台机器实例下,通过对每个机器实例分配不同的datacenterId和workerId避免中间的10位碰撞。最后12位每毫秒从0递增生产ID,再提一次:每毫秒最多生成4096个ID,每秒可达4096000个。理论上,只要CPU计算能力足够,单机每秒可生产400多万个,实测300w+,效率之高由此可见。

(该节改编自:http://www.cnblogs.com/relucent/p/4955340.html)

3.snowflake算法源码(java版)

@ToString
@Slf4j
public class SnowflakeIdFactory {private final long twepoch = 1288834974657L;private final long workerIdBits = 5L;private final long datacenterIdBits = 5L;private final long maxWorkerId = -1L ^ (-1L << workerIdBits);private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private final long sequenceBits = 12L;private final long workerIdShift = sequenceBits;private final long datacenterIdShift = sequenceBits + workerIdBits;private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final long sequenceMask = -1L ^ (-1L << sequenceBits);private long workerId;private long datacenterId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdFactory(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;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {//服务器时钟被调整了,ID生成器停止服务.throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;}protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}public static void testProductIdByMoreThread(int dataCenterId, int workerId, int n) throws InterruptedException {List<Thread> tlist = new ArrayList<>();Set<Long> setAll = new HashSet<>();CountDownLatch cdLatch = new CountDownLatch(10);long start = System.currentTimeMillis();int threadNo = dataCenterId;Map<String,SnowflakeIdFactory> idFactories = new HashMap<>();for(int i=0;i<10;i++){//用线程名称做map key.idFactories.put("snowflake"+i,new SnowflakeIdFactory(workerId, threadNo++));}for(int i=0;i<10;i++){Thread temp =new Thread(new Runnable() {@Overridepublic void run() {Set<Long> setId = new HashSet<>();SnowflakeIdFactory idWorker = idFactories.get(Thread.currentThread().getName());for(int j=0;j<n;j++){setId.add(idWorker.nextId());}synchronized (setAll){setAll.addAll(setId);log.info("{}生产了{}个id,并成功加入到setAll中.",Thread.currentThread().getName(),n);}cdLatch.countDown();}},"snowflake"+i);tlist.add(temp);}for(int j=0;j<10;j++){tlist.get(j).start();}cdLatch.await();long end1 = System.currentTimeMillis() - start;log.info("共耗时:{}毫秒,预期应该生产{}个id, 实际合并总计生成ID个数:{}",end1,10*n,setAll.size());}public static void testProductId(int dataCenterId, int workerId, int n){SnowflakeIdFactory idWorker = new SnowflakeIdFactory(workerId, dataCenterId);SnowflakeIdFactory idWorker2 = new SnowflakeIdFactory(workerId+1, dataCenterId);Set<Long> setOne = new HashSet<>();Set<Long> setTow = new HashSet<>();long start = System.currentTimeMillis();for (int i = 0; i < n; i++) {setOne.add(idWorker.nextId());//加入set}long end1 = System.currentTimeMillis() - start;log.info("第一批ID预计生成{}个,实际生成{}个<<<<*>>>>共耗时:{}",n,setOne.size(),end1);for (int i = 0; i < n; i++) {setTow.add(idWorker2.nextId());//加入set}long end2 = System.currentTimeMillis() - start;log.info("第二批ID预计生成{}个,实际生成{}个<<<<*>>>>共耗时:{}",n,setTow.size(),end2);setOne.addAll(setTow);log.info("合并总计生成ID个数:{}",setOne.size());}public static void testPerSecondProductIdNums(){SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2);long start = System.currentTimeMillis();int count = 0;for (int i = 0; System.currentTimeMillis()-start<1000; i++,count=i) {/**  测试方法一: 此用法纯粹的生产ID,每秒生产ID个数为300w+ */idWorker.nextId();/**  测试方法二: 在log中打印,同时获取ID,此用法生产ID的能力受限于log.error()的吞吐能力.* 每秒徘徊在10万左右. *///log.error("{}",idWorker.nextId());}long end = System.currentTimeMillis()-start;System.out.println(end);System.out.println(count);}public static void main(String[] args) {/** case1: 测试每秒生产id个数?*   结论: 每秒生产id个数300w+ *///testPerSecondProductIdNums();/** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复?*   结论: 验证通过,没有重复. *///testProductId(1,2,10000);//验证通过!//testProductId(1,2,20000);//验证通过!/** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复?*   结论: 验证通过,没有重复. */try {testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半!} catch (InterruptedException e) {e.printStackTrace();}}
}

测试用例:

/** case1: 测试每秒生产id个数?*   结论: 每秒生产id个数300w+ */
//testPerSecondProductIdNums();/** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复?*   结论: 验证通过,没有重复. */
//testProductId(1,2,10000);//验证通过!
//testProductId(1,2,20000);//验证通过!/** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复?*   结论: 验证通过,没有重复. */
try {testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半!
} catch (InterruptedException e) {e.printStackTrace();
}

4.snowflake算法推导和演算过程

说明:

演算使用的对象实例:SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2);

运行时数据workerId=1,datacenterId=2,分别表示机器实例的生产者编号,数据中心编号;

sequence=0表示每毫秒生产ID从0开始计数递增;

以下演算基于时间戳=1482394743339时刻进行推导。

一句话描述:以下演算模拟了1482394743339这一毫秒时刻,workerId=1,datacenterId=2的id生成器,生产第一个id的过程。

(图片原创,转载请注明出处,画图不易,谢谢!)

end!

参考

https://github.com/twitter/snowflake

http://www.cnblogs.com/relucent/p/4955340.html

[详解]Twitter开源分布式自增ID算法snowflake,附演算验证过程相关推荐

  1. Twitter的分布式自增ID算法Snowflake实现分析及其Java、Php和Python版

    在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12 ...

  2. Twitter的分布式自增ID算法snowflake

    全局ID 要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易.而这个交易ID由谁来分配是一件比较头疼的事.因为这个标识要能做到全局唯一. 如果由一个中心系统来分配,那么每一次交易都需 ...

  3. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  4. 分布式自增ID算法-Snowflake详解

    1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...

  5. 基于.NET Standard的分布式自增ID算法--Snowflake

    概述 本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法.搞.NET开发的,数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier. 那么为何还要引 ...

  6. UUID实现之一twitter的分布式自增IDsnowflake算法

    Twitter的分布式自增ID算法snowflake (Java版) 概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首 ...

  7. 详解阿里开源分布式事务框架Seata

    Seata发展历史 笔者于2014年开始着手解决阿里巴巴集团内部业务的分布式事务问题,从0到1研发一个支持非侵入模式(内部称之为AT模式,即自动模式)和TCC模式(内部称之为MT模式,即手动模式)的分 ...

  8. 基于.NET Standard的分布式自增ID算法--美团点评LeafSegment

    概述 前一篇文章讲述了最流行的分布式ID生成算法snowflake,本篇文章根据美团点评分布式ID生成系统文章,介绍另一种相对更容易理解和编写的分布式ID生成方式. 实现原理 Leaf这个名字是来自德 ...

  9. 分布式自增ID算法---雪花算法 (snowflake,Java版)---算法001

    一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. 方案一,通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID.这种呢,优点是可以体现全局的递增 ...

最新文章

  1. java语言显示运算时间_JAVA语言之ACM日期计算:输入一个日期,格式如:2010 10 24 ,判断这一天是这一年中的第几天...
  2. Linux共享库 base64库
  3. SpringCloud教程- 路由网关Zuul (SpringCloud版本Greenwich.SR4)
  4. 不用第三方插件如何统计自己wordpress的访问量
  5. 消息中间件系列(五):MQ消息队列的12点核心原理总结
  6. 基于java百货中心供应链管理系统(含源文件)
  7. OkHttp3详细使用教程(2)
  8. java和前端哪个难学?
  9. Vitalik:Rollups预计在短期和中长期成为以太坊扩容的基石
  10. Extjs store用法详解
  11. Julia: save 与 @save
  12. 西门子定位器6DR5020-0NG00-0AA0
  13. 有意思的文章 - 语音相关 - list
  14. 邮箱收取后删除服务器邮件设置,邮件为什么会被客户端(POP)收取并删除?能不能避免这种情况?...
  15. 中国居民身份证号码检验程序
  16. html5远程桌面 微软,微软正在测试远程桌面HTML5网页版本客户端!
  17. Vue~在线预览doc、docx、pdf、img文件
  18. 总结图扑软件可实现的可视化效果案例分享
  19. VB如何自动保存_VB编写的身份证号自动识别实用程序
  20. 【HTTP】HTTP Authorization 之 Basic Auth 和 X-Auth-Token

热门文章

  1. 总在“被卖身”的ofo:不卖!我能养活自己!
  2. 巴什博奕(Bash Game)与威佐夫博弈(Wythoff game)
  3. 取消Word开启后自动显示所有标记
  4. 小米移动广告联盟--记录关于其中的一些坑
  5. Python毕业设计论文计算机专业毕业论文基于Python实现的作业查重系统[包运行成功]
  6. 面经|猿辅导-数据分析|终面+HR面|30+40min
  7. Sips -- 图片处理
  8. 深度学习篇之tensorflow(2) ---图像识别
  9. Excel第13享:countif()函数之模糊计数
  10. 讲解开模行程板要注意的事项