全局ID 要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易。而这个交易ID由谁来分配是一件比较头疼的事。因为这个标识要能做到全局唯一。

如果由一个中心系统来分配,那么每一次交易都需要找那个中心系统来。 这样增加了程序的性能开销。如果由上游系统来分配,则可能会导致可能会出现分配ID重复了的问题。因为 上游系统可能会是一个集群,它们同时承担相同的工作。

为了不产生分配冲突,我们需要使用一个不会冲突的算法,比如使用UUID这样冲突非常小的算法。但UUID的问题是,它的字符串占用的空间比较大,索引的效率非常低,生成 的ID太过于随机,完全不是人读的,而且没有递增,如果要按前后顺序排序的话,基本不可能。

在全局唯一ID的算法中,这里介绍一个Twitter 的开源项目 Snowfake。它是一个分布式ID的生成算法。其核心思想是,产生一个long型的ID,其中:

41bits作为毫秒数。大概可以用69.7年。 10bits作为机器编号(5bits是数据中心,5bits的机器ID),支持1024个实例。 12bits作为毫秒内的序列号。一毫秒可以生成4096个序号。

snowflake的结构如下(每部分用-分开):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

/*** Twitter_Snowflake<br>* SnowFlake的结构如下(每部分用-分开):<br>* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>* 加起来刚好64位,为一个Long型。<br>* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。*/
public class SnowflakeIdWorker {// ==============================Fields===========================================/** 开始时间截 (2015-01-01) */private final long twepoch = 1420041600000L;/** 机器id所占的位数 */private final long workerIdBits = 5L;/** 数据标识id所占的位数 */private final long datacenterIdBits = 5L;/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/** 支持的最大数据标识id,结果是31 */private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/** 序列在id中占的位数 */private final long sequenceBits = 12L;/** 机器ID向左移12位 */private final long workerIdShift = sequenceBits;/** 数据标识id向左移17位(12+5) */private final long datacenterIdShift = sequenceBits + workerIdBits;/** 时间截向左移22位(5+5+12) */private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */private final long sequenceMask = -1L ^ (-1L << sequenceBits);/** 工作机器ID(0~31) */private long workerId;/** 数据中心ID(0~31) */private long datacenterId;/** 毫秒内序列(0~4095) */private long sequence = 0L;/** 上次生成ID的时间截 */private long lastTimestamp = -1L;//==============================Constructors=====================================/*** 构造函数* @param workerId 工作ID (0~31)* @param datacenterId 数据中心ID (0~31)*/public SnowflakeIdWorker(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;}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)* @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常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 + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间* @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}//==============================Test=============================================/** 测试 */public static void main(String[] args) {SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);for (int i = 0; i < 1000; i++) {long id = idWorker.nextId();System.out.println(Long.toBinaryString(id));System.out.println(id);}}
}

参考:

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

https://github.com/twitter/snowflake

Twitter的分布式自增ID算法snowflake相关推荐

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

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

  2. [详解]Twitter开源分布式自增ID算法snowflake,附演算验证过程

    1.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. 基于.NET Standard的分布式自增ID算法--美团点评LeafSegment

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

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

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

  9. 雪花算法id长度_【Java】分布式自增ID算法雪花算法 (snowflake,Java版)

    作者:H__D 转载自: https://www.cnblogs.com/h--d/p/11342741.html 一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. ...

最新文章

  1. 2021年中国工业互联网安全大赛核能行业赛道writeup之usb流量分析
  2. 2019年全球企业人工智能发展现状分析报告
  3. Windows下如何使用FTP命令
  4. Memcached原理分析
  5. 搭建mongodb分片
  6. springboot redis 断线重连_Redis的主从复制是如何做的?复制过程中也会产生各种问题?...
  7. Building Seam 2.0 Application with NetBeans 6.1
  8. (转)mssql2005生成表字典
  9. 如何进行在线Post接口测试?
  10. ENVI学习总结(九)——图像裁剪
  11. 01. Introdunction to Zero Knowlege -- Alon Rosen[零知识介绍]
  12. 赛效:怎么用改图宝给图片添加水印?
  13. ftp远程登陆常用命令
  14. C++字符串转16进制
  15. 软件工程-第一次结对编程
  16. 2019年区块链技术领域的三个主要冲突
  17. Java使用POI获取Excel公式
  18. Development Tools 错误解决
  19. 【Java学习笔记】工厂模式
  20. html网页自动铺满屏幕,HTML+CSS入门 高度如何铺满全屏

热门文章

  1. 《OpenGL编程指南》一第3章 OpenGL绘制方式
  2. CentOS 手工编译、手动编译安装 MongoDB
  3. Oracle job自动任务实用指南
  4. Linux系统启动流程图
  5. 2018腾讯内部转岗面试题3——找出数组中比左边大比右边的小的元素
  6. 数据迁移期间进程命令
  7. windows mysql导入sql文件
  8. [Swift]LeetCode289. 生命游戏 | Game of Life
  9. 删除排序数组中的重复数字 II · Remove Duplicates from Sorted Array II
  10. java笔试之计算n x m的棋盘格子