雪花算法(Twitter_Snowflake)

我们知道,分布式全局唯一id的生成,一般是以下几种:

  1. 基于雪花算法生成
  2. 基于数据库
  3. 基于redis
  4. 基于zookeeper

本文说下雪花算法,后面附源码以及测试代码。

如下图:

如上图:雪花算法生成的id,总共64位
第一位作为保留位,默认0
中间41位用来存放时间戳,是当前时间与初始时间的差值,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
后10位是机器id,可满足同时(1L << 10) = 1024个机器同时生成id
最后12位作为随机序列,每个机器,每毫秒可生成(1L << 12) = 4096个不同的值

改进:

其实雪花算法就是把id按位打散,然后再分成上面这几块,用位来表示状态,这其实就是一种思想。
所以咱们实际在用的时候,也不必非得按照上面这种分割,只需保证总位数在64位即可

  1. 如果你的业务不需要69年这么长,或者需要更长时间
    用42位存储时间戳,(1L << 42) / (1000L * 60 * 60 * 24 * 365) = 139年
    用41位存储时间戳,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
    用40位存储时间戳,(1L << 40) / (1000L * 60 * 60 * 24 * 365) = 34年
    用39位存储时间戳,(1L << 39) / (1000L * 60 * 60 * 24 * 365) = 17年
    用38位存储时间戳,(1L << 38) / (1000L * 60 * 60 * 24 * 365) = 8年
    用37位存储时间戳,(1L << 37) / (1000L * 60 * 60 * 24 * 365) = 4年

  2. 如果你的机器没有那么1024个这么多,或者比1024还多
    用7位存储机器id,(1L << 7) = 128
    用8位存储机器id,(1L << 8) = 256
    用9位存储机器id,(1L << 9) = 512
    用10位存储机器id,(1L << 10) = 1024
    用11位存储机器id,(1L << 11) = 2048
    用12位存储机器id,(1L << 12) = 4096
    用13位存储机器id,(1L << 13) = 8192

  3. 如果你的业务,每个机器,每毫秒最多也不会4096个id要生成,或者比这个还多
    用8位存储随机序列,(1L << 8) = 256
    用9位存储随机序列,(1L << 9) = 512
    用10位存储随机序列,(1L << 10) = 1024
    用11位存储随机序列,(1L << 11) = 2048
    用12位存储随机序列,(1L << 12) = 4096
    用13位存储随机序列,(1L << 13) = 8192
    用14位存储随机序列,(1L << 14) = 16384
    用15位存储随机序列,(1L << 15) = 32768
    注意,随机序列建议不要太大,一般业务,每毫秒要是能产生这么多id,建议在机器id上增加位

  4. 如果你的业务量很小,比如一般情况下每毫秒生成不到1个id,此时可以将随机序列设置成随机开始自增
    比如从0到48随机开始自增,算是一种优化建议

  5. 如果你有多个业务,也可以拿出来几位来表示业务,比如用最后4位,支持16种业务的区分

  6. 如果你的业务特别复杂,可以考虑128位存储,不过这样的话,也可以考虑使用uuid了,但uuid无序,这个有序

  7. 如果你的业务很简单,甚至可以考虑32位存储,时间戳改成秒为单位…

总结:

  1. 合理的根据自己的实际情况去设计各个唯一条件的组合,雪花算法只是提供了一种相对合理的方式。
  2. 雪花算法这种用位来表示状态的,我们还可以用在其他方面,比如数据库存储,可以用更小的空间去表示不同的状态位
    包括各种底层的比如序列化,也是有用到拆解位,充分利用存储

源码:

package com.zs.common;/*** 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) {long start = System.currentTimeMillis();SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 3);for (int i = 0; i < 50; i++) {long id = idWorker.nextId();System.out.println(Long.toBinaryString(id));System.out.println(id);}long end = System.currentTimeMillis();System.out.println(end - start);}
}

[分布式] ------ 全局唯一id生成之雪花算法(Twitter_Snowflake)相关推荐

  1. 全局唯一id生成之雪花算法

    雪花算法(Twitter_Snowflake) 我们知道,分布式全局唯一id的生成,一般是以下几种: 基于雪花算法生成 基于数据库 基于redis 基于zookeeper 本文说下雪花算法,后面附源码 ...

  2. 微服务架构分布式全局唯一ID生成策略及算法

    全局唯一的 ID 几乎是所有系统都会遇到的刚需.这个 id 在搜索, 存储数据, 加快检索速度 等等很多方面都有着重要的意义.工业上有多种策略来获取这个全局唯一的id,针对常见的几种场景,我在这里进行 ...

  3. SpringBoot实战:设备唯一ID生成【雪花算法、分布式应用】

    目录 SpringBoot实战:设备唯一ID生成[雪花算法.分布式应用] 背景: snowflake(雪花算法)方案: 实现: 雪花算法生成ID: 二维码打包: 多线程优化-批量插入: 二维码识别+扫 ...

  4. java 唯一id生成算法_分布式全局唯一ID生成方案之snowflake算法

    已有的方案: 可大致分为: 完全依赖关系/非关系型数据库递增的方案 完全不依赖数据源作为生成因子的UUID 半依赖数据源作为生成因子的snowflake 为什么推荐snowflake? 这个问题,可以 ...

  5. 分布式全局唯一ID生成算法(改进的雪花算法——解决时钟回拨问题)

    改进的雪花算法--解决时钟回拨问题 原创 公众号: 软件设计活跃区 改进的雪花算法--姑且称为梨花算法吧(忽如一夜春风来,千树万树梨花开). 改进目标:解决雪花算法的时钟回拨问题:部分避免机器id重复 ...

  6. snowflake算法 php,Snowflake —— 分布式全局唯一 id 生成算法

    简介 Snowflake 是 Twitter 提出一种的分布式唯一序列号生成算法,理论上单节点 1 毫秒可以生成 4096 个(每秒四百万个)唯一序列,这个序列是个 long 类型的数字,在数据库中的 ...

  7. 分布式全局唯一ID的实现

    分布式全局唯一ID的实现 前言 上周末考完试,这周正好把工作整理整理,然后也把之前的一些素材,整理一番,也当自己再学习一番. 一方面正好最近看到几篇这方面的文章,另一方面也是正好工作上有所涉及,所以决 ...

  8. c#随机数生成编号_忘掉 Snowflake,感受一下性能高出587倍的全局唯一ID生成算法...

    今天我们来拆解 Snowflake 算法,同时领略百度.美团.腾讯等大厂在全局唯一 ID 服务方面做的设计,接着根据具体需求设计一款全新的全局唯一 ID 生成算法.这还不够,我们会讨论到全局唯一 ID ...

  9. 微信用户全局唯一标识_忘掉 Snowflake,感受一下性能高出587倍的全局唯一ID生成算法...

    今天我们来拆解 Snowflake 算法,同时领略百度.美团.腾讯等大厂在全局唯一 ID 服务方面做的设计,接着根据具体需求设计一款全新的全局唯一 ID 生成算法.这还不够,我们会讨论到全局唯一 ID ...

最新文章

  1. SQL中的Where,Group By,Order By和Having的用法/区别
  2. linux网络编程--select/poll/epoll 详解
  3. 配置 mybatis的 log4j.properties
  4. 在云服务器上注意GeoServer和ShadowDataMap的跨域设置
  5. 关于datagrid
  6. 关于计应151/152《软件工程》课程实践的安排
  7. Vijos OJ搭建
  8. ApacheCN 翻译/校对/笔记整理活动进度公告 2019.9.13
  9. WIN11右键菜单默认展开
  10. java 调用 yed 绘制 流程图_让人一见倾心的流程图绘制软件yEd
  11. K2P设置为桥接路由器教程
  12. 【计算机毕业设计】017学生公寓电费信息管理系统
  13. 仿蘑菇街界面应用(1)
  14. java获取法定节假日_java 获取n个工作日后的日期(包含法定节假日、双休日、节后补班)...
  15. C++历史背景 [C++开发实战](边学边练哈 (#^.^#)~)
  16. Linux_网络_数据链路层协议 MAC帧/ARP协议 (以太网通信原理,MAC地址与IP地址的区分,MTU对IP/TCP/IP的影响,ARP协议及其通信过程)
  17. 查违章老显示服务器维护中咋回事,违章查询怎么老显示系统正在维护呢,为什么违章查询总是显示维护状态?...
  18. LDPC码Gallager构造校验矩阵(MATLAB)
  19. 如何保护前端JS代码?前端js代码加密
  20. 从零入门云计算(1):云计算究竟是个啥?

热门文章

  1. 性能测试需求调研分析方法
  2. Catalan数总结
  3. redisb并发访问慢出现的问题
  4. 熟悉 ASP.NET MVC 类
  5. burp的intruder报错Payload set 1: Invalid number settings
  6. Leetcode--162. 寻找峰值
  7. Leetcode--135. 发糖果
  8. Leetcode--7. 整数反转
  9. 惠普打印机怎么无线连接电脑_惠普打印机连不上无线?怎么解!
  10. python 获取 字典中的指定键_python中字典方法的详细教程