分布式——分布式發號器


今天停電,所以springboot源碼看不了,手頭剛好有本書,學習了下分布式發號器

一、方案

1、UUID

  • 無法滿足業務特性。UUID雖然能保證ID的唯一性,但是無法滿足業務要求的很多其他特性,如有序性+可反解性(沒有提供反解方法,例如反解得到時間戳)+可制造性(手工生成、洗臟數據難度變大)
  • 占用空間大。UUID比較長,利用JDK生成的一個UUID占用36字節(由於包含a-f,數據庫類型varchar類型):8-4-4-4-12,如果建立B+樹索引的話,導致單個索引節點包含關鍵字減少,索引節點變多,高度變高,性能下降
  • 由於是無序的,作為InnoDB主鍵索引,可能會導致頁分裂,降低插入性能同時,還產生了很多內存碎片
public class UUIDTest { public static void main(String[] args) { //JDK中uuid屬於總共128個bit //time_low = 32bit //time_mid = 16bit //time_high_and_version= 16bit //variant_and_sequence= 16bit //node = 48bit //① 由於toString采用16進制(4bit)打印 8-4-4-4-12共36個字節 //② version=4的uuid,隨機數生成,沒有機器碼,所以分布式還是可能存在重復的
        UUID uuid = UUID.randomUUID(); System.out.println(uuid.toString());//5db67001-7d8b-49a0-baa2-215629b00ae9
 } }

2、數據庫生成

利用數據庫作為發號器,有兩種方法:表字段自增與自增序列sequence。雖然能保證id唯一性,但是會影響數據庫的性能與伸縮性。

增加數據庫壓力,降低性能。Id的產生是一種頻繁的操作,會頻繁的請求數據庫,導致數據庫性能下降。

降低數據庫審索引。無論表字段自增還是自增序列實現,都會影響數據庫分表分庫。

sequence是一種隱式字段,需要人工維護

-- 表字段自增 mysql中常用
id  bigint(20) auto increment, -- 自增序列sequence oracle中常用
create sequence sequence_name increment by 1   -- 每次加的個數據
start with 1    -- 從1開始計數
nomaxvalue    -- 不設置最大值
nocycle      -- 一直累加,不循環
cache 10 ; sequence.nextval; -- 自增sequence並返回

3、Snowflake——雪花算法

 Snowflake是Twitter開源的分布式發號器,類似uuid使用bit位控制生成。結構如下:

-- 總共64bit,
-- 時間戳放在高位,保證毫秒級的有序性;
-- 機器號+序列號處於低位,單機有序的,多機無序的
-- 1bit:無效位,其實可以作為版本號 -- 41bit:時間戳位 -- 10bit:機器號 -- 12bit:序列號

簡單實現,具體的肯定比這個復雜得多:

public class SnowFlakeIdGenerator { private final long version;//版本號 1bit
    private final long STARTTIMESTAMP = 1585286065061L;//創建ID生成器的時間
    private long timestamp;//時間戳毫秒級 41bit
    private final long mcid;//機器id 10bit
    private int seq = -1;//自增seq

    private long lastTimstamp = -1;//記錄毫秒級
    private long lastSeql = 0;//記錄同毫秒級的sql

    public SnowFlakeIdGenerator(long version,long mcid){ //版本號校驗
        if (version>1 || version <0){ throw new IllegalArgumentException("version must in {0,1}"); } //機器號校驗
        if (mcid>1023 || mcid < 0){ throw new IllegalArgumentException("mcid must in {0-1023}"); } this.version = version; this.mcid = mcid; } /** * 同毫秒級seq自增, * 不同毫秒級seq歸零 * @return
     */
    private long getSeq(long current){ if (lastTimstamp == current){ //同毫秒級校驗seq是否超出范圍
            if (seq > 1022){ throw new IllegalArgumentException("seq arrive max,generator failed!"); } }else{ //不同毫秒級seq歸零
            lastTimstamp = current; seq = -1; System.out.println("============== 分割線 ================="); } return ++seq; } private long generator(){ long timestamp = System.currentTimeMillis()-STARTTIMESTAMP; long id = 0L; //1 + 41 + 10 + 12 由於時間處於高位,所以毫秒間是有序的
        id |= version << 63; id |= timestamp << 22; id |= mcid << 12; id |= getSeq(timestamp); return id; } public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(2); //開兩個線程模擬分布式兩台機器 //毫秒間是有序的 //毫秒內是單機有序的,整體是無序的
        pool.submit(new Runnable() { @Override public void run() { SnowFlakeIdGenerator idGenerator = new SnowFlakeIdGenerator(0L,0L); for (int i = 0; i < 1000; i++) { System.out.println(idGenerator.generator()); } } }); pool.submit(new Runnable() { @Override public void run() { SnowFlakeIdGenerator idGenerator1 = new SnowFlakeIdGenerator(0,1L); for (int i = 0; i < 1000; i++) { System.out.println(idGenerator1.generator()); } } }); pool.shutdown(); } }

雪花算法優點:

占用空間比uuid小。由於返回的是一個長整型long,所以數據庫字段可使用bigint屬性創建,實際最大8字節。

粗略有序。通過時間戳+機器號+seq自增的設計實現了粗略有序,毫秒級內單機有序,多機無序,毫秒級間有序。作為InnoDB主鍵索引的話,降低頁分裂的可能。

沒有依賴於數據庫等中間件,不受中間件限制。

雪花算法的缺點:

趨勢遞增:由於seq放在末位,會暴露業務信息,例如競爭對手可以通過ID判斷出每天大致的業務量。

時間同步:依賴於系統時間,電子時間是需要同步的,例如每4年同步一次閏秒,可能會影響ID的生成。

4、開源項目——vesta-id-generator

自定義設計主要學習《可伸縮服務架構-框架與中間件》中的具體代碼實現,snowflake的優化版。

書上提供的開源項目地址刪除了,從github上找到了一個地址

采用兩種粒度模式的ID:最大峰值型(秒級有序)、最小峰值型(毫秒級有序)

-- 兩種類型的最大區別是:時間+序列號占用位數不同 -- 最大峰值型(秒級有序)
  -- 版本 : 1bit 0或1,默認0,一個版本可堅持30年,兩個就是60年了 
  -- 類型 : 1bit 0或1,控制粒度 
  -- 生成方式 : 2bit 00或01或10或11:標識三種發布模式。 
  -- 秒級時間 : 30bit 秒級時間從0-2^30-1,2^30/60/60/24/365=34,可使用30年,毫秒級也是。 
  -- 序列號 : 20bit 
  -- 機器號 : 10bit 將序列號調整到機器號之前,避免遞增趨勢  -- 最小峰值型(毫秒級有序)
  -- 版本 : 1bit
  -- 類型 : 1bit
  -- 生成方式 : 2bit
  -- 毫秒級 : 40bit
  -- 序列號 : 10bit
  -- 機器號 : 10bit

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM