分布式唯一ID極簡教程

一运怖,題記

所有的業(yè)務系統(tǒng),都有生成ID的需求夏伊,如訂單id摇展,商品id,文章ID等溺忧。這個ID會是數(shù)據(jù)庫中的唯一主鍵咏连,在它上面會建立聚集索引!

閱讀本文鲁森,建議大家已經掌握了扎實的互聯(lián)網技術祟滴,可參考:互聯(lián)網技術清單

ID生成的核心需求有兩點:

全局唯一

趨勢有序

二,為什么要全局唯一歌溉?

著名的例子就是身份證號碼垄懂,身份證號碼確實是對人唯一的骑晶,然而一個人是可以辦理多個身份證的,例如你身份證丟了草慧,又重新補辦了一張桶蛔,號碼不變。

問題來了漫谷,因為系統(tǒng)是按照身份證號碼做唯一主鍵的仔雷。此時,如果身份證是被盜的情況下舔示,你是沒有辦法在系統(tǒng)里面注銷的碟婆,因為新舊2個身份證的“主鍵”都是身份證號碼。

也就是說惕稻,舊的身份證仍然逍遙在外竖共,完全有效。這個時候俺祠,還好有一個身份證有效時間的東西公给,只有靠身份證有效期來辨識了。不過锻煌,這就是現(xiàn)在這么多銀行妓布,電信詐騙的由來,撿到一張身份證宋梧,去很多銀行匣沼,手機,酒店都可以使用捂龄!身份證缺乏注銷機制释涛!

所以,經驗告訴我們倦沧。不要相信自己的直覺唇撬,業(yè)務上所謂的唯一往往都是不靠譜的,經不起時間的考研的展融。所以需要單獨設置一個和業(yè)務無關的主鍵窖认,專業(yè)術語叫做代理主鍵(surrogate key)。

這也是為什么數(shù)據(jù)庫設計范式告希,唯一主鍵是第一范式扑浸!

三,為什么要趨勢有序

以mysql為例燕偶,InnoDB引擎表是基于B+樹的索引組織表(IOT)喝噪;每個表都需要有一個聚集索引(clustered index);所有的行記錄都存儲在B+樹的葉子節(jié)點(leaf pages of the tree)指么;基于聚集索引的增酝惧、刪榴鼎、改、查的效率相對是最高的晚唇;如下圖:

如果我們定義了主鍵(PRIMARY KEY)巫财,那么InnoDB會選擇其作為聚集索引;

如果沒有顯式定義主鍵缺亮,則InnoDB會選擇第一個不包含有NULL值的唯一索引作為主鍵索引翁涤;

如果也沒有這樣的唯一索引桥言,則InnoDB會選擇內置6字節(jié)長的ROWID作為隱含的聚集索引(ROWID隨著行記錄的寫入而主鍵遞增萌踱,這個ROWID不像ORACLE的ROWID那樣可引用,是隱含的)号阿。

綜上總結并鸵,如果InnoDB表的數(shù)據(jù)寫入順序能和B+樹索引的葉子節(jié)點順序一致的話,這時候存取效率是最高的扔涧,也就是下面這幾種情況的存取效率最高

使用自增列(INT/BIGINT類型)做主鍵园担,這時候寫入順序是自增的,和B+數(shù)葉子節(jié)點分裂順序一致枯夜;

該表不指定自增列做主鍵弯汰,同時也沒有可以被選為主鍵的唯一索引(上面的條件),這時候InnoDB會選擇內置的ROWID作為主鍵湖雹,寫入順序和ROWID增長順序一致咏闪;

除此以外,如果一個InnoDB表又沒有顯示主鍵摔吏,又有可以被選擇為主鍵的唯一索引鸽嫂,但該唯一索引可能不是遞增關系時(例如字符串、UUID征讲、多字段聯(lián)合唯一索引的情況)据某,該表的存取效率就會比較差。)

這就是為什么我們的分布式ID一定要是趨勢遞增的诗箍!那么在開發(fā)當中癣籽,面對這種分布式ID需求,常見的處理方案有哪些呢滤祖?

四筷狼,數(shù)據(jù)庫自增長序列或字段

最常見的方式。利用數(shù)據(jù)庫氨距,全數(shù)據(jù)庫唯一桑逝。

優(yōu)點:

1)簡單,代碼方便俏让,性能可以接受楞遏。

2)數(shù)字ID天然排序茬暇,對分頁或者需要排序的結果很有幫助。

缺點:

1)不同數(shù)據(jù)庫語法和實現(xiàn)不同寡喝,數(shù)據(jù)庫遷移的時候或多數(shù)據(jù)庫版本支持的時候需要處理糙俗。

2)在單個數(shù)據(jù)庫或讀寫分離或一主多從的情況下,只有一個主庫可以生成预鬓。有單點故障的風險巧骚。

3)在性能達不到要求的情況下,比較難于擴展格二。

4)如果遇見多個系統(tǒng)需要合并或者涉及到數(shù)據(jù)遷移會相當痛苦劈彪。

5)分表分庫的時候會有麻煩。

優(yōu)化方案:

1)針對主庫單點顶猜,如果有多個Master庫沧奴,則每個Master庫設置的起始數(shù)字不一樣,步長一樣长窄,可以是Master的個數(shù)滔吠。比如:Master1 生成的是 1,4挠日,7疮绷,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12嚣潜。這樣就可以有效生成集群中的唯一ID冬骚,也可以大大降低ID生成數(shù)據(jù)庫操作的負載。

五郑原,UUID

常見的方式唉韭。可以利用數(shù)據(jù)庫也可以利用程序生成犯犁,一般來說全球唯一属愤。

優(yōu)點:

1)簡單,代碼方便酸役。

2)生成ID性能非常好住诸,基本不會有性能問題。

3)全球唯一涣澡,在遇見數(shù)據(jù)遷移贱呐,系統(tǒng)數(shù)據(jù)合并,或者數(shù)據(jù)庫變更等情況下入桂,可以從容應對奄薇。

缺點:

1)沒有排序,無法保證趨勢遞增抗愁。

2)UUID往往是使用字符串存儲馁蒂,查詢的效率比較低呵晚。

3)存儲空間比較大,如果是海量數(shù)據(jù)庫沫屡,就需要考慮存儲量的問題饵隙。

4)傳輸數(shù)據(jù)量大

5)不可讀。

六沮脖,Redis生成ID

當使用數(shù)據(jù)庫來生成ID性能不夠要求的時候金矛,我們可以嘗試使用Redis來生成ID。這主要依賴于Redis是單線程的勺届,所以也可以用生成全局唯一的ID驶俊。可以用Redis的原子操作 INCR和INCRBY來實現(xiàn)涮因。

可以使用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進行累加路召。

優(yōu)點:

1)不依賴于數(shù)據(jù)庫,靈活方便波材,且性能優(yōu)于數(shù)據(jù)庫股淡。

2)數(shù)字ID天然排序,對分頁或者需要排序的結果很有幫助廷区。

缺點:

1)如果系統(tǒng)中沒有Redis唯灵,還需要引入新的組件,增加系統(tǒng)復雜度隙轻。

2)需要編碼和配置的工作量比較大埠帕。

七忌傻,twitter

twitter在把存儲系統(tǒng)從MySQL遷移到Cassandra的過程中由于Cassandra沒有順序ID生成機制,于是自己開發(fā)了一套全局唯一ID生成服務:Snowflake搞监。

1 41位的時間序列(精確到毫秒水孩,41位的長度可以使用69年)

2 10位的機器標識(10位的長度最多支持部署1024個節(jié)點)

3 12位的計數(shù)順序號(12位的計數(shù)順序號支持每個節(jié)點每毫秒產生4096個ID序號) 最高位是符號位,始終為0琐驴。

優(yōu)點:

高性能俘种,低延遲;獨立的應用绝淡;

按時間有序宙刘。

缺點:

需要獨立的開發(fā)和部署。

強依賴時鐘,如果主機時間回撥,則會造成重復ID,會產生

ID雖然有序,但是不連續(xù)

原理

八牢酵,MongoDB的ObjectId

MongoDB的ObjectId和snowflake算法類似悬包。它設計成輕量型的,不同的機器都能用全局唯一的同種方法方便地生成它馍乙。MongoDB 從一開始就設計用來作為分布式數(shù)據(jù)庫布近,處理多個節(jié)點是一個核心要求。使其在分片環(huán)境中要容易生成得多丝格。

ObjectId使用12字節(jié)的存儲空間撑瞧,其生成方式如下:

|0|1|2|3|4|5|6 |7|8|9|10|11|

|時間戳 |機器ID|PID|計數(shù)器 |

前四個字節(jié)時間戳是從標準紀元開始的時間戳,單位為秒显蝌,有如下特性:

1 時間戳與后邊5個字節(jié)一塊预伺,保證秒級別的唯一性;

2 保證插入順序大致按時間排序曼尊;

3 隱含了文檔創(chuàng)建時間酬诀;

4 時間戳的實際值并不重要,不需要對服務器之間的時間進行同步(因為加上機器ID和進程ID已保證此值唯一骆撇,唯一性是ObjectId的最終訴求)瞒御。

機器ID是服務器主機標識,通常是機器主機名的散列值艾船。

同一臺機器上可以運行多個mongod實例葵腹,因此也需要加入進程標識符PID。

前9個字節(jié)保證了同一秒鐘不同機器不同進程產生的ObjectId的唯一性屿岂。后三個字節(jié)是一個自動增加的計數(shù)器(一個mongod進程需要一個全局的計數(shù)器)践宴,保證同一秒的ObjectId是唯一的。同一秒鐘最多允許每個進程擁有(256^3 = 16777216)個不同的ObjectId爷怀。

總結一下:時間戳保證秒級唯一阻肩,機器ID保證設計時考慮分布式,避免時鐘同步,PID保證同一臺服務器運行多個mongod實例時的唯一性烤惊,最后的計數(shù)器保證同一秒內的唯一性(選用幾個字節(jié)既要考慮存儲的經濟性乔煞,也要考慮并發(fā)性能的上限)。

"_id"既可以在服務器端生成也可以在客戶端生成柒室,在客戶端生成可以降低服務器端的壓力渡贾。

九,類snowflake算法

國內有很多廠家基于snowflake算法進行了國產化雄右,例如

百度的uid-generator:

https://github.com/baidu/uid-generator

美團Leaf:

https://github.com/zhuzhong/idleaf

基本是對snowflake的進一步優(yōu)化空骚,比如解決時鐘 回撥問題千元!

十圈盔,總結

總體而言,分布式唯一ID需要滿足以下條件:

高可用性:不能有單點故障付魔。

全局唯一性:不能出現(xiàn)重復的ID號逢渔,既然是唯一標識肋坚,這是最基本的要求。

趨勢遞增:在MySQL InnoDB引擎中使用的是聚集索引肃廓,由于多數(shù)RDBMS使用B-tree的數(shù)據(jù)結構來存儲索引數(shù)據(jù)智厌,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能。

時間有序:以時間為序亿昏,或者ID里包含時間峦剔。這樣一是可以少一個索引,二是冷熱數(shù)據(jù)容易分離角钩。

分片支持:可以控制ShardingId。比如某一個用戶的文章要放在同一個分片內呻澜,這樣查詢效率高递礼,修改也容易。

單調遞增:保證下一個ID一定大于上一個ID羹幸,例如事務版本號脊髓、IM增量消息、排序等特殊需求栅受。

長度適中:不要太長将硝,最好64bit。使用long比較好操作屏镊,如果是96bit依疼,那就要各種移位相當?shù)牟环奖悖€有可能有些組件不能支持這么大的ID而芥。

信息安全:如果ID是連續(xù)的律罢,惡意用戶的扒取工作就非常容易做了,直接按照順序下載指定URL即可棍丐;如果是訂單號就更危險了误辑,競爭對手可以直接知道我們一天的單量沧踏。所以在一些應用場景下,會需要ID無規(guī)則巾钉、不規(guī)則翘狱。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砰苍,隨后出現(xiàn)的幾起案子盒蟆,更是在濱河造成了極大的恐慌,老刑警劉巖师骗,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件历等,死亡現(xiàn)場離奇詭異,居然都是意外死亡辟癌,警方通過查閱死者的電腦和手機寒屯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來黍少,“玉大人寡夹,你說我怎么就攤上這事〕е茫” “怎么了菩掏?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昵济。 經常有香客問我智绸,道長,這世上最難降的妖魔是什么访忿? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任瞧栗,我火速辦了婚禮,結果婚禮上海铆,老公的妹妹穿的比我還像新娘迹恐。我一直安慰自己,他們只是感情好卧斟,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布殴边。 她就那樣靜靜地躺著,像睡著了一般珍语。 火紅的嫁衣襯著肌膚如雪锤岸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天廊酣,我揣著相機與錄音能耻,去河邊找鬼。 笑死,一個胖子當著我的面吹牛晓猛,可吹牛的內容都是我干的饿幅。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼戒职,長吁一口氣:“原來是場噩夢啊……” “哼栗恩!你這毒婦竟也來了?” 一聲冷哼從身側響起洪燥,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤磕秤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捧韵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體市咆,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年再来,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒙兰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡芒篷,死狀恐怖搜变,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情针炉,我是刑警寧澤挠他,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站篡帕,受9級特大地震影響殖侵,放射性物質發(fā)生泄漏。R本人自食惡果不足惜赂苗,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一愉耙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拌滋,春花似錦、人聲如沸猜谚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魏铅。三九已至昌犹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間览芳,已是汗流浹背斜姥。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铸敏。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓缚忧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杈笔。 傳聞我的和親對象是個殘疾皇子闪水,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350