1、基本构成

image.png

  1. 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

1.1 符号位

1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

1.2 41位时间戳(毫秒级)

41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。

1.3 10位的数据机器位

10位的数据机器位,可以部署在1024个分布式节点上,包括5位datacenterId(机房)和5位workerId(机器节点)。

1.4 12位序列

12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号。

  1. 1+41+10+12 = 64

2.代码实现

2.1 基本代码

  1. public class SnowFlake {
  2. // 数据中心(机房) id
  3. private long datacenterId;
  4. // 机器ID
  5. private long workerId;
  6. // 同一时间的序列
  7. private long sequence;
  8. public SnowFlake(long workerId, long datacenterId) {
  9. this(workerId, datacenterId, 0);
  10. }
  11. public SnowFlake(long workerId, long datacenterId, long sequence) {
  12. // 合法判断
  13. if (workerId > maxWorkerId || workerId < 0) {
  14. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  15. }
  16. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  17. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  18. }
  19. System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
  20. timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
  21. this.workerId = workerId;
  22. this.datacenterId = datacenterId;
  23. this.sequence = sequence;
  24. }
  25. // 开始时间戳(2021-10-16 22:03:32)
  26. private long twepoch = 1634393012000L;
  27. // 机房号,的ID所占的位数 5个bit 最大:11111(2进制)--> 31(10进制)
  28. private long datacenterIdBits = 5L;
  29. // 机器ID所占的位数 5个bit 最大:11111(2进制)--> 31(10进制)
  30. private long workerIdBits = 5L;
  31. // 5 bit最多只能有31个数字,就是说机器id最多只能是32以内
  32. private long maxWorkerId = -1L ^ (-1L << workerIdBits);
  33. // 5 bit最多只能有31个数字,机房id最多只能是32以内
  34. private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  35. // 同一时间的序列所占的位数 12个bit 111111111111 = 4095 最多就是同一毫秒生成4096个
  36. private long sequenceBits = 12L;
  37. // workerId的偏移量
  38. private long workerIdShift = sequenceBits;
  39. // datacenterId的偏移量
  40. private long datacenterIdShift = sequenceBits + workerIdBits;
  41. // timestampLeft的偏移量
  42. private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  43. // 序列号掩码 4095 (0b111111111111=0xfff=4095)
  44. // 用于序号的与运算,保证序号最大值在0-4095之间
  45. private long sequenceMask = -1L ^ (-1L << sequenceBits);
  46. // 最近一次时间戳
  47. private long lastTimestamp = -1L;
  48. // 获取机器ID
  49. public long getWorkerId() {
  50. return workerId;
  51. }
  52. // 获取机房ID
  53. public long getDatacenterId() {
  54. return datacenterId;
  55. }
  56. // 获取最新一次获取的时间戳
  57. public long getLastTimestamp() {
  58. return lastTimestamp;
  59. }
  60. // 获取下一个随机的ID
  61. public synchronized long nextId() {
  62. // 获取当前时间戳,单位毫秒
  63. long timestamp = timeGen();
  64. if (timestamp < lastTimestamp) {
  65. System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
  66. throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
  67. lastTimestamp - timestamp));
  68. }
  69. // 去重
  70. if (lastTimestamp == timestamp) {
  71. sequence = (sequence + 1) & sequenceMask;
  72. // sequence序列大于4095
  73. if (sequence == 0) {
  74. // 调用到下一个时间戳的方法
  75. timestamp = tilNextMillis(lastTimestamp);
  76. }
  77. } else {
  78. // 如果是当前时间的第一次获取,那么就置为0
  79. sequence = 0;
  80. }
  81. // 记录上一次的时间戳
  82. lastTimestamp = timestamp;
  83. // 偏移计算
  84. return ((timestamp - twepoch) << timestampLeftShift) |
  85. (datacenterId << datacenterIdShift) |
  86. (workerId << workerIdShift) |
  87. sequence;
  88. }
  89. private long tilNextMillis(long lastTimestamp) {
  90. // 获取最新时间戳
  91. long timestamp = timeGen();
  92. // 如果发现最新的时间戳小于或者等于序列号已经超4095的那个时间戳
  93. while (timestamp <= lastTimestamp) {
  94. // 不符合则继续
  95. timestamp = timeGen();
  96. }
  97. return timestamp;
  98. }
  99. private long timeGen() {
  100. return System.currentTimeMillis();
  101. }
  102. public static void main(String[] args) {
  103. SnowFlake worker = new SnowFlake(1, 1);
  104. long timer = System.currentTimeMillis();
  105. for (int i = 0; i < 10000; i++) {
  106. worker.nextId();
  107. }
  108. System.out.println(System.currentTimeMillis());
  109. System.out.println(System.currentTimeMillis() - timer);
  110. }
  111. }

2.2 成员变量

属性 含义 默认值
twepoch 开始时间截 项目起始点
workerIdBits 机器id所占的位
datacenterIdBits 数据标识id所占的位数
maxWorkerId 支持的最大机器id,结果是31 (-1^(-1<<5))=31
maxDatacenterId 支持的最大数据标识id,结果是31 (-1^(-1<<5))=31
sequenceBits 序列在id中占的位数 12
workerIdShift 机器ID向左移12位 12
datacenterIdShift 数据标识id向左移17位 17
timestampLeftShift 时间截向左移22位(5+5+12) 22
sequenceMask 生成序列的掩码,这里为4095 (-1^(-1<<12))=4095
workerId 工作机器ID(0~31) (-1^(-1<<5))=31
datacenterId 数据中心ID(0~31) (-1^(-1<<5))=31
sequence 毫秒内序列(0~4095) 12
lastTimestamp 上次生成ID的时间截

引用

https://www.cnblogs.com/clawhub/p/11991891.html#scroller-3