**:
原文: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 原始碼的,見
業務系統需要怎樣的全域性唯一ID
id 生成器在微博我們一直叫發號器,微博就是用這樣的號來儲存,而我微博裡討論的時候也都是以發號器為標籤。它的主要目的確如平常大家理解的 為乙個分布式系統的資料object產生乙個唯一的標識 但其實在乙個真實的系統裡可能也可以承擔更多的作用。概括起來主要有以下幾點 唯一性時間相關 粗略有序 可反解可製...
全域性唯一遞增的id 趨勢有序的全域性唯一ID
常見方法 不足與優化 常見方法一 使用資料庫的 auto increment 來生成全域性唯一遞增id 優點 1 簡單,使用資料庫已有的功能 2 能夠保證唯一性 3 能夠保證遞增性 4 步長固定 缺點 1 可用性難以保證 資料庫常見架構是一主多從 讀寫分離,生成自增id是寫請求,主庫掛了就玩不轉了 ...
怎樣生成唯一的ID
作為乙個web開發者應該知道這裡的users後面的那串 066ab87a062b 必定是我的id,這個id肯定是唯一的。這就是今天要討論的,怎樣生成唯一的id?如果你是乙個初級的web開發者,那麼你可能不會去生成這種唯一的id,因為有乙個很簡單的方法已經幫我們搞定了這個事情,那就是資料庫。這個時候我...