ID设计

在分布式系统中,经常需要使用全局唯一ID查找对应的数据。产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对较少的空间。

全局唯一ID在数据库中一般会被设成主键,这样为了保证数据插入时索引的快速建立,还需要保持一个有序的趋势。

这样全局唯一ID就需要保证这两个需求:

  • 全局唯一
  • 趋势有序

全局ID产生的几种方式

数据库自增

当服务使用的数据库只有单库单表时,可以利用数据库的auto_increment来生成全局唯一递增ID.

优势:

  • 简单,无需程序任何附加操作
  • 保持定长的增量
  • 在单表中能保持唯一性

劣势:

  • 高并发下性能不佳,主键产生的性能上限是数据库服务器单机的上限。
  • 水平扩展困难,在分布式数据库环境下,无法保证唯一性。

UUID

一般的语言中会自带UUID的实现,比如Java中UUID方式UUID.randomUUID().toString(),可以通过服务程序本地产生,ID的生成不依赖数据库的实现。

优势:

  • 本地生成ID,不需要进行远程调用。
  • 全局唯一不重复。
  • 水平扩展能力非常好。

劣势:

  • ID有128 bits,占用的空间较大,需要存成字符串类型,索引效率极低。
  • 生成的ID中没有带Timestamp,无法保证趋势递增

Twitter Snowflake

snowflake是twitter开源的分布式ID生成算法,其核心思想是:产生一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成1000*(2^12)个,也就是大约400W的ID,完全能满足业务的需求。

根据snowflake算法的思想,我们可以根据自己的业务场景,产生自己的全局唯一ID。因为Java中long类型的长度是64bits,所以我们设计的ID需要控制在64bits。

比如我们设计的ID包含以下信息:

  1. | 41 bits: Timestamp | 3 bits: 区域 | 10 bits: 机器编号 | 10 bits: 序列号 |

产生唯一ID的Java代码:

  1. import java.security.SecureRandom;
  2. /**
  3. * 自定义 ID 生成器
  4. * ID 生成规则: ID长达 64 bits
  5. *
  6. * | 41 bits: Timestamp (毫秒) | 3 bits: 区域(机房) | 10 bits: 机器编号 | 10 bits: 序列号 |
  7. */
  8. public class CustomUUID {
  9. // 基准时间
  10. private long twepoch = 1288834974657L; //Thu, 04 Nov 2010 01:42:54 GMT
  11. // 区域标志位数
  12. private final static long regionIdBits = 3L;
  13. // 机器标识位数
  14. private final static long workerIdBits = 10L;
  15. // 序列号识位数
  16. private final static long sequenceBits = 10L;
  17. // 区域标志ID最大值
  18. private final static long maxRegionId = -1L ^ (-1L << regionIdBits);
  19. // 机器ID最大值
  20. private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
  21. // 序列号ID最大值
  22. private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
  23. // 机器ID偏左移10位
  24. private final static long workerIdShift = sequenceBits;
  25. // 业务ID偏左移20位
  26. private final static long regionIdShift = sequenceBits + workerIdBits;
  27. // 时间毫秒左移23位
  28. private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits;
  29. private static long lastTimestamp = -1L;
  30. private long sequence = 0L;
  31. private final long workerId;
  32. private final long regionId;
  33. public CustomUUID(long workerId, long regionId) {
  34. // 如果超出范围就抛出异常
  35. if (workerId > maxWorkerId || workerId < 0) {
  36. throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
  37. }
  38. if (regionId > maxRegionId || regionId < 0) {
  39. throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
  40. }
  41. this.workerId = workerId;
  42. this.regionId = regionId;
  43. }
  44. public CustomUUID(long workerId) {
  45. // 如果超出范围就抛出异常
  46. if (workerId > maxWorkerId || workerId < 0) {
  47. throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
  48. }
  49. this.workerId = workerId;
  50. this.regionId = 0;
  51. }
  52. public long generate() {
  53. return this.nextId(false, 0);
  54. }
  55. /**
  56. * 实际产生代码的
  57. *
  58. * @param isPadding
  59. * @param busId
  60. * @return
  61. */
  62. private synchronized long nextId(boolean isPadding, long busId) {
  63. long timestamp = timeGen();
  64. long paddingnum = regionId;
  65. if (isPadding) {
  66. paddingnum = busId;
  67. }
  68. if (timestamp < lastTimestamp) {
  69. try {
  70. throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. }
  74. }
  75. //如果上次生成时间和当前时间相同,在同一毫秒内
  76. if (lastTimestamp == timestamp) {
  77. //sequence自增,因为sequence只有10bit,所以和sequenceMask相与一下,去掉高位
  78. sequence = (sequence + 1) & sequenceMask;
  79. //判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0
  80. if (sequence == 0) {
  81. //自旋等待到下一毫秒
  82. timestamp = tailNextMillis(lastTimestamp);
  83. }
  84. } else {
  85. // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,
  86. // 为了保证尾数随机性更大一些,最后一位设置一个随机数
  87. sequence = new SecureRandom().nextInt(10);
  88. }
  89. lastTimestamp = timestamp;
  90. return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence;
  91. }
  92. // 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势.
  93. private long tailNextMillis(final long lastTimestamp) {
  94. long timestamp = this.timeGen();
  95. while (timestamp <= lastTimestamp) {
  96. timestamp = this.timeGen();
  97. }
  98. return timestamp;
  99. }
  100. // 获取当前的时间戳
  101. protected long timeGen() {
  102. return System.currentTimeMillis();
  103. }
  104. }

使用自定义的这种方法需要注意的几点:

  • 为了保持增长的趋势,要避免有些服务器的时间早,有些服务器的时间晚,需要控制好所有服务器的时间,而且要避免NTP时间服务器回拨服务器的时间。
  • 在跨毫秒时,序列号总是归0,会使得序列号为0的ID比较多,导致生成的ID取模后不均匀,所以序列号不是每次都归0,而是归一个0到9的随机数。
  • 使用这个CustomUUID类,最好在一个系统中能保持单例模式运行。

ID设计的更多相关文章

  1. 全局唯一ID设计

    在分布式系统中,经常需要使用全局唯一ID查找对应的数据.产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对较少的空间. 全局唯一ID在数据库中一般会被设成主键,这样为了保证数据插入时索引的快速 ...

  2. 从数据库、页面加载速度角度思考 id设计 sku asin

    (已对数据进行字符串替换,去身份识别.隐私跟踪) 12-13-14-15-16-18岁20女孩夏装初中高中学生韩版上衣服短袖T恤衫-tmall.com天猫 https://detail.tmall.c ...

  3. 分布式ID生成器(CosId)的设计与实现

    分布式ID生成器(CosId)设计与实现 CosId 简介 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了俩类 ID 生成器: SnowflakeId : 单机 TPS 性 ...

  4. 一种简易但设计全面的ID生成器思考

    分布式系统中,全局唯一 ID 的生成是一个老生常谈但是非常重要的话题.随着技术的不断成熟,大家的分布式全局唯一 ID 设计与生成方案趋向于趋势递增的 ID,这篇文章将结合我们系统中的 ID 针对实际业 ...

  5. web开发架构设计

    2015-07-31 13:10:38 一, web服务器 .负载均衡 .不做对URL的rewrite逻辑判断, 全部转发到代码服务器的单一入口文件, 由代码去全权处理 二, 代码服务器(常跟web服 ...

  6. ID

    id 编辑 身份标识号.账号.唯一编码.专属号码.工业设计.国家简称.法律词汇.通用账户.译码器.软件公司等,各类专有词汇缩写. 身份证,身份识别,是一种身份证明. 中文名 身份证,帐号,工业设计,通 ...

  7. ID生成器详解

    概述 ID 生成器也叫发号器,它的主要目的就是"为一个分布式系统的数据object产生一个唯一的标识",但其实在一个真实的系统里可能也可以承担更多的作用.概括起来主要有以下几点: ...

  8. Hbase对hive的支持没有hdfs的好的原因 及hbase什么时候使用 及rowkey设计技巧

    hive-=mareduce 的  split  在 hbase就是  region了,,,,,,,访问region必须通过hregionserver 会造成regionser负担过大, 另外 reg ...

  9. UI、ID、UE和GUI,这些都是什么

    在网页和应用设计领域中,我们经常会听到人们用这些英文缩写描述设计工作,那么它们各自代表什么含义?具体的工作.方向和区别是什么呢? 下面让我们先来看看它们的英文全称和基本概念. UI(User Inte ...

随机推荐

  1. Python金融应用编程(数据分析、定价与量化投资)

    近年来,金融领域的量化分析越来越受到理论界与实务界的重视,量化分析的技术也取得了较大的进展,成为备受关注的一个热点领域.所谓金融量化,就是将金融分析理论与计算机编程技术相结合,更为有效的利用现代计算技 ...

  2. 基于visual Studio2013解决面试题之0305广度优先搜索二叉树

     题目

  3. Oracle静态监听与动态监听概念全解析

    基于11g,linux5.5做出的测试,单实例数据库做出的测试. 1.注册 Instance到监听器去注册自己的Instance_name与ORACLE_HOME,还可以选择添加global_dbna ...

  4. Eclipse扩展点

    ~~ org.eclipse.ui.actionSets(IWorkbenchWindowActionDelegate)||  org.eclipse.ui.commands 这两个扩展点都是对菜单进 ...

  5. Ajax 下拉列表联动显示

    一般处理程序文件 代码 using System;using System.Web;using System.Linq;using System.Data.Linq;using System.Text ...

  6. 96 Stocks APIs: Bloomberg, NASDAQ and E*TRADE

      Our API directory now includes 96 stocks APIs. The newest is the Eurex VALUES API. The most popula ...

  7. SQLite/嵌入式数据库

    SQLite/嵌入式数据库 的项目要么不使用数据库(一两个文配置文件就可以搞定),要么就会有很多的数据,用到 postgresql,操练sqlite的还没有.现在我有个自己的小测试例子,写个数据库对比 ...

  8. jquery学习之AJAX

    1,关于AJAX的简单介绍 AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). AJAX 不是新的编程语言,而是一种使用现有标准 ...

  9. 【总结】在VirtualBox上面安装Mac的注意事项

    看此文之前 http://www.crifan.com/category/work_and_job/virtual_machine/virtualbox-virtual_machine/ 此文仅仅是针 ...

  10. JAVA泛型之<? extends T>:(通配符上限)和<? super T>(通配符下限)

    一.通配符上限和通配符下限接受的类型 通配符上限:<? extends T> 通配符下限:<? super T> 以下代码是测试结果,注释为解释说明 package xayd. ...