分散式唯一ID(三)--Leaf-Segment資料庫方案
本文來自官方文件的簡單總結,非原創!!!
一、改進:
原始方案每次獲取ID都要讀寫資料庫,資料庫壓力比較大。
每次獲取一個號段的值(step決定大小),用完之後再去資料庫獲取新的號段,很大減輕資料庫的壓力。
各個業務不同的需求用biz_tag欄位來區分。
如果以後因為效能等原因需要分庫分表,只需要對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個號被消耗完了之後才會去重新讀寫一次資料庫。
架構圖:
- test_tag在第一臺Leaf機器上的號段為1~1000,當號段用完時,會去載入另一個號段。
- 假設另外兩臺機器號段都沒有更新,這個時候第一臺機器重新載入的號段應該是3001~4000。
- 同時資料庫對應的biz_tag這條資料的max_id會從3000被更新成4000。
Begin UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx SELECT tag, max_id, step FROM table WHERE biz_tag=xxx Commit
三、優點:
- Leaf服務方便拓展,效能方便是OK的。
- ID號碼是趨勢遞增的64位數字,滿足資料庫儲存的主鍵要求。
- Leaf服務內部有號段快取,即使DB宕機,短時間內Leaf仍能正常對外提供服務。
- 自定義max_id,非常方便業務從原有的ID方式上遷移過來。
四、缺點:
- ID號碼不夠隨機,可以被競對根據ID號碼得到一些資訊。
- 當號段使用完之後會hang在更新資料庫的I/O上,TP99資料會出現偶爾的尖刺。
- DB一段時間宕機會造成整個系統不可用。
五、雙buffer優化:
針對第二個缺點,Leaf做了雙buffer優化。
希望DB取號段的過程能夠做到無阻塞,即當號段消費到某個閾值時就非同步的把下一個號段載入到記憶體中。
而不是等到號段用盡的時候才去更新號段,這樣做可以很大程度上的降低突刺問題。
實現圖:
每個biz-tag都有消費速度監控,推薦segment長度設定為服務高峰期發號QPS的600倍(10分鐘)。
這樣即使DB宕機,Leaf仍能持續發號10-20分鐘不受影響。
六、高可用:
針對"DB可用性"的問題,採用一主兩從且分機房部署,Master和Slave之間採用半同步方式同步資料。
同時使用DBProxy資料庫中介軟體做主從切換。
這種非同步模式在非常極端情況下仍然會造成資料不一致的情況,但是出現的概率非常小。
如果系統要保證100%的資料強一致,可以選擇使用“類Paxos演算法”實現的強一致MySQL方案,如MySQL 5.7的Group Replication。
但是運維成本和精力都會相應的增加,根據實際情況選型即可。
同時Leaf服務分IDC部署,內部的服務化框架是“MTthrift RPC”。
服務呼叫的時候,根據負載均衡演算法會優先呼叫同機房的Leaf服務。
在該IDC內Leaf服務不可用的時候才會選擇其他機房的Leaf服務。
同時服務治理平臺OCTO還提供了針對服務的過載保護、一鍵截流、動態流量分配等對服務的保護措施。