系統唯一id是我們在設計乙個系統的時候常常會遇見的問題,也常常為這個問題而糾結。生成id的方法有很多,適應不同的場景、需求以及效能要求。所以有些比較複雜的系統會有多個id生成的策略。下面就介紹一些常見的id生成策略。
最常見的方式。利用資料庫,全資料庫唯一。
優點:1)簡單,**方便,效能可以接受。
2)數字id天然排序,對分頁或者需要排序的結果很有幫助。
缺點:1)不同資料庫語法和實現不同,資料庫遷移的時候或多資料庫版本支援的時候需要處理。
2)在單個資料庫或讀寫分離或一主多從的情況下,只有乙個主庫可以生成。有單點故障的風險。
3)在效能達不到要求的情況下,比較難於擴充套件。
4)如果遇見多個系統需要合併或者涉及到資料遷移會相當痛苦。
5)分表分庫的時候會有麻煩。
優化方案:
1)針對主庫單點,如果有多個master庫,則每個master庫設定的起始數字不一樣,步長一樣,可以是master的個數。比如:master1 生成的是 1,4,7,10,master2生成的是2,5,8,11 master3生成的是 3,6,9,12。這樣就可以有效生成集群中的唯一id,也可以大大降低id生成資料庫操作的負載。
常見的方式。可以利用資料庫也可以利用程式生成,一般來說全球唯一。
優點:1)簡單,**方便。
2)生成id效能非常好,基本不會有效能問題。
3)全球唯一,在遇見資料遷移,系統資料合併,或者資料庫變更等情況下,可以從容應對。
缺點:1)沒有排序,無法保證趨勢遞增。
2)uuid往往是使用字串儲存,查詢的效率比較低。
3)儲存空間比較大,如果是海量資料庫,就需要考慮儲存量的問題。
4)傳輸資料量大
5)不可讀。
1)為了解決uuid不可讀,可以使用uuid to int64的方法。及
/// /// 根據guid獲取唯一數字序列/// public static long guidtoint64()
2)為了解決uuid無序的問題,nhibernate在其主鍵生成方式中提供了comb演算法(combined guid/timestamp)。保留guid的10個位元組,用另6個位元組表示guid生成的時間(datetime)。
/// /// generate a new using the comb algorithm. /// private guid generatecomb()
用上面的演算法測試一下,得到如下的結果:作為比較,前面3個是使用comb演算法得出的結果,最後12個字串是時間序(統一毫秒生成的3個uuid),過段時間如果再次生成,則12個字串會比圖示的要大。後面3個是直接生成的guid。
如果想把時間序放在前面,可以生成後改變12個字串的位置,也可以修改演算法類的最後兩個array.copy。
當使用資料庫來生成id效能不夠要求的時候,我們可以嘗試使用redis來生成id。這主要依賴於redis是單執行緒的,所以也可以用生成全域性唯一的id。可以用redis的原子操作 incr和incrby來實現。
可以使用redis集群來獲取更高的吞吐量。假如乙個集群中有5臺redis。可以初始化每台redis的值分別是1,2,3,4,5,然後步長都是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
這個,隨便負載到哪個機確定好,未來很難做修改。但是3-5臺伺服器基本能夠滿足器上,都可以獲得不同的id。但是步長和初始值一定需要事先需要了。使用redis集群也可以方式單點故障的問題。
另外,比較適合使用redis來生成每天從0開始的流水號。比如訂單號=日期+當日自增長號。可以每天在redis中生成乙個key,使用incr進行累加。
優點:1)不依賴於資料庫,靈活方便,且效能優於資料庫。
2)數字id天然排序,對分頁或者需要排序的結果很有幫助。
缺點:1)如果系統中沒有redis,還需要引入新的元件,增加系統複雜度。
2)需要編碼和配置的工作量比較大。
snowflake是twitter開源的分布式id生成演算法,結果是乙個long型的id。其核心思想是:使用41bit作為毫秒數,10bit作為機器的id(5個bit是資料中心,5個bit的機器id),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 id),最後還有乙個符號位,永遠是0。具體實現的**可以參看
c#**如下:
/// /// from: /// an object that generates ids. /// this is broken into a separate class in case /// we ever want to support multiple worker threads /// per process /// public class idworker catch (exception e) console.writeline("done"); } private static void dotestidwoker(idworker idworker, hashset set) thread.sleep(1); } }
snowflake演算法可以根據自身專案的需要進行一定的修改。比如估算未來的資料中心個數,每個資料中心的機器數以及統一毫秒可以能的併發數來調整在演算法中所需要的bit數。
優點:1)不依賴於資料庫,靈活方便,且效能優於資料庫。
2)id按照時間在單機上是遞增的。
缺點:1)在單機上是遞增的,但是由於涉及到分布式環境,每台機器上的時鐘不可能完全同步,也許有時候也會出現不是全域性遞增的情況。
zookeeper主要通過其znode資料版本來生成序列號,可以生成32位和64位的資料版本號,客戶端可以使用這個版本號來作為唯一的序列號。很少會使用zookeeper來生成唯一id。主要是由於需要依賴zookeeper,並且是多步調用api,如果在競爭較大的情況下,需要考慮使用分布式鎖。因此,效能在高併發的分布式環境下,也不甚理想。
mongodb的objectid和snowflake演算法類似。它設計成輕量型的,不同的機器都能用全域性唯一的同種方法方便地生成它。mongodb 從一開始就設計用來作為分布式資料庫,處理多個節點是乙個核心要求。使其在分片環境中要容易生成得多。
其格式如下:
前4 個位元組是從標準紀元開始的時間戳,單位為秒。時間戳,與隨後的5 個位元組組合起來,提供了秒級別的唯一性。由於時間戳在前,這意味著objectid 大致會按照插入的順序排列。這對於某些方面很有用,如將其作為索引提高效率。這4 個位元組也隱含了文件建立的時間。絕大多數客戶端類庫都會公開乙個方法從objectid 獲取這個資訊。接下來的3 位元組是所在主機的唯一識別符號。通常是機器主機名的雜湊值。這樣就可以確保不同主機生成不同的objectid,不產生衝突。為了確保在同一臺機器上併發的多個程序產生的objectid 是唯一的,接下來的兩位元組來自產生objectid 的程序識別符號(pid)。前9 位元組保證了同一秒鐘不同機器不同程序產生的objectid 是唯一的。後3 位元組就是乙個自動增加的計數器,確保相同程序同一秒產生的objectid 也是不一樣的。同一秒鐘最多允許每個程序擁有2563(16 777 216)個不同的objectid。
常見的分布式唯一ID方案
最近看乙個新系統,發現裡面有很多場景用到唯一id,便蒐羅了一下常見的方案。對於分布式id,需要滿足下面的基本要求 全域性唯一 趨勢遞增 uuid universally unique identifier 全域性唯一識別符號,定義為乙個字串主鍵,採用32位數字組成,編碼採用16進製制,定義了在時間和...
php 生成分布式唯一ID
生成唯一id,主要針對的是分布式服務 md5 伺服器mac位址 時間戳 程序id 自增id 當應用於api時,由於 nginx 程序id一直在變化,所以自增id可以使用隨機數,同時避免檔案鎖影響api能力 ip位址 function getipaddr return preg replace ipa...
分布式唯一ID生成服務
snservice是一款基於分布式的唯一id生成服務,主要用於提供大數量業務資料建立唯一id的需要 服務提供最低10k s的唯一id請求處理.如果你部署服務的cpu資源達到4核的情況下那該服務最低可以提供100k s的請求處理能力.服務支援部署到linux mono 3.2.3和windows ne...