最近公司在搞什麼領域負責人,其實就是每一個人負責某幾個模塊,也就是owner的意思,公司的snowflake服務是我負責,藉此機會研究下雪花算法java
snowflake生成的結果是一個64bit大小的整數,使用long存儲,它的結構以下圖:算法
其中數據庫
[1]、1位,不用,二進制中的最高位是符號位,1表示負數,0表示正數,因爲咱們生成的雪花算法都是正整數,因此這裏是0[2]、41位,這裏的時間戳是表示的是從起始時間算起,到生成id時間所經歷的時間戳,也就是(當前時間戳-起始時間戳(固定)) 這裏一共是41位,範圍就是(0~ 2^41-1),這麼大的毫秒數轉化成時間就是大約69年 [3]、10位,這裏的10位表明工做機器id,一共能夠部署在(2^10=1024)臺機器上面,10位又能夠分爲前面五位是數據中心id(0~31),後面五位是機器id(0-31) [4]、共12位,序列位,一共可用(0 ~ 2^12-1)共4096個數字複製代碼
雪花算法能夠保證:服務器
package snowflake;import java.util.HashSet;import java.util.Set;/** * @author Smith 2020/12/26 */public class SnowFlakeService {//起始時間戳( 2020-12-26 00:00:00 )private static final long START_STAMP = 1608912000000L;//序列號佔用位數private static final long SEQUENCE_BIT = 12;//機器標識佔用位數private static final long MACHINE_BIT = 5;//數據中心佔用位數private static final long DATACENTER_BIT = 12;//序列號最大值private static final long MAX_SEQUENCE = -1L ^ (-1L << 12); // 4095/** * 偏移量 **/private static final long MACHINE_LEFT = SEQUENCE_BIT;private static final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private static final long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;private static long dataCenterId; //0, 數據中心(0-31)private static long machineId; //0, 機器標識(0-31)private static long sequence; //序列號 range(0 ~ 4095)private static long lastStamp; //上一次時間戳public static synchronized long getNextId() {long currentStamp = System.currentTimeMillis();if (currentStamp < lastStamp) {throw new IllegalArgumentException("時間被回退,不能繼續產生id"); }if (currentStamp == lastStamp) {//相同毫秒內,序列號自增sequence = (sequence + 1) & MAX_SEQUENCE;if (sequence == 0L) {//序列號已經到最大值System.out.println("序列號已經到達最大值");//使用下一個時間戳currentStamp = getNextStamp(); } } else {//不一樣毫秒,序列號重置sequence = 0L; } lastStamp = currentStamp;//當前時間戳存檔,用於判斷下次產生id時間戳是否相同return (currentStamp - START_STAMP) << TIMESTAMP_LEFT | dataCenterId << DATACENTER_LEFT | machineId << MACHINE_LEFT | sequence; }/** * 阻塞直至得到下一個時間戳 * * @return */public static long getNextStamp() {long newStamp = getCurrentStamp();while (newStamp <= lastStamp) { newStamp = getCurrentStamp(); }return newStamp; }/** * 獲取當前時間戳 * * @return */public static long getCurrentStamp() {return System.currentTimeMillis(); }public static void main(String[] args) { Set<Long> set = new HashSet<Long>();long start = System.currentTimeMillis();for (int i = 0; i < 100000; i++) { Long id = SnowFlakeService.getNextId(); set.add(id); } System.out.println("總耗時 : " + (System.currentTimeMillis() - start)); System.out.println(set.size()); } }複製代碼
運行結果:分佈式
序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 序列號已經到達最大值 總耗時 : 34100000複製代碼
由於序列號是每秒最多能夠生成4096個id,因此在序列號到達最大值的時候,程序會阻塞直到下一個毫秒時間戳,而後繼續生成id,從運行結果來看,在34ms內生成了100000個不一樣的id,仍是比較可觀的。ide
那麼如何解決這些問題呢(後面在研究): 百度開源了UIDGenerator 算法 美團團隊根據業務場景提出了基於號段思想的 Leaf-Segment 方案和基於 Snowflake 的 Leaf-Snowflake 方案操作系統