Java实现雪花算法(snowflake)-生成永不重复的ID(源代码+工具类)使用案例

x33g5p2x  于2021-09-19 转载在 Java  
字(4.2k)|赞(0)|评价(0)|浏览(673)

雪花算法是由Twitter公司开源的snowflake(雪花)算法。

1、雪花算法的原理

雪花算法会生成一个64位的二进制数据,为一个Long型。

(转换成字符串后长度最多19),其基本结构:

第一位:为未使用

第二部分:41位为毫秒级时间(41位的长度可以使用69年)

第三部分:5位datacenterld和5位workerld(10位的长度最多支持部署1024个节点)

第四部分:最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

snowflake生成的ID整体上按照时间自增排序,
并且整个分布式系统内不会产生ID碰撞(由datacenterworkerld作区分),
并且效率较高。

经测试snowflake每秒能够产生26万个ID。

2、工具类(雪花算法源代码)

  1. /** * <p>名称:IdWorker.java</p> * <p>描述:分布式自增长ID</p> * <pre> * Twitter的 Snowflake JAVA实现方案 * </pre> * 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用: * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间, * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识), * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。 * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。 * <p> * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加)) * * @author Polim */
  2. public class IdWorker {
  3. // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
  4. private final static long twepoch = 1288834974657L;
  5. // 机器标识位数
  6. private final static long workerIdBits = 5L;
  7. // 数据中心标识位数
  8. private final static long datacenterIdBits = 5L;
  9. // 机器ID最大值
  10. private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
  11. // 数据中心ID最大值
  12. private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  13. // 毫秒内自增位
  14. private final static long sequenceBits = 12L;
  15. // 机器ID偏左移12位
  16. private final static long workerIdShift = sequenceBits;
  17. // 数据中心ID左移17位
  18. private final static long datacenterIdShift = sequenceBits + workerIdBits;
  19. // 时间毫秒左移22位
  20. private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  21. private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
  22. /* 上次生产id时间戳 */
  23. private static long lastTimestamp = -1L;
  24. // 0,并发控制
  25. private long sequence = 0L;
  26. private final long workerId;
  27. // 数据标识id部分
  28. private final long datacenterId;
  29. public IdWorker(){
  30. this.datacenterId = getDatacenterId(maxDatacenterId);
  31. this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
  32. }
  33. /** * @param workerId * 工作机器ID * @param datacenterId * 序列号 */
  34. public IdWorker(long workerId, long datacenterId) {
  35. if (workerId > maxWorkerId || workerId < 0) {
  36. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  37. }
  38. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  39. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  40. }
  41. this.workerId = workerId;
  42. this.datacenterId = datacenterId;
  43. }
  44. /** * 获取下一个ID * * @return */
  45. public synchronized long nextId() {
  46. long timestamp = timeGen();
  47. if (timestamp < lastTimestamp) {
  48. throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
  49. }
  50. if (lastTimestamp == timestamp) {
  51. // 当前毫秒内,则+1
  52. sequence = (sequence + 1) & sequenceMask;
  53. if (sequence == 0) {
  54. // 当前毫秒内计数满了,则等待下一秒
  55. timestamp = tilNextMillis(lastTimestamp);
  56. }
  57. } else {
  58. sequence = 0L;
  59. }
  60. lastTimestamp = timestamp;
  61. // ID偏移组合生成最终的ID,并返回ID
  62. long nextId = ((timestamp - twepoch) << timestampLeftShift)
  63. | (datacenterId << datacenterIdShift)
  64. | (workerId << workerIdShift) | sequence;
  65. return nextId;
  66. }
  67. private long tilNextMillis(final long lastTimestamp) {
  68. long timestamp = this.timeGen();
  69. while (timestamp <= lastTimestamp) {
  70. timestamp = this.timeGen();
  71. }
  72. return timestamp;
  73. }
  74. private long timeGen() {
  75. return System.currentTimeMillis();
  76. }
  77. /** * <p> * 获取 maxWorkerId * </p> */
  78. protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
  79. StringBuffer mpid = new StringBuffer();
  80. mpid.append(datacenterId);
  81. String name = ManagementFactory.getRuntimeMXBean().getName();
  82. if (!name.isEmpty()) {
  83. /* * GET jvmPid */
  84. mpid.append(name.split("@")[0]);
  85. }
  86. /* * MAC + PID 的 hashcode 获取16个低位 */
  87. return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
  88. }
  89. /** * <p> * 数据标识id部分 * </p> */
  90. protected static long getDatacenterId(long maxDatacenterId) {
  91. long id = 0L;
  92. try {
  93. InetAddress ip = InetAddress.getLocalHost();
  94. NetworkInterface network = NetworkInterface.getByInetAddress(ip);
  95. if (network == null) {
  96. id = 1L;
  97. } else {
  98. byte[] mac = network.getHardwareAddress();
  99. id = ((0x000000FF & (long) mac[mac.length - 1])
  100. | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
  101. id = id % (maxDatacenterId + 1);
  102. }
  103. } catch (Exception e) {
  104. System.out.println(" getDatacenterId: " + e.getMessage());
  105. }
  106. return id;
  107. }
  108. }

3、使用案例

  1. public class OrderId {
  2. public static void main(String[] args) {
  3. IdWorker idWorker = new IdWorker(1, 1);
  4. long l = idWorker.nextId();
  5. System.out.println(l);
  6. }
  7. }

运行结果

相关文章

最新文章

更多