分布式系統(tǒng)如何生成全局唯一的ID

本文主要介紹在一個(gè)分布式系統(tǒng)中, 怎么樣生成全局唯一的 ID

一, 問題描述

在分布式系統(tǒng)存在多個(gè) Shard 的場(chǎng)景中, 同時(shí)在各個(gè) Shard 插入數(shù)據(jù)時(shí), 怎么給這些數(shù)據(jù)生成全局的 unique ID?

在單機(jī)系統(tǒng)中 (例如一個(gè) MySQL 實(shí)例), unique ID 的生成是非常簡(jiǎn)單的, 直接利用 MySQL 自帶的自增 ID 功能就可以實(shí)現(xiàn).

但在一個(gè)存在多個(gè) Shards 的分布式系統(tǒng) (例如多個(gè) MySQL 實(shí)例組成一個(gè)集群, 在這個(gè)集群中插入數(shù)據(jù)), 這個(gè)問題會(huì)變得復(fù)雜, 所生成的全局的 unique ID 要滿足以下需求:

保證生成的 ID 全局唯一
今后數(shù)據(jù)在多個(gè) Shards 之間遷移不會(huì)受到 ID 生成方式的限制
生成的 ID 中最好能帶上時(shí)間信息, 例如 ID 的前 k 位是 Timestamp, 這樣能夠直接通過對(duì) ID 的前 k 位的排序來對(duì)數(shù)據(jù)按時(shí)間排序
生成的 ID 最好不大于 64 bits
生成 ID 的速度有要求. 例如, 在一個(gè)高吞吐量的場(chǎng)景中, 需要每秒生成幾萬(wàn)個(gè) ID (Twitter 最新的峰值到達(dá)了 143,199 Tweets/s, 也就是 10萬(wàn)+/秒)
整個(gè)服務(wù)最好沒有單點(diǎn)
如果沒有上面這些限制, 問題會(huì)相對(duì)簡(jiǎn)單, 例如:

直接利用 UUID.randomUUID() 接口來生成 unique ID (http://www.ietf.org/rfc/rfc4122.txt). 但這個(gè)方案生成的 ID 有 128 bits, 另外, 生成的 ID 中也沒有帶 Timestamp
利用一個(gè)中心服務(wù)器來統(tǒng)一生成 unique ID. 但這種方案可能存在單點(diǎn)問題; 另外, 要支持高吞吐率的系統(tǒng), 這個(gè)方案還要做很多改進(jìn)工作 (例如, 每次從中心服務(wù)器批量獲取一批 IDs, 提升 ID 產(chǎn)生的吞吐率)
Flickr 的做法 (http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/). 但他這個(gè)方案 ID 中沒有帶 Timestamp, 生成的 ID 不能按時(shí)間排序
在要滿足前面 6 點(diǎn)要求的場(chǎng)景中, 怎么來生成全局 unique ID 呢?

Twitter 的 Snowflake 是一種比較好的做法. 下面主要介紹 Twitter Snowflake, 以及它的變種

二, Twitter Snowflake

https://github.com/twitter/snowflake

Snowflake 生成的 unique ID 的組成 (由高位到低位):

41 bits: Timestamp (毫秒級(jí))
10 bits: 節(jié)點(diǎn) ID (datacenter ID 5 bits + worker ID 5 bits)
12 bits: sequence number
一共 63 bits (最高位是 0)

unique ID 生成過程:

10 bits 的機(jī)器號(hào), 在 ID 分配 Worker 啟動(dòng)的時(shí)候, 從一個(gè) Zookeeper 集群獲取 (保證所有的 Worker 不會(huì)有重復(fù)的機(jī)器號(hào))
41 bits 的 Timestamp: 每次要生成一個(gè)新 ID 的時(shí)候, 都會(huì)獲取一下當(dāng)前的 Timestamp, 然后分兩種情況生成 sequence number:
如果當(dāng)前的 Timestamp 和前一個(gè)已生成 ID 的 Timestamp 相同 (在同一毫秒中), 就用前一個(gè) ID 的 sequence number + 1 作為新的 sequence number (12 bits); 如果本毫秒內(nèi)的所有 ID 用完, 等到下一毫秒繼續(xù) (這個(gè)等待過程中, 不能分配出新的 ID)
如果當(dāng)前的 Timestamp 比前一個(gè) ID 的 Timestamp 大, 隨機(jī)生成一個(gè)初始 sequence number (12 bits) 作為本毫秒內(nèi)的第一個(gè) sequence number
整個(gè)過程中, 只是在 Worker 啟動(dòng)的時(shí)候會(huì)對(duì)外部有依賴 (需要從 Zookeeper 獲取 Worker 號(hào)), 之后就可以獨(dú)立工作了, 做到了去中心化.

異常情況討論:

在獲取當(dāng)前 Timestamp 時(shí), 如果獲取到的時(shí)間戳比前一個(gè)已生成 ID 的 Timestamp 還要小怎么辦? Snowflake 的做法是繼續(xù)獲取當(dāng)前機(jī)器的時(shí)間, 直到獲取到更大的 Timestamp 才能繼續(xù)工作 (在這個(gè)等待過程中, 不能分配出新的 ID)
從這個(gè)異常情況可以看出, 如果 Snowflake 所運(yùn)行的那些機(jī)器時(shí)鐘有大的偏差時(shí), 整個(gè) Snowflake 系統(tǒng)不能正常工作 (偏差得越多, 分配新 ID 時(shí)等待的時(shí)間越久)

從 Snowflake 的官方文檔 (https://github.com/twitter/snowflake/#system-clock-dependency) 中也可以看到, 它明確要求 "You should use NTP to keep your system clock accurate". 而且最好把 NTP 配置成不會(huì)向后調(diào)整的模式. 也就是說, NTP 糾正時(shí)間時(shí), 不會(huì)向后回?fù)軝C(jī)器時(shí)鐘.

三, Snowflake 的其他變種

Snowflake 有一些變種, 各個(gè)應(yīng)用結(jié)合自己的實(shí)際場(chǎng)景對(duì) Snowflake 做了一些改動(dòng). 這里主要介紹 3 種.

  1. Boundary flake

http://boundary.com/blog/2012/01/12/flake-a-decentralized-k-ordered-unique-id-generator-in-erlang/

變化:

ID 長(zhǎng)度擴(kuò)展到 128 bits:
最高 64 bits 時(shí)間戳;
然后是 48 bits 的 Worker 號(hào) (和 Mac 地址一樣長(zhǎng));
最后是 16 bits 的 Seq Number
由于它用 48 bits 作為 Worker ID, 和 Mac 地址的長(zhǎng)度一樣, 這樣啟動(dòng)時(shí)不需要和 Zookeeper 通訊獲取 Worker ID. 做到了完全的去中心化
基于 Erlang
它這樣做的目的是用更多的 bits 實(shí)現(xiàn)更小的沖突概率, 這樣就支持更多的 Worker 同時(shí)工作. 同時(shí), 每毫秒能分配出更多的 ID

  1. Simpleflake

http://engineering.custommade.com/simpleflake-distributed-id-generation-for-the-lazy/

Simpleflake 的思路是取消 Worker 號(hào), 保留 41 bits 的 Timestamp, 同時(shí)把 sequence number 擴(kuò)展到 22 bits;

Simpleflake 的特點(diǎn):

sequence number 完全靠隨機(jī)產(chǎn)生 (這樣也導(dǎo)致了生成的 ID 可能出現(xiàn)重復(fù))
沒有 Worker 號(hào), 也就不需要和 Zookeeper 通訊, 實(shí)現(xiàn)了完全去中心化
Timestamp 保持和 Snowflake 一致, 今后可以無(wú)縫升級(jí)到 Snowflake
Simpleflake 的問題就是 sequence number 完全隨機(jī)生成, 會(huì)導(dǎo)致生成的 ID 重復(fù)的可能. 這個(gè)生成 ID 重復(fù)的概率隨著每秒生成的 ID 數(shù)的增長(zhǎng)而增長(zhǎng).

所以, Simpleflake 的限制就是每秒生成的 ID 不能太多 (最好小于 100次/秒, 如果大于 100次/秒的場(chǎng)景, Simpleflake 就不適用了, 建議切換回 Snowflake).

  1. instagram 的做法

先簡(jiǎn)單介紹一下 instagram 的分布式存儲(chǔ)方案:

先把每個(gè) Table 劃分為多個(gè)邏輯分片 (logic Shard), 邏輯分片的數(shù)量可以很大, 例如 2000 個(gè)邏輯分片
然后制定一個(gè)規(guī)則, 規(guī)定每個(gè)邏輯分片被存儲(chǔ)到哪個(gè)數(shù)據(jù)庫(kù)實(shí)例上面; 數(shù)據(jù)庫(kù)實(shí)例不需要很多. 例如, 對(duì)有 2 個(gè) PostgreSQL 實(shí)例的系統(tǒng) (instagram 使用 PostgreSQL); 可以使用奇數(shù)邏輯分片存放到第一個(gè)數(shù)據(jù)庫(kù)實(shí)例, 偶數(shù)邏輯分片存放到第二個(gè)數(shù)據(jù)庫(kù)實(shí)例的規(guī)則
每個(gè) Table 指定一個(gè)字段作為分片字段 (例如, 對(duì)用戶表, 可以指定 uid 作為分片字段)
插入一個(gè)新的數(shù)據(jù)時(shí), 先根據(jù)分片字段的值, 決定數(shù)據(jù)被分配到哪個(gè)邏輯分片 (logic Shard)
然后再根據(jù) logic Shard 和 PostgreSQL 實(shí)例的對(duì)應(yīng)關(guān)系, 確定這條數(shù)據(jù)應(yīng)該被存放到哪臺(tái) PostgreSQL 實(shí)例上
instagram unique ID 的組成:

41 bits: Timestamp (毫秒)
13 bits: 每個(gè) logic Shard 的代號(hào) (最大支持 8 x 1024 個(gè) logic Shards)
10 bits: sequence number; 每個(gè) Shard 每毫秒最多可以生成 1024 個(gè) ID
生成 unique ID 時(shí), 41 bits 的 Timestamp 和 Snowflake 類似, 這里就不細(xì)說了.

主要介紹一下 13 bits 的 logic Shard 代號(hào) 和 10 bits 的 sequence number 怎么生成.

logic Shard 代號(hào):

假設(shè)插入一條新的用戶記錄, 插入時(shí), 根據(jù) uid 來判斷這條記錄應(yīng)該被插入到哪個(gè) logic Shard 中.
假設(shè)當(dāng)前要插入的記錄會(huì)被插入到第 1341 號(hào) logic Shard 中 (假設(shè)當(dāng)前的這個(gè) Table 一共有 2000 個(gè) logic Shard)
新生成 ID 的 13 bits 段要填的就是 1341 這個(gè)數(shù)字
sequence number 利用 PostgreSQL 每個(gè) Table 上的 auto-increment sequence 來生成:

如果當(dāng)前表上已經(jīng)有 5000 條記錄, 那么這個(gè)表的下一個(gè) auto-increment sequence 就是 5001 (直接調(diào)用 PL/PGSQL 提供的方法可以獲取到)
然后把 這個(gè) 5001 對(duì) 1024 取模就得到了 10 bits 的 sequence number
instagram 這個(gè)方案的優(yōu)勢(shì)在于:

利用 logic Shard 號(hào)來替換 Snowflake 使用的 Worker 號(hào), 就不需要到中心節(jié)點(diǎn)獲取 Worker 號(hào)了. 做到了完全去中心化
另外一個(gè)附帶的好處就是, 可以通過 ID 直接知道這條記錄被存放在哪個(gè) logic Shard 上
同時(shí), 今后做數(shù)據(jù)遷移的時(shí)候, 也是按 logic Shard 為單位做數(shù)據(jù)遷移的, 所以這種做法也不會(huì)影響到今后的數(shù)據(jù)遷移

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嘹叫,隨后出現(xiàn)的幾起案子婆殿,更是在濱河造成了極大的恐慌,老刑警劉巖罩扇,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婆芦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喂饥,警方通過查閱死者的電腦和手機(jī)消约,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來员帮,“玉大人或粮,你說我怎么就攤上這事±谈撸” “怎么了氯材?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)硝岗。 經(jīng)常有香客問我氢哮,道長(zhǎng),這世上最難降的妖魔是什么型檀? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任冗尤,我火速辦了婚禮,結(jié)果婚禮上胀溺,老公的妹妹穿的比我還像新娘裂七。我一直安慰自己,他們只是感情好月幌,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布碍讯。 她就那樣靜靜地躺著,像睡著了一般扯躺。 火紅的嫁衣襯著肌膚如雪捉兴。 梳的紋絲不亂的頭發(fā)上蝎困,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音倍啥,去河邊找鬼禾乘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛虽缕,可吹牛的內(nèi)容都是我干的始藕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼氮趋,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼伍派!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起剩胁,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诉植,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后昵观,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晾腔,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年啊犬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灼擂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡觉至,死狀恐怖剔应,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情语御,我是刑警寧澤领斥,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站沃暗,受9級(jí)特大地震影響月洛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孽锥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一嚼黔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惜辑,春花似錦唬涧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抵卫,卻和暖如春狮荔,著一層夾襖步出監(jiān)牢的瞬間胎撇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工殖氏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晚树,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓雅采,卻偏偏與公主長(zhǎng)得像爵憎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子婚瓜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文主要介紹在一個(gè)分布式系統(tǒng)中, 如何去生成全局唯一的 ID宝鼓。 前言 單純的生成全局ID并不是什么難題,生成全局的...
    FX_SKY閱讀 2,337評(píng)論 0 6
  • 序 本文主要來聊聊分布式id的生成方案巴刻。 目標(biāo) 業(yè)務(wù)系統(tǒng)需要什么樣的ID生成器中提出了幾點(diǎn)目標(biāo): 唯一性 時(shí)間相關(guān)...
    go4it閱讀 629評(píng)論 0 3
  • 前言 前面介紹了ES的插件安裝席函,ELK的準(zhǔn)備工作就已經(jīng)做完了,現(xiàn)在開始學(xué)習(xí)搭建ES集群冈涧。看了網(wǎng)上的大神總結(jié)出來的經(jīng)...
    我的橙子很甜閱讀 4,221評(píng)論 0 8
  • 今天是分手后第四個(gè)月……十一月十六日正蛙,我在KTV唱了陳奕迅的好久不見督弓,十年,愛情轉(zhuǎn)移什么的乒验,陳奕迅的歌像是在講故事...
    55c96f98e667閱讀 231評(píng)論 0 2
  • 《記亮劍Ledge的第一天》 作者:陳序 借宿在奶奶家愚隧,距離上課地點(diǎn)較...
    陳序原創(chuàng)閱讀 264評(píng)論 0 0