Springboot分布式Snowflake ID生成工具uid-generator-starter

分布式環(huán)境不同于單機系統(tǒng),對ID生成有著更為嚴(yán)苛的需求,具體如下:

  • 全局唯一,這是基本要求,不能出現(xiàn)重復(fù)。
  • 單調(diào)遞增,連續(xù)的,下一個ID要大于上一個ID,這主要是從MySQL InnoDB存儲引擎的性能來考慮的。
  • 長度,長度越短需要的存儲空間越小,同時還能夠提高查詢效率,使用MySQL時尤為突出。
  • 高可用,無單點隱患
  • 高性能,生成速度快,延時低,扛住高并發(fā)

常用的分布式ID生成方案

UUID

優(yōu)點: 開發(fā)語言本身提供實現(xiàn),足夠簡單;全球唯一;無性能問題

缺點:長度過大,不利于存儲和檢索;非單調(diào)遞增,對MySQL索引不利(作為數(shù)據(jù)庫主鍵,在InnoDB引擎下,UUID的無序性可能會引起數(shù)據(jù)位置頻繁變動,嚴(yán)重影響性能)

數(shù)據(jù)庫自增主鍵

基于數(shù)據(jù)庫的自增主角,單獨使用一個數(shù)據(jù)庫實例作全局ID生成器。

優(yōu)點:實現(xiàn)簡單;單調(diào)遞增;數(shù)值類型,長度合適,查詢速度快

缺點:強依賴數(shù)據(jù),存在單點隱患;存在性能問題,無法抗住高并發(fā)

數(shù)據(jù)庫多實例自增主鍵

每個數(shù)據(jù)庫設(shè)置固定的step增長步長,使得每個數(shù)據(jù)庫生成的主鍵單調(diào)遞增且不重復(fù),如:DB1生成1、4、7、10;DB2生成2、5、8、11;DB3生成3、6、9、12

優(yōu)點:無單點隱患;平衡負載

缺點:需固定步長,擴容困難;單庫壓力依然大;應(yīng)用較為復(fù)雜

類Snowflake算法

使用twitter開源的Snowflake算法,其構(gòu)造如下:

snowflake

優(yōu)點:高性能(每秒生成百萬ID);單調(diào)遞增

缺點:強依賴機器時鐘,存在時鐘回撥問題(會導(dǎo)致重復(fù)的ID生成)

uid-generator

uid-generator是由百度開源的基于Snowflake算法的唯一ID生成器,使用java語言實現(xiàn)。uid-generator以組件形式工作在應(yīng)用項目中, 支持自定義workerId位數(shù)和初始化策略, 從而適用于docker等虛擬化環(huán)境下實例自動重啟、漂移等場景。 在實現(xiàn)上, uid-generator通過借用未來時間來解決sequence天然存在的并發(fā)限制; 采用RingBuffer來緩存已生成的UID, 并行化UID的生產(chǎn)和消費, 同時對CacheLine補齊,避免了由RingBuffer帶來的硬件級「偽共享」問題. 最終單機QPS可達600萬。

uid-generator項目詳情: 請點擊

uid-generator對Snowflake算法生成的ID構(gòu)造做了調(diào)整,如下:

uid-generator snowflake id

worker node id 為每個工作節(jié)點的ID(機器、應(yīng)用實例),uid-generator提供接口可由用戶自行實現(xiàn)其生成方式,默認(rèn)是基于數(shù)據(jù)庫生成。

uid-generator解決時間回撥問題、提升性能主要是通過如下技術(shù)手段實現(xiàn):

1、動態(tài)遞增worker node id : 每次啟動都會往數(shù)據(jù)庫WORKER_NODE表中插入一條記錄,插入成功后返回的該數(shù)據(jù)對應(yīng)的自增唯一主鍵,此主鍵就作為該應(yīng)用實例的worker node id 。保證每個應(yīng)用實例、每次啟動所獲取的worker node id 都不同,因此不會出現(xiàn)生成重復(fù)的ID。即使時鐘回撥,因為workerId不同,也不會出現(xiàn)ID沖突

2、RingBuffer: RingBuffer本質(zhì)是一個數(shù)組,uid-generator利用RingBuffer數(shù)據(jù)結(jié)構(gòu)預(yù)先生成若干個ID并緩存,當(dāng)需要獲取ID時候,如果數(shù)組中有則優(yōu)先使用緩存的ID,這樣可極大提高效率與吞吐量

3、未來時間:大部分snowflake算法的實現(xiàn)都會使用System.currentTimeMillis()來獲取時間戳,這樣嚴(yán)重依賴服務(wù)器的時間。uid-generator使用填充完RingBuffer時的時間戳作為lastSecond(AtomicLong類型),下次填充時使用lastSecond.incrementAndGet()來獲取新的時間戳,非使用System.currentTimeMillis(),規(guī)避了時鐘回撥問題。

uid-generator-starter

從官網(wǎng)說明或者其他網(wǎng)上的使用教程可見,將uid-generator集成到springboot項目中,還是有點小麻煩的。uid-generator-starter對uid-generator進行了Springboot Starter風(fēng)格的封裝,只要一行注解便可將其集成到項目中,同時還增加一些實用的特性:

  1. spring-boot-starter風(fēng)格的開箱即用。

  2. 可為uid-generator獨立設(shè)置數(shù)據(jù)源,和業(yè)務(wù)系統(tǒng)的主數(shù)據(jù)源分開。

  3. 支持使用ZooKeeper進行WORKER ID分配,藉由ZK的Paxos強一致性算法獲取更高的可用性。

開源地址

github:uid-generator-starter

如果此工具對你有幫助,請在github中Star支持下

快速開始

1、引入uid-generator-starter

<dependency>
    <groupId>com.github</groupId>
    <artifactId>uid-generator-starter</artifactId>
    <version>最新的版本號</version>
</dependency>

2、在數(shù)據(jù)庫(mysql)中創(chuàng)建WORKER_NODE表

DROP TABLE IF EXISTS WORKER_NODE;
CREATE TABLE WORKER_NODE
(
    ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
    HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
    PORT VARCHAR(64) NOT NULL COMMENT 'port',
    TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
    LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
    MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
    CREATED TIMESTAMP NOT NULL COMMENT 'created time',
    PRIMARY KEY(ID)
)
COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;

3、注解啟用uid-generator

@Transactional
@EnableUidGenerator //啟用uid-generator
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4、使用UidGenerator

@Resource
private UidGenerator uidGenerator;

@Test
public void contextLoads()  {
    for(int i=0;i<100;i++) {
        System.out.println("uid:"+uidGenerator.getUID());
    }
}

使用獨立的數(shù)據(jù)源

在數(shù)據(jù)庫uid-db中創(chuàng)建WORKER_NODE表,使用其作為uid-generator的專用數(shù)據(jù)庫

每個業(yè)務(wù)系統(tǒng)只需將uid-generator的數(shù)據(jù)庫設(shè)置為uid-db即可

#---------------------- 業(yè)務(wù)配置   -----------------------
spring:
  datasource: #業(yè)務(wù)數(shù)據(jù)源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/yewu1
    password: admin
    username: 123456
#---------------------- uid-generator   -----------------------
uid-generator: 
  #time-bits: 28 #可選配置, 如未指定將采用默認(rèn)值
  #worker-bits: 22 #可選配置, 如未指定將采用默認(rèn)值
  #seq-bits: 13 #可選配置, 如未指定將采用默認(rèn)值
  #epoch-str: 2020-10-21 #可選配置, 如未指定將采用默認(rèn)值(2020-10-21)
  #boost-power: 3 #可選配置, 如未指定將采用默認(rèn)值
  #padding-factor: 50 #可選配置, 如未指定將采用默認(rèn)值
  #schedule-interval:  #可選配置, 如未指定則不啟用此功能
  datasource: #使用獨立的數(shù)據(jù)源,如未指定將采用應(yīng)用系統(tǒng)的數(shù)據(jù)源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.666:3306/uid-db
    password: admin
    username: 123456

使用zookeeper

作為一個專門為分布式應(yīng)用提供一致性服務(wù)的軟件,使用zookeeper作為workerId的配置維護工具再合適不過了,如果你的系統(tǒng)追求高度可用性,強烈推薦使用zookeeper集群。

#---------------------- 業(yè)務(wù)配置   -----------------------
spring:
  datasource: #業(yè)務(wù)數(shù)據(jù)源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/yewu?
    password: admin
    username: 123456
#---------------------- uid-generator   -----------------------
uid-generator: 
  #time-bits: 28 #可選配置, 如未指定將采用默認(rèn)值
  #worker-bits: 22 #可選配置, 如未指定將采用默認(rèn)值
  #seq-bits: 13 #可選配置, 如未指定將采用默認(rèn)值
  #epoch-str: 2016-05-20 #可選配置, 如未指定將采用默認(rèn)值
  #boost-power: 3 #可選配置, 如未指定將采用默認(rèn)值
  #padding-factor: 50 #可選配置, 如未指定將采用默認(rèn)值
  #schedule-interval:  #可選配置, 如未指定則不啟用此功能
  #datasource: #使用獨立的數(shù)據(jù)源,如未指定將采用應(yīng)用系統(tǒng)的數(shù)據(jù)源
    #driver-class-name: com.mysql.cj.jdbc.Driver
    #url: jdbc:mysql://192.168.1.666:3306/uid-db
    #password: root
    #username: root
  zookeeper: 
    #zk連接地址,集群模式則用逗號分開,如: 192.168.1.333:2181,192.168.1.555:2182,192.168.1.66:2183
    addrs: 192.168.1.333:2181 
    #authentication: admin:123456 #digest類型的訪問秘鑰,如:user:password,默認(rèn)為不使用秘鑰
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容