「惟一ID」在應用程序中是一個很常見的需求,它用於惟一標識一個業務對象、一個資源、或者一個消息等等。在數據庫中,惟一ID通常是用來作爲一個數據的主鍵。看過前面介紹MySQL索引原理的文章的朋友應該知道,主鍵對於數據庫的重要性不言而喻。算法
在單機場景下,要獲得一個全局惟一的ID是很是容易的,你可使用數據庫的自增功能。數據庫
可是若是在分佈式的場景下,想要構建構建一個全局惟一的ID就有些不同。由於分佈式系統通常是高併發場景,那天然不適合使用單機數據庫的自增功能了。若是你的技術選型剛好是MySQL這樣的「非分佈式數據庫」,那就得參考一下業界常見的分佈式全局惟一ID生成策略了。緩存
UUID全稱是Universally Unique Identifier,翻譯過來叫通用惟一識別碼。標準型式包含32個16進制數字,以連字號分爲五段,形式爲8-4-4-4-12的36個字符,示例:9628f6e9-70ca-45aa-9f7c-77afe0d26e05
,到目前爲止業界一共有5種方式生成UUID,詳情見IETF發佈的UUID規範《A Universally Unique IDentifier (UUID) URN Namespace》,分別稱爲UUID的5個版本。服務器
在JDK自帶的UUID
類能夠產生版本3和版本4的UUID。因此這裏簡單介紹一下版本3和版本4的UUID的生成方式。數據結構
也有在線生成UUID的網站,若是你的項目上用到了UUID,能夠用來生成臨時的測試數據。www.uuidgenerator.net/併發
UUID的優點是實現起來很簡單,用JDK原生的API便可獲得。劣勢是與基於b-Tree引擎的數據庫的主鍵索引策略不太符合,不適合做爲高性能需求的場景下的數據庫主鍵。分佈式
都用分佈式了,多半要上個緩存。用緩存的話,可能會使用Redis。Redis的INCR
函數在單機上是原子操做,能夠保證惟一且遞增。函數
單機Redis可能沒法支撐高併發。而若是使用Redis集羣,如何保證ID的惟一性呢?可使用步長的方式。好比有5個Redis節點組成的集羣,它們生成的ID分別爲:高併發
A: 1,6,11,16,21性能
B: 2,7,12,17,22
C: 3,8,13,18,23
D: 4,9,14,19,24
E: 5,10,15,20,25
Twitter利用Zookeeper實現了一個全局ID生成的服務snowflake。其生成ID的數據結構以下圖所示:
共64位,正好對應Java中的long
型,第一個符號位不用,而後41位用於表示時間戳。後續10位用來表示節點的id,若是是多機房節點,能夠劃分前5位用來表示機房id,後5位用來表示每一個機房下的機器的id。最後12位用來表示序列號,這樣能夠作到同一毫秒,同一機器生成多個id,12位算下來最多支持4096個。
這裏的時間戳並非當前時間的Time Stamp,而是當前時間相對於起始時間的差值。若是基於毫秒來計算的話,41位大約能夠用69年。
snowflake算法有許多變種。能夠根據本身的實際狀況調整位數的分配,好比時間戳佔42位,機器id佔9位。42位時間戳就能夠用138年等。
百度的UidGenerator和美團的Leaf都是基於snowflake的變種。
snowflake是一種比較好的生成ID方式,保證全局惟一,且支持高併發。並且是long
類型的,趨勢遞增,能夠用於數據庫主鍵。還能夠根據時間來排序。
但也有其缺點,就是強依賴服務器的時鐘,若是服務器的時鐘出現回撥(好比閏秒或者NTP同步),就會致使ID重複。
美團的Leaf解決了時鐘回撥的問題,具體流程以下圖,能夠了解一下:
固然,還有一些其餘的ID生成方案,好比:
滴滴:時間+起點編號+車牌號
淘寶訂單:時間戳+用戶ID
其餘電商:時間戳+下單渠道+用戶ID,有的會加上訂單第一個商品的ID。
MongoDB的ID:也算是類snowflake的一種。經過「時間+機器碼+pid+inc」共12個字節,4+3+2+3的方式最終標識成一個24長度的十六進制字符。
若是不用於數據庫主鍵,建議直接用UUID。
若是想要用來作數據庫主鍵,又沒有使用分佈式數據庫(好比TiDB、MongoDB等),能夠考慮使用snowflake算法,建議使用美團的Leaf。
數據庫中間件sharding-jdbc的分佈式ID採用twitter開源的snowflake算法,不須要依賴任何第三方組件,這樣其擴展性和維護性獲得最大的簡化;可是snowflake算法的缺陷(強依賴時間,若是時鐘回撥,就會生成重複的ID),sharding-jdbc沒有給出解決方案,若是用戶想要強化,須要自行擴展。
認真寫文章,用心作分享。
我的網站:yasinshaw.com
公衆號:xy的技術圈