業務系統需要什么樣的ID生成器

jopen 9年前發布 | 24K 次閱讀 生成器

ID 生成器在微博我們一直叫發號器,微博就是用這樣的號來存儲,而我微博里討論的時候也都是以發號器為標簽。它的主要目的確如平常大家理解的“為一個分布式系 統的數據object產生一個唯一的標識”,但其實在一個真實的系統里可能也可以承擔更多的作用。概括起來主要有以下幾點:

  1. 唯一性
  2. 時間相關
  3. 粗略有序
  4. 可反解
  5. 可制造

下面我會分別講每個作用后面的考慮和權衡,也會對比介紹一下業界已知的幾種 ID 設計。

  1. 要唯一性,是否需要全局唯一?

    說起全局唯一,通常大家都會在想到發號器服務,分布式的通常需要更大空間,中心式的則需要在一個合適的地方在會聚。這就可能涉及到鎖,而鎖意味著成本和性能的下降。所以當前的系統是否需要全局的唯一性,就是一個需要考慮的問題。

    比如在通訊系統里,聊天消息可能就未必需要全局,因為一條消息只是某一個人發出,系統只要保證一個人維度的唯一性即可。本質上而言,這里利用了用戶 ID 的唯一性,因為唯一性是可以依賴的,通常我們設計系統也都是基于類似的性質,比如后面降到的使用時間唯一性的方式。

  2. 用時間來做什么?千萬年太久,只爭朝夕?

    前面說到唯一性可以依賴,我們需要選擇的是依賴什么。通常的做法可以選擇數據庫自增,這在很多數據庫里都是可以滿足ACID 的操作。但是用數據庫有個缺點,就是數據庫有性能問題,在多機房情況下也很難處理。當然,你可以通過調整自增的步長來設計,但對于一個發號器而言,操作和 維護都略重了。

    而時間是天然唯一的,因此也是很多設計的選擇。但對于一個8Byte的 ID 而言,時間并沒有那么多。你如果精確到秒級別,三十年都要使用30bit,到毫秒級則要再增加10bit,你也只剩下20bit 可以做其他事情了。之所以在8Byte 上搗鼓,因為8Byte 是一個Long,不管在處理器和編譯器還是語言層面,都是可以更好地被處理。

    然而三十年夠么?對于一個人來說,可能不夠,但對一個系統而言,可能足夠。我們經常開玩笑,互聯網里能活三十年的系統有多少呢?三十年過去,你的系統可能 都被重寫 N 遍了。這樣的信心同樣來自于摩爾定律,三十年后,計算性能早就提高了上千倍,到時候更多Byte 都不會是問題了。

  3. 粗略有多粗略,秒還是毫秒?

    每秒一個或者每毫秒一個ID明顯是不夠的,剛才說到還有20bit 可以做其他事情,就包括一個SequenceID。如果要達到精確的有序,就要對 Sequence 進行并發控制,性能上肯定會打折。所以經常會有的一個選擇就是,在這個秒的級別上不再保證順序,而整個 ID 則只保證時間上的有序。后一秒的 ID肯定比前一秒的大,但同一秒內可能后取的ID比前面的號小。這在使用時非常關鍵,你要理解,系統也要接受才可以。

    那時間用秒還是毫秒呢?其實不用毫秒的時候就可以把空出來的10bit 送給 Sequence,但整個ID 的精度就下降了。峰值速度是更現實的考慮。Sequence 的空間決定了峰值的速度,而峰值也就意味著持續的時間不會太久。這方面,每秒100萬比每毫秒1000限制更小。

  4. 可反解,解開的是什么?

    一個 ID 生成之后,就會伴隨著信息終身,排錯分析的時候,我們需要查驗。這時候一個可反解的 ID 可以幫上很多忙,從哪里來的,什么時候出生的。 跟身份證倒有點兒相通了,其實身份證就是一個典型的分布式 ID 生成器。

    如果ID 里已經有了時間而且能解開,在存儲層面可能不再需要timestamp 一類的字段了。微博的 ID 還有很多業務信息,這個后面會細講。

  5. 可制造,為什么不用UUID?

    互聯網系統上可用性永遠是優先指標。但由于分布式系統的脆弱,網絡不穩定或者底層存儲系統的不可用,業務系統隨時面臨著失敗。為了給前端更友好的響應,我 們需要能盡量容忍失敗。比如在存儲失敗時,可能需要臨時導出請求供后續處理,而后續處理時已經離開了當時的時間點,順序跟其他系統錯開了。我們需要制造出 這樣的ID 以便系統好像一直正常運行一樣,可制造的 ID 讓你可以控制生產日期(汗,有點兒假冒偽劣的意思了),然后繼續下面的處理。

    另一個重要場景就是數據清洗。這個屬于較少遇到,但并不罕見的情況,可能是原來 ID 設計的不合理,也可能由于底層存儲的改變,都可能出現。這樣一個可制造的 ID 就會帶來很多操作層面的便利。

    這也是我們不用 UUID 的一個原因。UUID 標準可以保證在某時某地生成,但如果要控制生成一個特定時間的 UUID,可能需要底層庫的改動。經驗告訴我們,能在上層解決的問題不要透到下層,這種庫的維護成本是非常高的。

設計細節

UUID 就不說了, 其他公開出來的這里說下SnowFlake、Weibo以及 Ticktick 的設計。

  1. SnowFlake

    41bit留給毫秒時間,10bit給MachineID,也就是機器要預先配置,剩下12位留給Sequence。代碼雖然露出來了,但其實已經不可用了,據說是內部改造中。

  2. Weibo

    微博使用了秒級的時間,用了30bit,Sequence 用了15位,理論上可以搞定3.2w/s的速度。用4bit來區分IDC,也就是可以支持16個 IDC,對于核心機房來說夠了。剩下的有2bit 用來區分業務,由于當前發號服務是機房中心式的,1bit 來區分熱備。是的,也沒有用滿64bit。

  3. Ticktick

    也就是當前在環信系統里要用到的。使用了30bit 的秒級時間,20bit 給Sequence。這里是有個考慮,第一版實現還是希望到毫秒級,所以20bit 的前10bit給了毫秒來用,剩下10bit給 Sequence。等到峰值提高的時候可以暫時回到秒級。

    前面說到的三十年問題,因此我在高位留了2bit 做 Version,或者到時候改造使用更長字節數,用第一位來標識不同 ID,或者可以把這2bit 挪給時間用,可以給系統改造留出一定的時間。

    剩下的10bit 留給 MachineID,也就是說當前 ID 生成可以直接內嵌在業務服務中,最多支持千級別的服務器數量。最后有2bit 做Tag 用,可能區分群消息和單聊消息。同時你也看出,這個 ID 最多支持一天10億消息,也是怕系統增速太快,這2bit 可以挪給 Sequence,可以支持40億級別消息量,或者結合前面的版本支持到百億級別。

后記

自己實現一個發號器非常簡單,所以Ticktick 怎么實現并不重要。不過吶,我還是有 demo 源碼的,見 https://github.com/ericliang/ticktick

來自:http://ericliang.info/what-kind-of-id-generator-we-need-in-business-systems/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!