分佈式ID - Snowflake
今天要跟大家介紹如果在分布式的系統上,我們要如何產生系統的唯一 ID 呢?
目前常見的幾個方式:
- 使用 UUID
- 使用資料庫控制
- 實作 Snowflake
UUID 由於不具可讀性,無法有序排序且過長,而資料庫控制的瓶頸很容易會卡在資料庫上。
因次今天要大家介紹的方法是 Snowflake
Quick Start
Snowflake
是 Twitter 在 2010 年公佈的一個分布式 ID 的演算法。原始代碼是使用 Scala 撰寫。
目前 Snowflake
已經不公開維護了,不過大家還是可以在 GitHub 下載 Source Code
大家可以從上面的源碼中查看 README,可以看到,Twitter 設計的 UUID 是透過 64 bits
生成的一個唯一 ID (long)
整個 ID 組成包含
- time - 41 bits (millisecond precision w/ a custom epoch gives us 69 years)
- configured machine id - 10 bits - gives us up to 1024 machines
- sequence number - 12 bits - rolls over every 4096 per machine (with protection to avoid rollover in the same ms)
有很多人有將 Twitter 的 Snowflake
演算法實作成 Java 的版本。如:
不過從上面大家可以看到,代碼中我們在創建 Snowflake
時,需要帶入類似機器碼的資訊來生成 UUID1
SnowFlake snowFlake = new SnowFlake(2, 3);
如果是一個分散式的程式架構,那我們要如何處理這邊呢?
這裡我們可以參考百度出的UidGenerator,來幫我們處理這個問題。
百度的 UidGenerator
百度的方式基本上也是依照 Twitter 提出的 Snowflake
演算法進行調整。
不過他在資料庫中,加入了一張 Table 去紀錄我們所謂的 merchant id
Table Schema 如下:1
2
3
4
5
6
7
8
9
10
11
12
13DROP 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;
不知道大家有沒有發現,使用百度的 UidGenerator
並沒有很方便,因為我們還需要將他的 Source Code 放入我們的 Project 中來做使用。
那有沒有更快速便捷的方式呢?
UidGenerator Springboot Starter
UidGenerator Springboot Starter主要基於 UidGenerator 調整為 spring-boot-starter
方式。
使用方式如下:
加入 maven
pom.xml1
2
3
4
5<dependency>
<groupId>com.github.wujun234</groupId>
<artifactId>uid-generator-spring-boot-starter</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>可選配置
application.yml1
2
3
4
5
6
7
8
9
10
11uid:
timeBits: 30 # 时间位, 默认:30
workerBits: 16 # 机器位, 默认:16
seqBits: 7 # 序列号, 默认:7
epochStr: "2019-02-20" # 初始时间, 默认:"2019-02-20"
enableBackward: true # 是否容忍时钟回拨, 默认:true
maxBackwardSeconds: 1 # 时钟回拨最长容忍时间(秒), 默认:1
CachedUidGenerator: # CachedUidGenerator相关参数
boostPower: 3 # RingBuffer size扩容参数, 可提高UID生成的吞吐量, 默认:3
paddingFactor: 50 # 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50
#scheduleInterval: 60 # 默认:不配置此项, 即不实用Schedule线程. 如需使用, 请指定Schedule线程时间间隔, 单位:秒測試
UuidTests.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@RunWith(SpringRunner.class)
@SpringBootTest
public class UuidTests {
@Resource
private UidGenerator defaultUidGenerator;
// @Resource
// private UidGenerator cachedUidGenerator;
@Test
public void testSerialGenerate() {
// Generate UID
for(int i=0; i<=1000; i++) {
long uid = defaultUidGenerator.getUID();
System.out.println(defaultUidGenerator.parseUID(uid));
}
}
}
Unit 結果驗證圖
資料庫的 WORKER_NODE
數據
補充!
- 這邊我們可以發現,如果是同一台 Server 如果
HOST_NAME
與PORT
就不會新產生一筆資料。 - 而我們也可以透過配置
application.yml
調整整個 UUID 想要配置的各個區塊的長度(時間、機器碼、流水號)等。
如果有其他問題,歡迎寄信討論!謝謝!
Reference
Twitter Snoflake
Java Snowflake
UidGenerator
UidGenerator Springboot Starter
Donate
謝謝您的支持與鼓勵