系統唯一id是設計乙個系統的時候常常會遇到的問題,也常常為這個問題而糾結。
生成id的方法有很多,適應不同的場景、需求以及效能要求。所以有些比較複雜的系統會有多個id生成的策略。
(1)全域性唯一性:不能出現重複的id號,既然是唯一標識,這是最基本的要求;
(2)粗略有序:如果在分布式環境中做到完全有序,需要用到鎖等,考慮到效能,採用粗略有序,具體分為秒級有序和毫秒級有序;
(3)可反解:即生成id服務提供反解方法,這樣在儲存時就能以十進位制儲存,省下傳統timestamp類字段的占用空間了;
(4)可伸縮:中心發布模式時可以進行集群部署,這樣在生成id裡就必須包含機器id;
(5)趨勢遞增:在mysql innodb引擎中使用的是聚集索引,由於多數rdbms使用b-tree的資料結構來儲存索引資料,在主鍵的選擇上我們應該盡量使用有序的主鍵保證寫入效能;
針對主庫單點,如果有個多個master庫,則每個master庫設定的起始數字不一樣,步長一樣(可以使master的個數)。比如:master1生成的是1,4,7,10,master2生成的是2,5,8,11,master3生成的是3,6,9,12。這樣就可以有效生成集群中的唯一id,也可以大大降低id生成資料庫操作的負載。
優點:(1)最常見的方式,利用資料庫,全資料庫唯一,簡單,**方便,效能可以接受
(2)數字id天然排序,對分頁或需要排序的結果很有幫助
缺點:(1)不同資料庫語法和實現不同,資料庫遷移的時候或多資料庫版本支援的時候需要處理
(2)在單個資料庫或讀寫分離或一主多從的情況下,只有乙個主庫可以生成,有單點故障的風險
(3)在效能達不到要求的情況下,比較難於擴充套件
(4)如果遇到多個系統需要合併或者涉及到資料遷移會相當痛苦
(5)分表分庫的時候有麻煩
優點:(1)常見的方式,可以利用資料庫也可以利用程式生成,一般來說全球唯一,簡單,**方便
(2)生成id效能非常好,基本不會有效能問題
(3)全球唯一,在遇見資料遷移,系統資料合併或資料庫變更等情況下,可以從容應對
缺點:(1)沒有排序,無法保證趨勢遞增
(2)uuid往往是使用字串儲存,查詢的效率比較低
(3)儲存空間比較大,如果是海量資料庫,就需要儲存量的問題
(4)傳輸資料量大
(5)不可讀
當使用資料庫來生成id效能不夠要求的時候,可以嘗試用redis來生成id。
snowflake是twitter開源的分布式id生成演算法,結果是乙個long型的id。其核心思想是:
使用41bit作為毫秒數
10bit作為機器的id(5個bit是資料中心,5個bit的機器id)
12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生4096個id)
最後還有乙個符號位,永遠是0。
優點:(1)不依賴資料庫,靈活方便,且效能優於資料庫
(2)id按照時間在單機上是遞增的
缺點:(1)在單機上是遞增的,但是由於涉及到分布式環境中,每台機器上的時鐘不可能完全同步,也許有時候也會出現不是全域性遞增的情況。
zookeeper主要通過其znode資料版本來生成序列號,可以生成32位和64位的資料版本號,客戶端可以使用這個版本號來作為唯一的序列號。
很少會使用zookeeper來生成唯一id,主要是由於依賴zookeeper,並且是多步調用api,如果在競爭較大的情況下,需要考慮使用分布式鎖。因此,效能在高併發的分布式環境中,也不甚理想。
mongodb的objectid和snowflake演算法類似。它涉及成輕量級,不同的機器都能用全域性唯一的同種方法方便地生成它。mongodb從一開始就設計用來作為分布式資料庫,處理多個節點是乙個核心要求,使其在分布式環境中要容易生成得多。
前4個位元組是從標準紀元開始的時間戳,單位是秒。時間戳和隨後的5個位元組組合起來,提供了秒級別的唯一性。由於時間戳在前,意味著objectid大致會按照插入的順序排列。這對於某些方面很有用,如將其作為索引提高效率。這4個位元組也隱含了文件建立時間。絕大多數客戶端類庫都會公開乙個方法從objectid獲取這個資訊。
接下來3位元組是所在主機的唯一識別符號。通常是主機名的雜湊值,這樣就可以確保不同主機生成不同的objectid,不產生衝突。
接下來2個位元組來自產生objectid的程序識別符號(pid),為了確保在同一臺機器上併發的多個程序產生的objectid是唯一的。
前9位元組保證了同一秒中不同機器不同程序產生的objectid是唯一的,後3位元組就是乙個自動增加的計數器,確保相同程序同一秒產生的objectid也是不一樣的。同一秒鐘最多允許每個程序擁有16777216個不同的objectid。
分布式系統之所以難,很重要的原因之一是「沒有乙個全域性時鐘,難以保證絕對的時序」,要想保證絕對的時序,還是只能使用單點服務,用本地時鐘保證「絕對時序」。資料庫寫壓力大,是因為每次生成id都訪問了資料庫,可以使用批量的方式降低資料庫寫壓力。
資料庫中只儲存當前id的最大值,例如0。id生成服務假設每次批量拉取6個id,服務訪問資料庫,將當前id的最大值修改為5,這樣應用訪問id生成服務索要id,id生成服務不需要每次訪問資料庫,就能依次派發0,1,2,3,4,5這些id了,當id發完後,再將id的最大值修改為11,就能再次派發6,7,8,9,10,11這些id了,於是資料庫的壓力就降低到原來的1/6了。
優點:
(1)保證了id生成的絕對遞增有序
(2)大大的降低了資料庫的壓力,id生成可以做到每秒生成幾萬幾十萬個
缺點:
(1)服務仍然是單點
(2)如果服務掛了,服務重啟起來之後,繼續生成id可能會不連續,中間出現空洞(服務記憶體是儲存著0,1,2,3,4,5,資料庫中max-id是5,分配到3時,服務重啟了,下次會從6開始分配,4和5就成了空洞,不過這個問題也不大)
(3)雖然每秒可以生成幾萬幾十萬個id,但畢竟還是有效能上限,無法進行水平擴充套件
改進方法:
單點服務的常用高可用優化方案是「備用服務」,也叫「影子服務」,所以我們能用以下方法優化上述缺點(1)。對外提供的服務是主服務,有乙個影子服務時刻處於備用狀態,當主服務掛了的時候影子服務頂上。這個切換的過程對呼叫方是透明的,可以自動完成,常用的技術是vip+keepalived,具體就不在這裡展開。
分布式全域性ID生成方案
1 背景 分布式架構下,唯一序列號生成是我們在設計乙個系統,尤其是資料庫使用分庫分表的時候常常會遇見的問題。當分成若干sharding表後,如何能夠快速拿到乙個唯一序列號,是經常遇到的問題。在網際網路的業務系統中,涉及到各種各樣的id,如在支付系統中就會有支付id 退款id等。那一般生成id都有哪些...
分布式唯一ID的生成方案
不能出現重複的id,這是最基本的要求。有利於關聯式資料庫索引效能。既然是服務於分布式系統,為多個服務提供id服務,訪問壓力一定很大,所以需要保證高可用。如果id是有規律的,就容易被惡意操作,在一些場景下需要id無規則。核心思想是結合機器的網絡卡 當地時間 乙個隨機數來生成。優點 缺點 利用資料庫自增...
分布式全域性ID的幾種生成方案
前言 在網際網路的業務系統中,涉及到各種各樣的id,如在支付系統中就會有支付id 退款id等。那一般生成id都有哪些解決方案呢?特別是在複雜的分布式系統業務場景中,我們應該採用哪種適合自己的解決方案是十分重要的。下面我們一一來列舉一下,不一定全部適合,這些解決方案僅供你參考,或許對你有用。使用資料庫...