1.snowflake简介

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

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

Snowflake是我见过生成唯一主键id最快的方法,它是生成的是一个64位的数字,其中42位时间戳,接下来10位是自定义的数,其作用就是区分集群中的所有机器,最后12位是毫秒内序列,集群内每个机器能够在1毫秒内生成2^12 - 1个ID

2.snowflake算法原理

snowflake生产的ID二进制结构表示如下(每部分用-分开):

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+,效率之高由此可见。下面是一个demo,通过测试可以直接使用。

1 packagecom.test;2

3 importjava.util.ArrayList;4 importjava.util.List;5

6 public classSnowflakeIdWorker {7

8 //==============================Fields===========================================

9 /**开始时间截 (2015-01-01)*/

10 private final long twepoch = 1420041600000L;11

12 /**机器id所占的位数*/

13 private final long workerIdBits = 5L;14

15 /**数据标识id所占的位数*/

16 private final long datacenterIdBits = 5L;17

18 /**支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/

19 private final long maxWorkerId = -1L ^ (-1L <

21 /**支持的最大数据标识id,结果是31*/

22 private final long maxDatacenterId = -1L ^ (-1L <

24 /**序列在id中占的位数*/

25 private final long sequenceBits = 12L;26

27 /**机器ID向左移12位*/

28 private final long workerIdShift =sequenceBits;29

30 /**数据标识id向左移17位(12+5)*/

31 private final long datacenterIdShift = sequenceBits +workerIdBits;32

33 /**时间截向左移22位(5+5+12)*/

34 private final long timestampLeftShift = sequenceBits + workerIdBits +datacenterIdBits;35

36 /**生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)*/

37 private final long sequenceMask = -1L ^ (-1L <

39 /**工作机器ID(0~31)*/

40 private longworkerId;41

42 /**数据中心ID(0~31)*/

43 private longdatacenterId;44

45 /**毫秒内序列(0~4095)*/

46 private long sequence = 0L;47

48 /**上次生成ID的时间截*/

49 private long lastTimestamp = -1L;50

51 //==============================Constructors=====================================

52 /**

53 * 构造函数54 *@paramworkerId 工作ID (0~31)55 *@paramdatacenterId 数据中心ID (0~31)56 */

57 public SnowflakeIdWorker(long workerId, longdatacenterId) {58 if (workerId > maxWorkerId || workerId < 0) {59 throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));60 }61 if (datacenterId > maxDatacenterId || datacenterId < 0) {62 throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));63 }64 this.workerId =workerId;65 this.datacenterId =datacenterId;66 }67

68 //==============================Methods==========================================

69 /**

70 * 获得下一个ID (该方法是线程安全的)71 *@returnSnowflakeId72 */

73 public synchronized longnextId() {74 long timestamp =timeGen();75

76 //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常

77 if (timestamp

82 //如果是同一时间生成的,则进行毫秒内序列

83 if (lastTimestamp ==timestamp) {84 sequence = (sequence + 1) &sequenceMask;85 //毫秒内序列溢出

86 if (sequence == 0) {87 //阻塞到下一个毫秒,获得新的时间戳

88 timestamp =tilNextMillis(lastTimestamp);89 }90 }91 //时间戳改变,毫秒内序列重置

92 else{93 sequence = 0L;94 }95

96 //上次生成ID的时间截

97 lastTimestamp =timestamp;98

99 //移位并通过或运算拼到一起组成64位的ID

100 return ((timestamp - twepoch) << timestampLeftShift) //101 | (datacenterId << datacenterIdShift) //102 | (workerId << workerIdShift) //103 |sequence;104 }105

106 /**

107 * 阻塞到下一个毫秒,直到获得新的时间戳108 *@paramlastTimestamp 上次生成ID的时间截109 *@return当前时间戳110 */

111 protected long tilNextMillis(longlastTimestamp) {112 long timestamp =timeGen();113 while (timestamp <=lastTimestamp) {114 timestamp =timeGen();115 }116 returntimestamp;117 }118

119 /**

120 * 返回以毫秒为单位的当前时间121 *@return当前时间(毫秒)122 */

123 protected longtimeGen() {124 returnSystem.currentTimeMillis();125 }126

127 //==============================Test=============================================

128 /**测试*/

129 public static voidmain(String[] args) {130 SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);131 List list = new ArrayList();132 for (int i = 0; i < 100000; i++) {133 long id =idWorker.nextId();134 136 String string =newString().valueOf(id);137 list.add(string);138 }139 String str1 ="";140 String str2 ="";141 for(int j=0;j<100000;j++){142 str1=list.get(j);143 for(int n =0;n<100000;n++){144 str2=list.get(n);145 if(j==n){146 break;147 }148 if(str1.equals(str2)){149 System.out.println("存在相等的id "+str1+"-------> " +str2);150 }151 }152 }153 System.out.println("测试成功");154 }155 }

snowflake做主键 自增_snowflake主键生成策略相关推荐

  1. postgre 没有主键自增ma_PostgreSQL 主键自增解决方案

    因为工作需要,接触到了PostgreSQL,遇到了主键自增的情况 8.1.4. Serial Types 数据类型serial和bigserial不是真正的类型,而只是用于设置唯一标识符列的符号方便( ...

  2. MySQL 使用自增ID主键和UUID 作为主键的优劣比較具体过程(从百万到千万表记录測试)...

    測试缘由 一个开发同事做了一个框架.里面主键是uuid.我跟他建议说mysql不要用uuid用自增主键,自增主键效率高,他说不一定高,我说innodb的索引特性导致了自增id做主键是效率最好的,为了拿 ...

  3. MySQL 使用自增ID主键和UUID 作为主键的优劣比较详细过程(从百万到千万表记录测试)...

    Reference: https://blog.csdn.net/mchdba/article/details/52336203 一个开发同事做了一个框架,里面主键是uuid,我跟他建议说mysql不 ...

  4. Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1

    记录一次傻逼的问题, 自己把自己蠢哭:Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1 错误说明: 返回的1是影响的行数,并不是自增的主键id: 想要获取自增主键id,需要 ...

  5. 做工作流时候 Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1

    Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1 错误说明: 返回的1是影响的行数,并不是自增的主键id: 想要获取自增主键id,需要通过xx.getId()方法获取,因为 ...

  6. 【MyBatis笔记】6 - 特殊SQL的执行:不能使用#{}的场景、获取自增的主键

    文章目录 1.模糊查询 2.批量删除 3.动态设置表名 4.添加功能获取自增的主键 视频链接:https://www.bilibili.com/video/BV1VP4y1c7j7?p=37& ...

  7. MySQL设置自增的主键

    1.如何设置自增的主键 很简单,只需要在主键后面添加AUTO_INCREMENT关键字就行了 CREATE TABLE `user`(id INT PRIMARY KEY AUTO_INCREMENT ...

  8. mysql 主键 聚集索引_MySQL主键索引和聚焦索引

    主键索引 主键索引,简称主键,原文是PRIMARY KEY,由一个或多个列组成,用于唯一性标识数据表中的某一条记录.一个表可以没有主键,但最多只能有一个主键,并且主键值不能包含NULL. 在MySQL ...

  9. mysql innodb 主键,Mysql InnoDB 引擎 主键性能

    前些天看到网上有人说:Mysql InnoDB 引擎 主键不适合用UUID , 若要用UUID的话可考虑用 自增ID做物理主键,UUID做逻辑主键. 带着以上问题,本人做了如下测试: 先自报测试环境: ...

最新文章

  1. spring第五讲:aop
  2. CenOS下安装Eclipse并配置PyDev
  3. java imapi_读写DVD / CD-Java
  4. 史上最硬核的rpm和dpkg依赖问题解决方案
  5. 架构师必备最全SQL优化方案
  6. Win7虚拟Wifi热点(设置后能连接wifi但无法上网的可按后面提示操作)
  7. FlashDevelop4 快捷键
  8. bat 执行php文件
  9. 窗函数(matlab)
  10. 图的应用--最短路算法
  11. 黑马程序员-黑马,人生的一个重要转折
  12. 慧都与数据库厂商Devart进一步提升合作层次
  13. CSS实现抽奖大转盘
  14. 截取某个字符串之前的字符
  15. 【JavaSE】JavaSE之控制逻辑
  16. 读--《谁动了我的奶酪》
  17. mathcad入门一
  18. python字符串驻留机制_python字符串驻留(intern)机制
  19. android动画不占cpu如何实现,【实战总结】帧动画调优实践
  20. 关于与kafka的爱恨交织

热门文章

  1. 深入dos编程_计算机编程能给孩子带来什么?
  2. vue动态在页面添加背景图片
  3. Java并发13:并发三特性-原子性定义、原子性问题与原子性保证技术
  4. mac修改chrome的刷新及开发者工具等快捷键
  5. HMACSHA256加密数据
  6. 产品经理知识体系:1.什么是互联网思维?
  7. 云服务器下行_关于云服务器的选择
  8. (PHP开发)thinkphp5 换网站图标icon无法显示问题
  9. NLP学习D2-TF2基础学习-北大教程
  10. 用Python语言实现共享单车的数据分析