zoukankan      html  css  js  c++  java
  • 分布式Snowflake雪花算法

     前言

    项目中主键ID生成方式比较多,但是哪种方式更能提高的我们的工作效率、项目质量、代码实用性以及健壮性呢,下面作了一下比较,目前雪花算法的优点还是很明显的。

    优缺点比较

    • UUID(缺点:太长、没法排序、使数据库性能降低)
    • Redis(缺点:必须依赖Redis)
    • Oracle序列号(缺点:用Oracle才能使用)
    • Snowflake雪花算法,优点:生成有顺序的id,提高数据库的性能

    Snowflake雪花算法解析

     

    雪花算法解析 结构 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型。(转换成字符串长度为18)。

    Snowflake算法核心把时间戳,工作机器id,序列号组合在一起。

    整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),

    并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

    分布式Snowflake雪花算法代码

      1 public class SnowFlakeGenerator {
      2  
      3     public static class Factory {
      4         /**
      5          * 每一部分占用位数的默认值
      6          */
      7         private final static int DEFAULT_MACHINE_BIT_NUM = 5;   //机器标识占用的位数
      8         private final static int DEFAULT_IDC_BIT_NUM = 5;//数据中心占用的位数
      9  
     10         private int machineBitNum;
     11         private int idcBitNum;
     12  
     13         public Factory() {
     14             this.idcBitNum = DEFAULT_IDC_BIT_NUM;
     15             this.machineBitNum = DEFAULT_MACHINE_BIT_NUM;
     16         }
     17  
     18         public Factory(int machineBitNum, int idcBitNum) {
     19             this.idcBitNum = idcBitNum;
     20             this.machineBitNum = machineBitNum;
     21         }
     22  
     23         public SnowFlakeGenerator create(long idcId, long machineId) {
     24             return new SnowFlakeGenerator(this.idcBitNum, this.machineBitNum, idcId, machineId);
     25         }
     26     }
     27  
     28     /**
     29      * 起始的时间戳
     30      * 作者写代码时的时间戳
     31      */
     32     private final static long START_STAMP = 1508143349995L;
     33  
     34     /**
     35      * 可分配的位数
     36      */
     37     private final static int REMAIN_BIT_NUM = 22;
     38  
     39     /**
     40      * idc编号
     41      */
     42     private long idcId;
     43  
     44     /**
     45      * 机器编号
     46      */
     47     private long machineId;
     48  
     49     /**
     50      * 当前序列号
     51      */
     52     private long sequence = 0L;
     53  
     54     /**
     55      * 上次最新时间戳
     56      */
     57     private long lastStamp = -1L;
     58  
     59     /**
     60      * idc偏移量:一次计算出,避免重复计算
     61      */
     62     private int idcBitLeftOffset;
     63  
     64     /**
     65      * 机器id偏移量:一次计算出,避免重复计算
     66      */
     67     private int machineBitLeftOffset;
     68  
     69     /**
     70      * 时间戳偏移量:一次计算出,避免重复计算
     71      */
     72     private int timestampBitLeftOffset;
     73  
     74     /**
     75      * 最大序列值:一次计算出,避免重复计算
     76      */
     77     private int maxSequenceValue;
     78  
     79     private SnowFlakeGenerator(int idcBitNum, int machineBitNum, long idcId, long machineId) {
     80         int sequenceBitNum = REMAIN_BIT_NUM - idcBitNum - machineBitNum;
     81  
     82         if (idcBitNum <= 0 || machineBitNum <= 0 || sequenceBitNum <= 0) {
     83             throw new IllegalArgumentException("error bit number");
     84         }
     85  
     86         this.maxSequenceValue = ~(-1 << sequenceBitNum);
     87  
     88         machineBitLeftOffset = sequenceBitNum;
     89         idcBitLeftOffset = idcBitNum + sequenceBitNum;
     90         timestampBitLeftOffset = idcBitNum + machineBitNum + sequenceBitNum;
     91  
     92         this.idcId = idcId;
     93         this.machineId = machineId;
     94     }
     95  
     96     /**
     97      * 产生下一个ID
     98      */
     99     public synchronized long nextId() {
    100         long currentStamp = getTimeMill();
    101         if (currentStamp < lastStamp) {
    102             throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
    103         }
    104  
    105         //新的毫秒,序列从0开始,否则序列自增
    106         if (currentStamp == lastStamp) {
    107             sequence = (sequence + 1) & this.maxSequenceValue;
    108             if (sequence == 0L) {
    109                 //Twitter源代码中的逻辑是循环,直到下一个毫秒
    110                 lastStamp = tilNextMillis();
    111 //                throw new IllegalStateException("sequence over flow");
    112             }
    113         } else {
    114             sequence = 0L;
    115         }
    116  
    117         lastStamp = currentStamp;
    118  
    119         return (currentStamp - START_STAMP) << timestampBitLeftOffset | idcId << idcBitLeftOffset | machineId << machineBitLeftOffset | sequence;
    120     }
    121  
    122     private long getTimeMill() {
    123         return System.currentTimeMillis();
    124     }
    125  
    126     private long tilNextMillis() {
    127         long timestamp = getTimeMill();
    128         while (timestamp <= lastStamp) {
    129             timestamp = getTimeMill();
    130         }
    131         return timestamp;
    132     }
    133 }
  • 相关阅读:
    DOM深度优先遍历算法
    DOM事件
    DOM修改
    DOM的方法和属性
    DOM简介
    JSON.stringify()
    JSON解析
    JSON对象
    JSON语法
    JSON对比XML
  • 原文地址:https://www.cnblogs.com/yanduanduan/p/10038345.html
Copyright © 2011-2022 走看看