• Twitter的分布式自增ID算法snowflake (Java版)


    概述

    分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。

    有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。

    而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

    结构

    snowflake的结构如下(每部分用-分开):

    0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

    第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

    一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

    snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

    源码

    (JAVA版本的源码)

      1 package com.xazhxc.htjcom.back.controller.base;
      2 
      3 import lombok.extern.slf4j.Slf4j;
      4 
      5 /**
      6  * 原作者 zzxadi https://github.com/zzxadi/Snowflake-IdWorker
      7  * @author Exrickx
      8  */
      9 @Slf4j
     10 public class SnowFlakeUtil {
     11 
     12     private final long id;
     13     /**
     14      * 时间起始标记点,作为基准,一般取系统的最近时间
     15      */
     16     private final long epoch = 1524291141010L;
     17     /**
     18      * 机器标识位数
     19      */
     20     private final long workerIdBits = 10L;
     21     /**
     22      * 机器ID最大值: 1023
     23      */
     24     private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;
     25     /**
     26      * 0,并发控制
     27      */
     28     private long sequence = 0L;
     29     /**
     30      * 毫秒内自增位
     31      */
     32     private final long sequenceBits = 12L;
     33 
     34     /**
     35      * 12
     36      */
     37     private final long workerIdShift = this.sequenceBits;
     38     /**
     39      * 22
     40      */
     41     private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;
     42     /**
     43      * 4095,111111111111,12位
     44      */
     45     private final long sequenceMask = -1L ^ -1L << this.sequenceBits;
     46     private long lastTimestamp = -1L;
     47 
     48     private SnowFlakeUtil(long id) {
     49         if (id > this.maxWorkerId || id < 0) {
     50             throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", this.maxWorkerId));
     51         }
     52         this.id = id;
     53     }
     54 
     55     public synchronized long nextId() {
     56         long timestamp = timeGen();
     57         if (this.lastTimestamp == timestamp) {
     58             //如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环); 对新的timestamp,sequence从0开始
     59             this.sequence = this.sequence + 1 & this.sequenceMask;
     60             if (this.sequence == 0) {
     61                 // 重新生成timestamp
     62                 timestamp = this.tilNextMillis(this.lastTimestamp);
     63             }
     64         } else {
     65             this.sequence = 0;
     66         }
     67 
     68         if (timestamp < this.lastTimestamp) {
     69             log.error(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
     70             return -1;
     71         }
     72 
     73         this.lastTimestamp = timestamp;
     74         return timestamp - this.epoch << this.timestampLeftShift | this.id << this.workerIdShift | this.sequence;
     75     }
     76 
     77     private static SnowFlakeUtil flowIdWorker = new SnowFlakeUtil(1);
     78     public static SnowFlakeUtil getFlowIdInstance() {
     79         return flowIdWorker;
     80     }
     81 
     82     /**
     83      * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
     84      */
     85     private long tilNextMillis(long lastTimestamp) {
     86         long timestamp = timeGen();
     87         while (timestamp <= lastTimestamp) {
     88             timestamp = timeGen();
     89         }
     90         return timestamp;
     91     }
     92 
     93     /**
     94      * 获得系统当前毫秒数
     95      */
     96     private static long timeGen() {
     97         return System.currentTimeMillis();
     98     }
     99 
    100     public static void main(String[] args) {
    101         for(int i=0;i<100;i++){
    102             SnowFlakeUtil snowFlakeUtil = SnowFlakeUtil.getFlowIdInstance();
    103             System.out.println(snowFlakeUtil.nextId());
    104         }
    105     }
    106 }
  • 相关阅读:
    redis常用数据类型与命令
    bcb6重启应用程序
    MySQL 关联查询  外连接 { LEFT| RIGHT } JOIN
    MySQL 关联查询 内连接
    MySql子查询
    MySql单表查询
    表级操作语句
    库级操作语句
    14.正则表达式、re模块、元字符
    13.生成器、迭代器、 模块、包和包管理
  • 原文地址:https://www.cnblogs.com/joyny/p/10142986.html
Copyright © 2020-2023  润新知