分佈式系統的唯一ID 頂 原

需求

爲什麼需要唯一ID

讓分佈式系統中的需要辨別的元素,都能有唯一的辨識標誌。 幾乎所有的業務系統,都有生成一個記錄標識的需求,例如:

  1. 消息標識:message-id
  2. 訂單標識:order-id
  3. 帖子標識:tiezi-id

爲什麼需要趨勢有序

記錄標識上的查詢,往往又有分頁或者排序的業務需求,例如:

  1. 拉取最新的一頁消息:select message-id order by time limit 100
  2. 拉取最新的一頁訂單:select order-id order by time limit 100
  3. 拉取最新的一頁帖子:select tiezi-id order by time limit 100

所以往往要有一個time字段,並且在time字段上建立普通索引(non-cluster index)。

普通索引存儲的是實際記錄的指針,其訪問效率一般會比聚集索引慢,如果記錄標識在生成時能夠基本按照時間有序,則可以省去這個time字段的索引查詢:select message-id (order by message-id) limit 100但是,能這麼做的前提是,message-id的生成基本是趨勢時間遞增的

怎麼實現唯一ID

UUID

UUID就是爲了要在分佈式環境中產生唯一標示符而發佈的一個標準。標準中規定UUID長度爲16Bytes(128Bits),一般將其表示爲550e8400-e29b-41d4-a716-446655440000這種16進制格式,同時將其分爲5部分,每部分用-分割,各部分長度分別爲8,4,4,12。現在使用的UUID算法有5個版本,分別使用5種不同的算法計算產生。

  1. UUID1: 依據當前計算機的MAC地址時鐘來生成uuid。
  2. UUID2: 和版本1類似,不過使用域標示符本地UID代替了版本1中的時鐘信息。
  3. UUID3: 根據url,域標示符等標示符做MD5 Hash產生的。
  4. UUID4: 根據產生的隨機數來生成。
  5. UUID5: 和版本3類似,只不過替換成了SHA-1算法。

優點:

  1. 本地生成,不需要控制中心管理,成本低
  2. 性能好

缺點:

  1. id共128Bits太長
  2. id間沒有次序關係,不能隱含信息

mogodb ObjectId

MongoDB中每一條記錄都有一個id字段用來唯一標示本記錄。如果用戶插入數據時沒有顯示提供id字段,那麼系統會自動生成一個。ObjectID一共12Bytes,設計的時候充分考慮了分佈式環境下使用的情況,所以能保證在一個分佈式MongoDB集羣中唯一。ObjectID格式如下:

0        4      7    9      12  
+--------+------+----+------+
|time    |pc    |pid |inc   |
+--------+------+----+------+

0~4 Byte是Unix Timestamp。 4~7 Byte是當前機器“hostname/mac地址/虛擬編號”其中之一的MD5結果的前3個字節。 7~9 Byte是當前進程的PID。 9~12Byte是累加計數器或是一個隨機數(只有當不支持累加計數器時才用隨機數)。 最後生成的仍然是一個用16進製表示的串,如47cc67093475061e3d95369d。這裏MongoDB的ObjectID相對UUID有個很大的優點就是ObjectID是時間上有序的。另外還有ObjectID本身也包含了很多其它有用的信息,通過直接解碼ObjectID即可直接獲得這些信息。

優點:

  1. 時間有序
  2. 隱含信息,可在業務中結合加以利用。

缺點:

  1. 當time段一樣,由於MD5只取前3Byte,有可能造成pc段一樣,這樣就有可能有重複的id。
  2. ID 間隙較大(當某一段時間不生成id,那麼這個time段浪費很多空間)

snowflack

Snowflake是twitter開源的一款獨立的適用於分佈式環境的ID生成服務器。生成的ID是64Bits,同時滿足高性能(>10K ids/s),低延遲(<2ms)和高可用。與MongoDB ObjectID類似這裏生成的ID也是時間上有序的。編碼方式也和ObjectID類似,如下:

0           41     51     64  
+-----------+------+------+
|time       |pc    |inc   |
+-----------+------+------+

前41bits是以微秒爲單位的timestamp。 接着10bits是事先配置好的機器ID。 最後12bits是累加計數器。

有缺點與MongoDB ObjectId類似。但是隻要機器ID不重複,應該不會出現重複的ID。

Instagram採用的方式

Instagram要將其中存儲的圖片分片到多個PostgreSQL中,其中生成ID的方案和MongoDB ObjectID類似。整個ID的長度爲64Bits,設定爲這個長度是爲了優化在redis中的存儲。ID的編碼格式如下:

41bits以微秒爲單位的timestamp,時間起點從2011-01-01開始。 13bits表示進行邏輯分片的Shard ID。 10bits表示一個累加計數器。 ID的生成邏輯用PL/PGSQL語言寫到PostgreSQL數據庫中,當每次插入數據時由數據庫自動計算生成。 與上面優缺點類似。

Leaf

主要參考:http://wiki.sankuai.com/pages/viewpage.action?pageId=465861190。 利用step設置每個服務能從數據庫拿到的號段大小,能充分的利用id的空間,能保證號段內各個id的時間順序,但是不能保證號段間時間上的順序。

主要優點是id佔用字節少(64bits),能充分利用空間,幾乎沒有間隙(按作者說,除非服務器宕機,這種可能會比較小)。

我的想法:

假設應用生命週期爲30年(一般極少有應用生命週期30年,linux系統到現在也不超過30年,就算30年到時候也該換方案和架構了),如果時間的精確度是微秒,30年需要通過12位整數保存,使用二進制保存所有12位整數需要大約40位二進制;如果是秒,需要9位整數保存,使用大約30位二進制。假設63位中(除最高位,最高位應該是符號位。)

  • 使用微秒方案:前40位給時間,那麼還有23位可以給step區間(可表示8百萬個整數,相當於容量爲1微秒8百萬個id)。
  • 使用秒方案:前30位給時間,那麼還有33位可以給step區間(每秒產生id數量與使用微妙方案一秒產生的id數量相同)。
  • 使用X秒方案:以此類推

對比秒方案和微秒方案,(X)秒方案可能由於時間對系統能表述的id空間的浪費更少,而且整體能表述的id數量不變,但是遞增趨勢更弱(使用微妙,遞增趨勢更強)。

總結

一般在分佈式系統中,與生成唯一ID有關的因素可以來自:

  • 時間(基於某一時刻到現在的相對時間,更節約空間)
  • 機器邏輯區分ID(如:機器ID,存儲的分片)
  • 機器的硬件信息(如:MAC地址等)
  • 局部自增
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章