在分布式系統(tǒng)中,有些場景需要使用全局唯一 ID,一來作為業(yè)務(wù)標(biāo)識(shí),一來為了滿足接口的冪等性設(shè)計(jì)。例如我們文件系統(tǒng)中的 fid。
單表情況下我們可以直接使用數(shù)據(jù)庫的自增id,但是分庫分表后就無法滿足需求了,需要想辦法通過其他手段來實(shí)現(xiàn)。
對(duì)于全局唯一ID,需要具備的特性:
- 全局唯一
- 遞增
- 高可用
- 自主性
- 安全性(不會(huì)暴露)
利用數(shù)據(jù)庫自增id生成
實(shí)現(xiàn)方式

image.png
- 利用 MySQL 數(shù)據(jù)庫自增 id 的特性獲取到一個(gè) sequence
- 上層服務(wù)(多臺(tái)機(jī)器)獲取到 sequence,然后在此基礎(chǔ)上逐步累加 step。相當(dāng)于每次獲取到的 sequence 共有 step 個(gè)id可用
- 獲取到的 id 用盡后,重新獲取 sequence
優(yōu)點(diǎn)
- 天然有序
- 理解起來容易,實(shí)現(xiàn)起來簡單
缺點(diǎn)
- “下發(fā) sequence 的服務(wù)” 以及 “數(shù)據(jù)庫” 是單點(diǎn)(使用多個(gè)水平庫來生成 sequence,自增id設(shè)置不同的初始值以及步長)
- 數(shù)據(jù)庫遷移,需要提前預(yù)留出足夠的 sequence,防止沖突
- 服務(wù)器重啟、單點(diǎn)故障會(huì)導(dǎo)致 id 不連續(xù)
- 暴露一天的數(shù)據(jù)量
UUID
實(shí)現(xiàn)方式
優(yōu)點(diǎn)
- 無需依賴中間件或三方服務(wù),可自主生成
- 高并發(fā)
- 各個(gè)語言均有實(shí)現(xiàn)庫可直接使用
缺點(diǎn)
- 字符串存儲(chǔ),占空間,DB查詢及索引效率差
- 不是自增id,可讀性差
- 不同的實(shí)現(xiàn)方式可能泄漏信息
Redis/MongoDB 文檔全局唯一ID/zookeeper
實(shí)現(xiàn)方式
Redis
利用 redis 單線程的特性,使用 incr 或 increby 生成 id-
MongoDB 的 objectId
3.2 及之前版本
image.png
3.2 之后版本
image.png zookeeper 數(shù)據(jù)節(jié)點(diǎn)的版本
優(yōu)點(diǎn)
- 性能相較于數(shù)據(jù)庫會(huì)好上很多
- 可以集群部署
- 生成的id 可以帶上時(shí)間信息
缺點(diǎn)
- 需要引入中間件,增加系統(tǒng)復(fù)雜度
- 下發(fā) id 的服務(wù)同樣是一個(gè)單點(diǎn)
雪花算法
實(shí)現(xiàn)方式

image.png
- 41bits 作為毫秒數(shù)。大概可以用 69.7 年
- 10bits 作為機(jī)器編號(hào)(5bits 是數(shù)據(jù)中心,5bits 的機(jī)器 ID),支持 1024 個(gè)實(shí)例
- 12bits 作為毫秒內(nèi)的序列號(hào)。一毫秒可以生成 4096 個(gè)序號(hào)
優(yōu)點(diǎn)
- 本地生成,沒有網(wǎng)絡(luò)消耗,不需要引入中間件
- 高并發(fā)
- 包含時(shí)間信息
缺點(diǎn)
- 依賴于機(jī)器時(shí)鐘,同一臺(tái)機(jī)器如果把時(shí)間回?fù)?,生成?ID 就會(huì)有重復(fù)的風(fēng)險(xiǎn)
解決方案:- 將ID生成交給少量服務(wù)器,并關(guān)閉時(shí)鐘同步。
- 直接報(bào)錯(cuò),交給上層業(yè)務(wù)處理。
- 如果回?fù)軙r(shí)間較短,在耗時(shí)要求內(nèi),比如5ms,那么等待回?fù)軙r(shí)長后再進(jìn)行生成。
- 如果回?fù)軙r(shí)間很長,那么無法等待,可以勻出少量位(1~2位)作為回?fù)芪唬坏r(shí)鐘回?fù)?,將回?fù)芪患?,可得到不一樣的ID,2位回?fù)芪辉试S標(biāo)記三次時(shí)鐘回?fù)?,基本夠使用。如果超出了,可以再選擇拋出異常。
可以看到我們采用的方案并沒有滿足全部全局ID的特性,就好像分布式系統(tǒng)的 CAP 特性,我們只能盡量多的滿足其中的特性。
沒有最好的方案,只有最合適的方案。

