分布式服务自增长唯一ID小结

Posted atomicbomb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式服务自增长唯一ID小结相关的知识,希望对你有一定的参考价值。

1、常用生成唯一ID的方式,例如UUID

 

2、生成唯一自自增长ID方式:

  例如:

    • Zookeeper的增加ID;
    • redis的incr方法
    • mongodb的objectId

3、采用雪花模型

如下代码:

  1 /**
  2  * 采用twitter的雪花算法,生成有一定顺序且不重复的id,结果类型为64位的long型
  3  */
  4 public class SnowflakeIdUtils {
  5     //集群id
  6     private long datacenterId;
  7     //机器id
  8     private long workerId;
  9     //序列号
 10     private long sequenceId;
 11 
 12     //集群id的bit位数
 13     private long datacenterIdBits = 5L;
 14     //机器id的bit位数
 15     private long workerIdBits = 5L;
 16     //序列号的bit位数
 17     private long sequenceIdBits = 12L;
 18 
 19     //集群id的最大编号
 20     private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 21     //机器id的最大编号
 22     private long maxWorkerId = -1L ^ (-1L << workerIdBits);
 23     //序列号的掩码
 24     private long sequenceIdMask = -1L ^ (-1L << sequenceIdBits);
 25 
 26     //生成最终结果时,集群id需移动的bit位数
 27     private long timestampShiftBits = sequenceIdBits + workerIdBits + datacenterIdBits;
 28     //生成最终结果时,集群id需移动的bit位数
 29     private long datacenterIdShiftBits = sequenceIdBits + workerIdBits;
 30     //生成最终结果时,机器id需移动的bit位数
 31     private long workerIdShiftBits = sequenceIdBits;
 32 
 33     //去掉过去的时间,即从指定时间(本例以2017-10-12 00:00:00)开始算,
 34     // 大约可用69.5年(41位的时间位,最大值换成毫秒,再换算成年,大约69.5年)
 35     //1507737600000为从1970-01-01 00:00:00到2017-10-12 00:00:00经过的毫秒数
 36     private long pastMills = 1507737600000L;
 37     //上一次生成id使用的timestamp ,以毫秒为单位
 38     private long lastTimestamp = 1L;
 39 
 40     /**
 41      * 若没有指定集群id和机器id,则默认均为0
 42      */
 43     public SnowflakeIdUtils() {
 44         this(0, 0);
 45     }
 46 
 47     /**
 48      * 指定集群id和机器id
 49      *
 50      * @param datacenterId
 51      * @param workerId
 52      */
 53     public SnowflakeIdUtils(long datacenterId, long workerId) {
 54         if (datacenterId < 0 || datacenterId > maxDatacenterId) {
 55             throw new RuntimeException(String.format("datacenterId greater than %d or less than 0", maxDatacenterId));
 56         }
 57         if (workerId < 0 || workerId > maxWorkerId) {
 58             throw new RuntimeException(String.format("workerId greater than %d or less than 0", maxWorkerId));
 59         }
 60         this.datacenterId = datacenterId;
 61         this.workerId = workerId;
 62     }
 63 
 64     /**
 65      * 生成全局唯一的id
 66      *
 67      * @return
 68      */
 69     public synchronized long nextId() {
 70         long timestamp = System.currentTimeMillis();
 71         if (timestamp < lastTimestamp) {  //出现这种情况,通常是由于机器时间出问题了
 72             throw new RuntimeException("machine time error");
 73         }
 74 
 75         //同一时刻生成的id号
 76         if (timestamp == lastTimestamp) {
 77             sequenceId = (sequenceId + 1) & sequenceIdMask;
 78             if (sequenceId == 0) {  //说明当前毫秒的序列号用完了,需从下个毫秒数开始重新计数
 79                 timestamp = nextTimestamp(lastTimestamp);
 80             }
 81         } else {
 82             //否则序列号从0开始
 83             sequenceId = 0L;
 84         }
 85 
 86         lastTimestamp = timestamp;
 87         long id = ((timestamp - pastMills) << timestampShiftBits)
 88                 | (datacenterId << datacenterIdShiftBits)
 89                 | (workerId << workerIdShiftBits)
 90                 | sequenceId;
 91         return id;
 92     }
 93 
 94     /**
 95      * 获取上次取数毫秒的下一时刻
 96      *
 97      * @param lastTimestamp
 98      * @return
 99      */
100     long nextTimestamp(long lastTimestamp) {
101         long timestamp = System.currentTimeMillis();
102         while (timestamp <= lastTimestamp) {
103             timestamp = System.currentTimeMillis();
104         }
105         return timestamp;
106     }
107 
108     public static void main(String[] args) throws Exception {
109         SnowflakeIdUtils snowflakeIdGen = new SnowflakeIdUtils();
110         //测试,生成10个唯一id
111         for (int i = 0; i < 10; i++) {
112             long id = snowflakeIdGen.nextId();
113             System.out.println(id);
114         }
115     }
116 }

这里采用的是默认构造生成 SnowflakeIdUtils,如果分布式服务同时调用的话,会出现重复ID,

所以需要加上 集群id(0~31),workerId(0~31)

 

了解更多雪花模型知识,后续补充。

 

以上是关于分布式服务自增长唯一ID小结的主要内容,如果未能解决你的问题,请参考以下文章

游戏服务器生成全局唯一ID的几种方法

游戏服务器生成全局唯一ID的几种方法

在分布式场景,生成唯一ID

分布式系统ID生成方案汇总

分布式系统唯一ID生成方案汇总

分布式系统唯一ID生成方案汇总