全局ID生成器
文章目錄
- 全局ID生成器
- 一、全局ID生成器的定義
- 定義
- 核心作用
- 二、全局ID生成器需滿足的特征
- 1. 唯一性(Uniqueness)?
- 2. 高性能(High Performance)?
- 3. 可擴展性(Scalability)?
- 4. 有序性(Orderliness)?
- 5. 可靠性(Reliability)?
- 6. 安全性(Security)?
- 7. 兼容性(Compatibility)?
- 三、全局唯一ID生成策略:
- 1. UUID
- 2. 數據庫自增ID
- 3. Snowflake算法(Twitter開源)?
- 4. Redis自增
- 5. 數據庫號段模式
- 6. Leaf(美團開源)?
- 四、策略對比
- 五、選型建議
一、全局ID生成器的定義
定義
- 全局ID生成器是一種在分布式系統中生成唯一標識符(ID)的機制,確保生成的ID在整個系統范圍內(可能跨多個服務、數據庫或數據中心)具有唯一性。它是解決分布式環境下數據唯一性、數據關聯和事務協調等問題的核心技術之一。
核心作用
- ?唯一性:確保每個生成的ID在全局范圍內不重復。
- 可識別性:通過ID快速定位資源或關聯業務(如訂單、用戶、消息等),不能被看出太明顯的規律。
- ?擴展性:支持系統規模增長(如分庫分表、多節點部署)時仍能高效生成ID,高效應對海量數據。
二、全局ID生成器需滿足的特征
全局ID生成器,是分布式系統環境下生成全局唯一ID的工具,一般需要滿足下列特征:
1. 唯一性(Uniqueness)?
- ?核心要求:生成的ID在全局范圍內絕對唯一。
- 意義:避免數據沖突(如主鍵重復、消息覆蓋)。
- 實現方式:
- 算法設計(如Snowflake通過時間戳+機器ID+序列號保證唯一)。
- 中心化協調(如數據庫自增ID依賴數據庫的唯一約束)。
2. 高性能(High Performance)?
- ?要求:生成ID的速度快,延遲低,支持高并發場景。
- 意義:避免成為系統瓶頸(如秒殺場景下每秒生成數十萬ID)。
- 實現方式:
- 本地生成(如Snowflake在內存中生成,無需網絡請求)。
- 批量預分配(如數據庫號段模式一次性獲取多個ID)。
3. 可擴展性(Scalability)?
- 要求:支持系統橫向擴展(如新增節點)時無需重構ID生成邏輯。
- 意義:適應業務增長,避免單點瓶頸。
- 實現方式:
- 分布式算法(如Snowflake允許動態增加機器ID)。
- 去中心化設計(如UUID無需中心節點)。
4. 有序性(Orderliness)?
- ?要求:生成的ID按時間遞增或可排序。
- 意義:便于數據庫索引優化、分頁查詢和業務排序(如按時間排序訂單)。
- ?實現方式:
- 時間戳高位(如Snowflake將時間戳放在ID的高位)。
- 數據庫自增ID天然有序,但分布式下需分片步長策略。
5. 可靠性(Reliability)?
- 要求:生成過程容錯,避免單點故障。
- 意義:保障系統高可用(如機器宕機不影響ID生成)。
- 實現方式:
- 去中心化算法(如Snowflake無單點依賴)。
- 冗余設計(如Leaf通過ZooKeeper管理機器ID,支持故障轉移)。
6. 安全性(Security)?
- ?要求:防止ID被猜測或遍歷(如避免暴露業務量或用戶隱私)。
- 意義:防止惡意攻擊(如通過ID遍歷批量查詢數據)。
- 實現方式:
- 加入隨機性(如UUIDv4的隨機部分)。
- 加密或哈希處理(如美團的Leaf-Segment加密號段)。
7. 兼容性(Compatibility)?
- 要求:ID格式適配現有技術棧(如數據庫類型、網絡傳輸)。
- 意義:降低集成成本(如避免使用超長ID導致存儲浪費)。
- 實現方式:
- 數值型ID(如Snowflake的64位Long類型,兼容MySQL BIGINT)。
- 字符串ID(如UUID的128位字符串,適配NoSQL數據庫)。
三、全局唯一ID生成策略:
1. UUID
- 原理:生成128位隨機字符串(如 550e8400-e29b-41d4-a716-446655440000)。
- 優點:簡單、無需中心化協調。
- 缺點:無序、長度長、存儲和索引效率低。
- 適用場景:非高頻查詢場景,如日志跟蹤、臨時標識。
- 優化:使用UUIDv4(隨機生成)避免隱私問題。
2. 數據庫自增ID
- ?原理:利用數據庫自增主鍵生成唯一ID。
- 優點:簡單、天然有序。
- ?缺點:單點瓶頸、橫向擴展困難。
- ?優化:
- 分庫分表:為每個分片設置不同步長(如庫1步長=2,起始值=1;庫2步長=2,起始值=2)。
- 批量獲取:一次性申請多個ID(如 INSERT … SELECT MAX(id)+N)。
3. Snowflake算法(Twitter開源)?
- ID結構?(64位):
| 符號位(1) | 時間戳(41) | 機器ID(10) | 序列號(12) |
- 優點:有序、高性能、去中心化。
- ?缺點:依賴系統時鐘(時鐘回撥需處理)。
- 實現:
public class Snowflake {private final long machineId; // 機器ID(0~1023)private long lastTimestamp = -1L;private long sequence = 0L;public synchronized long nextId() {long timestamp = System.currentTimeMillis();if (timestamp < lastTimestamp) throw new RuntimeException("時鐘回撥");if (timestamp == lastTimestamp) {sequence = (sequence + 1) & 0xFFF; // 序列號自增,溢出則等待下一毫秒if (sequence == 0) timestamp = waitNextMillis();} else {sequence = 0;}lastTimestamp = timestamp;return ((timestamp - TWEPOCH) << 22) | (machineId << 12) | sequence;} }
4. Redis自增
-
原理:利用Redis的原子操作 INCR 或 INCRBY 生成自增ID。使用Java的Long類型存儲,為了增加ID的安全性,我們可以不直接只用Redis自增的數值,而是拼接一些其他信息:
ID的組成部分(Java Long類型存儲,占用8個字節,也就是64個比特位):0 - 0000000 00000000 00000000 00000000 - 00000000 00000000 00000000 00000000↑ ↑ ↑符號位 |<------- 時間戳(31 bit)-------->| |<-------- 序列號(32 bit)-------->|
- 符號位: 1 bit,永遠為0(表示正數)
- 時間戳:31 bit,以秒為單位,以2000年1月1日00時作為參照系,用
當前時間 - 參照時間 = 時間戳
,2^31 秒 約等于69年。 - 序列號:31 bit,Redis自增的值,該方式理論上支持每秒產生 2^32 個不同ID。
-
?優點:高性能、簡單。
-
缺點:依賴Redis可用性,需處理持久化問題。
-
優化:集群模式 + 多Key分片(如 order_id:shard_1)。
-
實現:
@Component public class RedisIdGenerator {/*** 開始時間戳 2000年1月1日零時零分零秒*/private static final long BEGIN_TIMESTAMP = 946684800L;/*** 序列號位數*/private static final int COUNT = 32;private StringRedisTemplate stringRedisTemplate;@Autowiredpublic RedisIdGenerator(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public long nextId(String keyPrefix) {// 1.生成時間戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowSecond - BEGIN_TIMESTAMP;// 2.生成序列號String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd")); // 使用冒號分割,方便統計Long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date); // 加入日期,防止超出2^32上限(Redis自增上限為2^64)long incrementUnbox = 0L;if (increment != null) {incrementUnbox = increment;}// 3.拼接并返回return timestamp << COUNT | incrementUnbox; // 左移 32位,拼接時間戳和序列號}public static void main(String[] args) {// 2000年1月1日零時零分零秒 的秒數LocalDateTime time = LocalDateTime.of(2000, 1, 1, 0, 0, 0);long second = time.toEpochSecond(ZoneOffset.UTC);System.out.println("second = " + second); // second = 946684800} }
5. 數據庫號段模式
- 原理:從數據庫批量獲取號段(如一次分配1000個ID),緩存在本地使用。
- ?表設計:
CREATE TABLE id_segment (biz_tag VARCHAR(32) PRIMARY KEY, -- 業務標識max_id BIGINT NOT NULL, -- 當前最大IDstep INT NOT NULL -- 每次步長 );
- 優點:減少數據庫壓力、可擴展性強。
- 缺點:需維護號段表,處理并發沖突。
6. Leaf(美團開源)?
- ?混合模式:
- 號段模式:類似數據庫號段,依賴數據庫。
- Snowflake模式:依賴ZooKeeper分配機器ID。
- 優點:高可用、靈活切換模式。
- 實現:通過ZooKeeper管理機器ID,支持號段預分配。
四、策略對比
策略 | 唯一性 | 有序性 | 性能 | 可靠性 | 典型場景 |
---|---|---|---|---|---|
UUID | ? | ? | 高 | ? | 日志跟蹤、臨時標識 |
?數據庫自增ID | ? | ? | 低 | ?(單點) | 中小規模分庫分表 |
Snowflake | ? | ? | 極高 | ?(去中心化) | 高并發訂單、消息隊列 |
Redis INCR | ? | ? | 高 | ?(依賴Redis) | 短期唯一ID(如會話ID) |
?Leaf(美團)? | ? | ? | 高 | ?(混合模式) | 大規模分布式系統 |
五、選型建議
場景 | 推薦方案 | 關鍵考慮 |
---|---|---|
高性能、有序ID | Snowflake | 處理時鐘回撥(NTP同步、異常等待) |
簡單、無序 | UUID v4 | 適合臨時標識 |
依賴Redis | Redis INCR | 需保障Redis高可用 |
數據庫友好、可控 | 數據庫號段模式 | 適合中小規模分布式系統 |
企業級復雜場景 | Leaf | 結合號段和Snowflake優勢 |
實際選型建議
- ?高并發有序ID:優先選擇Snowflake或其變種(如美團的Leaf-Segment)。
- ?簡單臨時標識:使用UUID v4(如用戶臨時Token)。
- ?數據庫友好場景:數據庫號段模式(如分庫分表的預分配ID)。
- ?強一致性需求:結合Redis或ZooKeeper的分布式鎖生成ID。
常見問題處理
- 時鐘回撥:
- 方案:記錄上次時間戳,發現回撥時拋出異常或等待時鐘追上。
- 優化:使用NTP同步服務器時間,或擴展Snowflake增加時間戳位數。
- 機器ID分配:
- 方案:ZooKeeper/配置中心動態分配,或按數據中心+機器ID編碼。
根據業務規模、一致性要求及運維成本選擇合適的策略,必要時可組合使用多種方案。