SnowFlake 算法,是 Twitter 開源的分布式 id 生成算法。
其核心思想是,使用一個 64 bit 的 long 型的數字作為全局唯一 id。
這個64 bit 的 long 型數字的儲存模型如下:
第一部分,占用 1 bit:0。
第二部分,占用 41 bit:表示的是時間戳。
第三部分,占用 5 bit:表示的是機房 id。
第四部分,占用 5 bit:表示的是機器 id。
第五部分,占用 12 bit:表示的是序號。
代碼如下:
package com.demo.springboot.common; public class IdWorker { //因為二進制里第一個 bit 為如果是 1,那么都是負數,但是我們生成的 id 都是正數,所以第一個 bit 統一都是 0。 //代表一毫秒內生成的多個id的最新序號 2進制12位 2^12 - 1 = 4095個 private long sequence; //機器ID 2進制5位 2^5 - 1 = 31個 private long workerId; //機房ID 2進制5位 2^5 - 1 = 31個 private long datacenterId; //設置一個時間初始值 2進制41位 2^41 - 1差不多可以用69年 //2021-01-01 => 1609430400000 private long epoch = 1609430400000L; //12位的最新序號 private long sequenceBits = 12L; //5位的機器id private long workerIdBits = 5L; //5位的機房id private long datacenterIdBits = 5L; //這個是二進制運算,就是5 bit機器id最大值31 private long maxWorkerId = -1L ^ (-1L << workerIdBits); //這個是一個意思,就是5 bit機房id最大值31 private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerIdShift = sequenceBits; private long datacenterIdShift = sequenceBits + workerIdBits; private long timestampShift = sequenceBits + workerIdBits + datacenterIdBits; //記錄產生時間毫秒數,判斷是否是同1毫秒 private long lastTimestamp = -1L; public IdWorker(long datacenterId, long workerId, long sequence) { //檢查機房id和機器id 不能超過31 不能小於0 if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException( String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException( String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } this.datacenterId = datacenterId; this.workerId = workerId; this.sequence = sequence; } //生成一個全局唯一的id public synchronized long nextId() { //獲取當前時間戳,單位毫秒 long timestamp = timeGen(); if (timestamp < lastTimestamp) { System.err.printf( "clock is moving backwards. Rejecting requests until %d.", lastTimestamp); throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //同一個毫秒內生成多個id,seqence序號遞增1,最大是4095 if (lastTimestamp == timestamp) { //這個位運算避免sequence超過4095 sequence = (sequence + 1) & sequenceMask; //當某一毫秒產生的id數超過4095,系統會進入等待,直到下一毫秒繼續產生id if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } //記錄一下最近一次生成id的時間戳,單位毫秒 lastTimestamp = timestamp; //二進制位運算操作,生成一個64bit的id return ((timestamp - epoch) << timestampShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } //當某一毫秒產生的id數超過4095,系統會進入等待,直到下一毫秒繼續產生id private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } //獲取當前時間戳 private long timeGen(){ return System.currentTimeMillis(); } public static void main(String[] args) { IdWorker worker = new IdWorker(1,1,1); for (int i = 0; i < 10; i++) { System.out.println(worker.nextId()); } } }