全局惟一id生成算法--雪花算法詳解-snowflake

最近公司在搞什麼領域負責人,其實就是每一個人負責某幾個模塊,也就是owner的意思,公司的snowflake服務是我負責,藉此機會研究下雪花算法java

1.1 雪花算法結構

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個數字複製代碼

1.2 雪花算法做用

雪花算法能夠保證:服務器

  • 生成的全部的id都是隨着時間遞增
  • 分佈式系統內不會產生重複的id(由於有機器位作區分)

1.3 雪花算法代碼實現

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

1.4 雪花算法的弊端

  • snowflake不依賴數據庫也不依賴內存,隨時能夠生成id,這也是爲何它如此受歡迎,可是由於它在設計時經過時間戳來避免對內存和數據庫的依賴,因此它依賴於服務器的時間。  試想,若是服務器的時間發生了錯亂或者回撥,這就直接影響了生成的id,有很大可能生成重複的id,且必定會打破遞增屬性,這也是他的一個致命缺點(不支持服務器時間回撥)
  • 每毫秒生成id的上限受到限制,因爲時間戳位是41位的毫秒級時間戳,因此從當前起始到41bit 耗盡,只能堅持70年
  • 程序獲取操做系統時間會耗費較多時間

那麼如何解決這些問題呢(後面在研究): 百度開源了UIDGenerator 算法 美團團隊根據業務場景提出了基於號段思想的 Leaf-Segment 方案和基於 Snowflake 的 Leaf-Snowflake 方案操作系統

相關文章
相關標籤/搜索