讓人眼前一亮的算法------唯一ID生成器snowflake

分佈式全局唯一ID生成器

很多場景需要使用全局唯一ID,用來標識唯一一條消息,唯一一筆交易,唯一一個用戶,唯一一張圖片等等。
傳統數據庫表的自增主鍵是很簡單的一種實現方式,前提是你沒有分庫,也沒有分表,如果你分表了,id就會重複,失去唯一性:

當然,通過數據庫的一些配置,使不同的分表以不同的起始值但是相同的步長自增,可以繞開這個限制:

 

但是,如果哪天發現數據量增大,原先的分表不夠用了,需要擴容,這時,就很麻煩很難搞了。
所以,如果存在一種和業務數據無關的全局唯一ID生成器就好了。開動腦筋,我們能想到的有以下幾種:

時間戳

用時間做唯一id,這個在併發比較高或者分佈式環境中基本不可行,統一時間生成的id是重複的,不滿足全局唯一。

利用數據庫自增

依然利用數據庫產生自增id,保證唯一性,和開頭提到的不同之處是,單獨使用一張(或固定幾張)數據庫表專門用來產生自增id,與業務無關,後續不再重新分表,數據量大時,可以刪除早一些時候產生的數據。
這樣做的好處是,實現簡單,容易理解。
不好的地方是,嚴重依賴數據庫,id產生速率受數據庫性能以及連接數據庫的網絡影響。

利用Redis原子操作incrBy

好處:實現簡單,容易理解。
壞處:依賴Redis,且Redis需要持久化。

UUID/GUID

好處:使用非常簡單,不需要依賴其他設施。
壞處:太長,128bit,不適合做數據庫主鍵。

snowflake

通常情況下,用時間來表示是最簡單的,如果同一時間(毫秒)有很多請求進來怎麼辦?
時間戳後面拼接上一個數字,這個數字可以通過鎖控制每次遞增,每毫秒清零,重新開始遞增。

即便這樣,只是解決了單機的問題,如果是分佈式環境,不同的機器,還是可能產生一樣的id的,這怎麼解決?
在上述時間戳和數字的基礎上在拼接上機器的id,這樣就不會重複了。

不同的數據中心,機器id是可能重複的,怎麼搞?
再拼接上數據中心的id就行了。

最終產生的id是這個樣子的,時間戳,工作機器id,序列號可以根據實際需要調整長度(通常情況下不需要調整,完全夠用),總體64bit就行:

 

snowflake名字起得真好

雪花(snowflake)的形狀和算法的思想十分吻合,沿着主幹(時間戳),如果有重複,那麼分叉分出機器id,如果仍有重複,再分叉,分出序列號

 

好處與不足

snowflake有以下幾個特點:

算法簡單,不需要依靠額外組件

id可以直接靠算法在內存中產生,靠鎖控制併發,不需有諸如MySQL,Redis這樣的外部依賴,無維護成本。

長度合適

snowflake產生的id長度爲64bit,對應大多數語言的long類型,用於作爲數據庫唯一鍵建立索引時,也不會因爲長度過大影響性能。

趨勢遞增

snowflake產生的id並不是嚴格遞增的,而是趨勢遞增的。
這是因爲,當id生成器分佈式部署的時候,比如統一毫秒由不同機器產生的id,時間戳的部分肯定是一樣的,後面機器id的部分並不一定是遞增的。
舉個例子,有兩個機器,id分別是0和1,那麼同一毫秒內產生的id可能是這樣的順序:

從圖中可以看出,由於機器id的存在,在同1毫秒內產生的id並不一定是遞增的,但是因爲時間戳的存在,在毫秒間總體上id是遞增的。
所以總體上說snowflake產生的id是趨勢遞增的。

爲何追求遞增

爲何追求遞增?因爲遞增最大的優勢就是對磁盤IO是友好的。
熟悉磁盤結構的同學們都知道,隨機寫的效率是很慢的,因爲磁頭需要轉動到指定的位置,這個磁頭轉動的過程比起cpu或者內存來,完全不是一個數量級的,太慢太慢了,所以如果能儘可能的使數據靠近在一一起(遞增就能靠在一起),那麼就不需要頻繁的擡起磁頭,轉動磁盤,寫數據了,一路寫到底會快很多。
一些大型分佈式數據庫,比如HBase,ElasticSearch等,也都是利用順序寫這個特點提高數據的寫入性能的。

隱患

snowflake並不完美,因爲有一種情況,snowflake產生的id是有可能會出現重複的。
爲什麼會重複,我們再回頭看看snowflake產生的id的組成:時間戳+機器id+序列號。
這三部分,機器id可以不重複,序列號也可以做到不重複,那唯一可能重複的就是時間戳了。
什麼?時間怎麼回重複?時間明明是一直向前的,除非時間倒退,退回到之前的某個時間點,再次產生的id纔可能是重複的。
你說對了,人類感受的時間是不會倒退的,但是,機器上的時間都是時鐘,時鐘可能會因爲種種原因變慢了或者變快了,比如有一天你(或者機器上的時間同步器)發現有一臺機器的時鐘變快了,於是往回撥1秒,然後。。。 你懂的

時鐘的問題,一直都是老大難,某些對時間及其敏感的程序,甚至會考慮使用GPS上的原子鐘來做時鐘同步,或者,乾脆有土豪(某歌)直接在數據中心自己搞原子鐘,然並卵,時間同步時的網絡傳輸延遲、抖動,依然無解。
永遠都是隻能減小,無法消滅。

##最後
忍不住再誇一下算法的名字,snowflake,真是美妙。
 

發佈了170 篇原創文章 · 獲贊 63 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章