UUID的學習與思考

我們在進行分佈式系統相關開發的時候,經常需要ID號(例如,訂單號,消息id號)。如果不是分佈式系統的化,生成一個ID號是非常簡單的,因爲你自己知道自己生成的所有ID號,但是分佈式系統環境下,你自己生成ID號的時候你是不知道其他人生成的ID號的,你再按原先的規則生成的ID號可能就與其他人生成的ID號重複了,這在很多時候是不行的。所以就有了UUID這個東西,其實就是分佈式ID,讓所有分佈式節點各自生成ID而不與其他節點發生ID衝突。

UUID生成方法

常見的UUID有幾種版本的生成方法,可參考A Universally Unique IDentifier (UUID) URN Namespace以及Universally_unique_identifier。比較常用的一種基於時間的生成方式是:<Time, MAC, Sequence>,也有基於哈希、隨機數等方式。這種生成方式一個缺點就是生成的ID太長了,有的128bit或更長。生成的ID越長,一般意味着發生重複的概率更低,但在實際系統中,往往是ID越短越好。

snowflake算法

當我們要設計一個分佈式ID算法時,如果要保證ID唯一性(不會發生重複),ID生成算法就必須有一定的歷史記憶功能,知道以前生產過哪些ID,以便今後不再產生,即ID的值應該是一個單調遞增(或單調遞減)的過程。這樣就能保證新產生的一定不是歷史中產生過的ID。滿足單調遞增的可以是自增序列,但是自增序列有個問題是一旦重啓,歷史就消失了,所以自增序列可以放到整個id值得尾部,將時戳放到高位值得地方,這樣重啓後,時間增加,整個值能夠保證遞增。這是一種解決問題的思路,當然不是唯一的思路。snowflake算法基本就可以這麼理解。

雪花算法(Snowflake)是twitter公司內部分佈式項目採用的ID生成算法,共8字節正好一個long整型就可以表示了。
image
其中:最高位0表示是一個正數,41bit時間戳,以ms爲單位可以表示math.pow(2,41)/(365*24*3600*1000)=69.7305700010147169年的時間,在使用時往往會設定一個基址時間,以這個基址時間爲0開始時間的計算。10bit工作機器id可以表示1k多機器,大多數場景已足夠。12bit序列號,每毫秒可產生4096個序列號,理論上性能上限爲409.6w tps/s。

snowflake算法的最缺點就是唯一性嚴重依賴於時間,若發生時間回調,則可能會發生重複,此時唯一性依賴於最後的12bit序列號,如果回調時間內,12bit序列號沒有發生重複,則安全,如果12bit序列號發生了重複,則不保證ID唯一。

解決時間這個問題,網絡上有NTP時間同步的解決方案。局域網網絡上利用NTP時間同步精度在1~50ms左右,精度爲毫秒級,廣域網可能在秒級。影響其精度最大的因素是網絡因素,主要需要計算報文往返時間差,其最大的誤差不超過網絡延時。利用NTP雖然能同步時間,但是同步時間時很可能發生時間回調,這是我們不願意看到的。

snowflake算法延伸

有沒有辦法解決snowflake算法中時間回調這一缺陷呢?有這麼幾種可能的解決辦法,其中的一個解決辦法其主要思想是如果時間發生回調,就更改機器workid,這個workid是單獨一個集合,不與現有的wordid集合有交集。比如,現有機器500臺,分配機器ID時全部分配爲奇數ID(1,3,5,… , 499),如果某一機器ID時間發生回調,就新分配一個機器ID(如果現有ID是奇數就加1,如果是偶數就減1),這樣,即時時間回調,新的機器ID仍能保證唯一性,缺點就是連續發生2次以上回調就無法保證唯一性了。

還可參考美團的解決方案Leaf

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章