基于.NET Standard的分布式自增ID算法--美团点评LeafSegment
概述
前一篇文章講述了最流行的分布式ID生成算法snowflake,本篇文章根據(jù)美團點評分布式ID生成系統(tǒng)文章,介紹另一種相對更容易理解和編寫的分布式ID生成方式。
實現(xiàn)原理
Leaf這個名字是來自德國哲學家、數(shù)學家萊布尼茨的一句話:
There are no two identical leaves in the world
"世界上沒有兩片相同的樹葉"
設(shè)置數(shù)據(jù)表主鍵自增是最簡單的方案,缺點也很明顯:
強依賴數(shù)據(jù)庫,無法提供高可用
ID生成強依賴單臺服務(wù),無法橫向擴展
很容易想到,如果我的應(yīng)用每次申請一批id,插入數(shù)據(jù)時順序取一個使用,即將耗盡時再去獲取一批新的id,如此即可在一定程度上減弱與數(shù)據(jù)庫的關(guān)系,同時將單臺擴展延伸為獲取id的步長。
負責發(fā)放ID的服務(wù)既可以使用MySQL服務(wù),也可以使用Redis等服務(wù)。
基于MySQL實現(xiàn)
首先我們建立一張數(shù)據(jù)庫表
| DROP?TABLE?IF?EXISTS?`leafsegment`;CREATE?TABLE?`leafsegment`??(??`biz_tag`?varchar(255)?NULL?DEFAULT?NULL,??`max_id`?bigint(20)?NULL?DEFAULT?0,??`step`?int(11)?NULL?DEFAULT?5000,??`desc`?varchar(255)??NULL?DEFAULT?NULL,??`update_time`?datetime(0)?NULL?DEFAULT?now());--?添加一條初始化數(shù)據(jù)INSERT?INTO?`leafsegment`?VALUES?('test',?0,?5000,?'測試',?'2018-12-06?23:32:11'); |
數(shù)據(jù)庫表如下圖
biz_tag:業(yè)務(wù)標記,不同業(yè)務(wù)使用不同的值,可以最大限度地利用ID
max_id:當前已經(jīng)被申請走的最大Id
step:每次申請Id的步長
desc:業(yè)務(wù)內(nèi)容描述
update_time:最新一次申請時間
應(yīng)用如何獲取一批有效ID呢?
| BeginUPDATE?leafsegment?SET?max_id=max_id+step,update_time=now()?WHERE?biz_tag='test'SELECT?biz_tag,?max_id,?step?FROM?leafsegment?WHERE?biz_tag='test'Commit |
在一個事務(wù)周期內(nèi)完成max_id的更新,和最新數(shù)據(jù)的獲取,天然解決了資源競爭問題。
而后,我們就可以在應(yīng)用中將[max_id-step+1,max_id]閉區(qū)間的所有值作為ID來使用了。
基于Redis實現(xiàn)
Redis的實現(xiàn)更為簡單,基本原理是利用了Redis的IncrBy命令實現(xiàn)原子加N,具體實現(xiàn)流程無須贅述。
代碼實現(xiàn)
首先我們定義一個傳遞Step(步長)和MaxId(最大值)的DTO
| ????///?<summary>????///?數(shù)據(jù)單元????///?</summary>????public?class?DataVal????{????????///?<summary>????????///?當前最大Id????????///?</summary>????????public?long?MaxId?{?get;?set;?}?=?1;????????///?<summary>????????///?當前步長????????///?</summary>????????public?int?Step?{?get;?set;?}?=?1000;????} |
這個類僅負責將ID生發(fā)器的數(shù)據(jù)傳入核心類LeafSegment中。核心類的具體實現(xiàn)如下代碼:
| ????///?<summary>????///?美團的Leaf?Segment?方案????///?</summary>????public?class?LeafSegment????{????????private?long?_currentStep?=?long.MaxValue?>>?1;????????private?readonly?Func<DataVal>?_idGetAction;????????private?readonly?ConcurrentQueue<long>?_data?=?new?ConcurrentQueue<long>();????????private?readonly?AutoResetEvent?_autoReset?=?new?AutoResetEvent(false);????????///?<summary>????????///?美團的Leaf?Segment?方案????????///?</summary>????????///?<param?name="idGetAction">Id生成策略</param>????????///?<param?name="prefill">是否立即初始化數(shù)據(jù)</param>????????public?LeafSegment(Func<DataVal>?idGetAction,bool?prefill=false)????????{????????????_idGetAction?=?idGetAction;????????????if?(prefill)????????????{????????????????FillData();????????????}????????????Loop();????????}????????///?<summary>????????///?獲取下一個Id????????///?</summary>????????///?<returns></returns>????????public?long?NextId()????????{????????????_autoReset.Set();????????????if?(_data.TryDequeue(out?var?result))????????????{????????????????return?result;????????????}????????????throw?new?Exception("Resource?not?enough");????????}????????private?void?Loop()????????{????????????(new?Thread(_?=>????????????{????????????????while?(true)????????????????{????????????????????_autoReset.WaitOne();????????????????????FillData();????????????????}????????????})?{IsBackground?=?true}).Start();????????}????????private?void?FillData()????????{????????????//數(shù)量小于步長一半時觸發(fā)拉新????????????while?(_data.Count?<?(_currentStep?>>?1))????????????{????????????????var?tmp?=?_idGetAction.Invoke();????????????????_currentStep?=?tmp.Step;????????????????for?(var?i?=?tmp.MaxId?-?tmp.Step?+?1;?i?<=?tmp.MaxId;?i++)????????????????{????????????????????_data.Enqueue(i);????????????????}????????????}????????}????} |
此處需要注意的是LeafSegment構(gòu)造函數(shù)的第一個入?yún)dGetAction是一個返回DataVal的回調(diào)函數(shù),因此外部實現(xiàn)中可以在該回調(diào)函數(shù)中返回所需ID序列;
第二個參數(shù)prefill,該參數(shù)控制實例化LeafSegment對象時,是否同步調(diào)用獲取ID區(qū)段,如該值為false,將會由啟動的線程稍后補充數(shù)據(jù)。
完整實現(xiàn)、使用Demo以及benchmark測試請參見源代碼:https://github.com/sampsonye/nice
原文地址:?https://www.cnblogs.com/leafly/p/10135431.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的基于.NET Standard的分布式自增ID算法--美团点评LeafSegment的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rainbond 5.0正式发布, 支持
- 下一篇: asp.net ajax控件工具集 Au