擴展篇之唯一ID生成總結

生成ID的基本要求

1.必須全局唯一,包括在分布式的環境下
2.一般都需要單調遞增,因為一般都會存到數據庫,而Innodb的特性就是將內容存儲在主鍵索引樹上的葉子節點,而且是從左往右,遞增的,所以考慮到數據庫性能,一般生成的id也最好是單調遞增.
3.可能還會需要無規則,因為如果使用唯一ID作為訂單號這種,為了不然別人知道一天的訂單量是多少,就需要這個規則.


常見唯一Id生成方案

UUID

數據庫生成

Leaf-segment數據庫方案

Leaf-snowflake方案


UUID

UUID(Universally Unique Identifier)的標準型式包含32個16進制數字,以連字號分為五段,形式為8-4-4-4-12的36個字符.
目前共有5種方式生成UUID,詳情見IETF發布的UUID規范 A Universally Unique IDentifier (UUID) URN Namespace

優點:

本地生成,沒有網絡消耗,性能很高

缺點:

①不易于存儲:UUID太長,16字節128位,通常以36長度的字符串表示,很多場景不適用。
②信息不安全:基于MAC地址生成UUID的算法可能會造成MAC地址泄露,這個漏洞曾被用于尋找梅麗莎病毒的制作者位置。
③ID作為主鍵時在特定的環境會存在一些問題,比如做DB主鍵的場景下,UUID就非常不適用:對MySQL索引不利:如果作為數據庫主鍵,在InnoDB引擎下,UUID的無序性可能會引起數據位置頻繁變動,嚴重影響性能。


雪花算法snowflake

把64-bit分別劃分成多段,分開來標示機器、時間


雪花算法.png

41-bit的時間可以表示(1L<<41)/(1000L360024*365)=69年的時間,10-bit機器可以分別表示1024臺機器。如果我們對IDC劃分有需求,還可以將10-bit分5-bit給IDC,分5-bit給工作機器。這樣就可以表示32個IDC,每個IDC下可以有32臺機器,可以根據自身需求定義。12個自增序列號可以表示2^12個ID,理論上snowflake方案的QPS約為409.6w/s,這種分配方式可以保證在任何一個IDC的任何一臺機器在任意毫秒內生成的ID都是不同的。

優點:

①毫秒數在高位,自增序列在低位,整個ID都是趨勢遞增的
②不依賴數據庫等第三方系統,以服務的方式部署,穩定性更高,生成ID的性能也是非常高的。
③可以根據自身業務特性分配bit位,非常靈活。

缺點:

強依賴機器時鐘,如果機器上時鐘回撥,會導致發號重復或者服務會處于不可用狀態。而多機下時鐘不同步是很正常的


數據庫生成

利用給字段設置auto_increment_increment和auto_increment_offset(表示步長,即每次自增多少)來保證ID自增,每次業務使用下列SQL讀寫MySQL得到ID號。

begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');SELECT LAST_INSERT_ID();
commit;

優點:

①非常簡單,利用現有數據庫系統的功能實現,成本小,有DBA專業維護。
②ID號單調自增,可以實現一些對ID有特殊要求的業務。

缺點:

①強依賴DB,當DB異常時整個系統不可用,屬于致命問題。配置主從復制可以盡可能的增加可用性,但是數據一致性在特殊情況下難以保證。主從切換時的不一致可能會導致重復發號。
②ID發號性能瓶頸限制在單臺MySQL的讀寫性能。

當然也可以在數據庫壓力不是很大的時候,批量生成加載至redis中,需要的時候直接從redis中獲取,同時監控,redis里的使用,達到一定百分比時觸發數據庫再生成一批導入至redis中.


Leaf-segment數據庫方案

原方案每次獲取ID都得讀寫一次數據庫,造成數據庫壓力大。改為利用proxy server批量獲取,每次獲取一個segment(step決定大小)號段的值。用完之后再去數據庫獲取新的號段,可以大大的減輕數據庫的壓力。
各個業務不同的發號需求用biz_tag字段來區分,每個biz-tag的ID獲取相互隔離,互不影響。如果以后有性能需求需要對數據庫擴容,不需要上述描述的復雜的擴容操作,只需要對biz_tag分庫分表就行。

表設計如下:

+-------------+--------------+------+-----+-------------------+-----------------------------+| Field       | Type         | Null | Key | Default           | Extra                       |
+-------------+--------------+------+-----+-------------------+-----------------------------+| biz_tag     | varchar(128) | NO   | PRI |                   |                             |
| max_id      | bigint(20)   | NO   |     | 1                 |                             |
| step        | int(11)      | NO   |     | NULL              |                             |
| desc        | varchar(256) | YES  |     | NULL              |                             |
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+

字段說明

biz_tag用來區分業務,max_id表示該biz_tag目前所被分配的ID號段的最大值,step表示每次分配的號段長度。原來獲取ID每次都需要寫數據庫,現在只需要把step設置得足夠大,比如1000。那么只有當1000個號被消耗完了之后才會去重新讀寫一次數據庫。讀寫數據庫的頻率從1減小到了1/step

Leaf-segment方案架構圖.png

test_tag在第一臺Leaf機器上是1-1000的號段,當這個號段用完時,會去加載另一個長度為step=1000的號段,假設另外兩臺號段都沒有更新,這個時候第一臺機器新加載的號段就應該是3001~4000。同時數據庫對應的biz_tag這條數據的max_id會從3000被更新成4000,更新號段的SQL語句如下:

Begin
UPDATE table SET max_id=max_id+step WHERE biz_tag=xxxSELECT tag, max_id, step FROM table WHERE biz_tag=xxx;
Commit

優點:

①Leaf服務可以很方便的線性擴展,性能完全能夠支撐大多數業務場景。
②ID號碼是趨勢遞增的8byte的64位數字,滿足上述數據庫存儲的主鍵要求。
③容災性高:Leaf服務內部有號段緩存,即使DB宕機,短時間內Leaf仍能正常對外提供服務。
④可以自定義max_id的大小,非常方便業務從原有的ID方式上遷移過來。

缺點:

①ID號碼不夠隨機,能夠泄露發號數量的信息,不太安全。
②TP999數據波動大,當號段使用完之后還是會hang在更新數據庫的I/O上,tg999數據會出現偶爾的尖刺。
③DB宕機會造成整個系統不可用。

優化方案

對于第二個缺點,可以做如下優化,不需要在DB取號段的時候阻塞請求線程,即當號段消費到某個點時就異步的把下一個號段加載到內存中。而不需要等到號段用盡的時候才去更新號段。這樣做就可以很大程度上的降低系統的TP999指標。如下圖所示:

Leaf預加載優化.png

服務內部有兩個號段緩存區segment。當前號段已下發10%時,如果下一個號段未更新,則另啟一個更新線程去更新下一個號段。當前號段全部下發完后,如果下個號段準備好了則切換到下個號段為當前segment接著下發,循環往復。

每個biz-tag都有消費速度監控,通常推薦segment長度設置為服務高峰期發號QPS的600倍(10分鐘),這樣即使DB宕機,Leaf仍能持續發號10-20分鐘不受影響。
每次請求來臨時都會判斷下個號段的狀態,從而更新此號段,所以偶爾的網絡抖動不會影響下個號段的更新。

對于數據庫宕機的優化,可以使用數據庫的集群部署,保證高可用


Leaf-snowflake方案

由于Leaf-segment方案可以生成趨勢遞增的ID,同時ID號是可計算的,所以是可以計算業務量的

而Leaf-snowflake又有什么改進呢?


Leaf-snowflake方案.png

Leaf-snowflake方案完全沿用snowflake方案的bit位設計,即是“1+41+10+12”的方式組裝ID號。對于workerID的分配,當服務集群數量較小的情況下,完全可以手動配置。Leaf服務規模較大,動手配置成本太高。所以使用Zookeeper持久順序節點的特性自動對snowflake節點配置wokerID。Leaf-snowflake是按照下面幾個步驟啟動的:

1.啟動Leaf-snowflake服務,連接Zookeeper,在leaf_forever父節點下檢查自己是否已經注冊過(是否有該順序子節點)。

2.如果有注冊過直接取回自己的workerID(zk順序節點生成的int類型ID號),啟動服務。

3.如果沒有注冊過,就在該父節點下面創建一個持久順序節點,創建成功后取回順序號當做自己的workerID號,啟動服務。

除了每次會去ZK拿數據以外,也會在本機文件系統上緩存一個workerID文件。當ZooKeeper出現問題,恰好機器出現問題需要重啟時,能保證服務能夠正常啟動。這樣做到了對三方組件的弱依賴。一定程度上提高了可用性


Leaf-snowflake-zookeeper.png

另外關于時鐘回退的解決

時鐘回退解決.png

服務啟動時首先檢查自己是否寫過ZooKeeper leaf_forever節點:

若寫過,則用自身系統時間與leaf_forever/{self}節點記錄時間做比較,若小于leaf_forever/{self}時間則認為機器時間發生了大步長回撥,服務啟動失敗并報警。

若未寫過,證明是新服務節點,直接創建持久節點leaf_forever/${self}并寫入自身系統時間,接下來綜合對比其余Leaf節點的系統時間來判斷自身系統時間是否準確,具體做法是取leaf_temporary下的所有臨時節點(所有運行中的Leaf-snowflake節點)的服務IP:Port,然后通過RPC請求得到所有節點的系統時間,計算sum(time)/nodeSize。

若abs( 系統時間-sum(time)/nodeSize ) < 閾值,認為當前系統時間準確,正常啟動服務,同時寫臨時節點leaf_temporary/${self} 維持租約。

否則認為本機系統時間發生大步長偏移,啟動失敗并報警。

每隔一段時間(3s)上報自身系統時間寫入leaf_forever/${self}。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容