1. 背景
最近項(xiàng)目需要有分布式ID生成器這樣的組件,利用生成的ID作為表的主鍵而不是mysql的自增ID
查閱了一些網(wǎng)上的一些資料,針對(duì)性的思考,整理成文。
2. 組件要求
高可用
工業(yè)生產(chǎn)需要,比如單機(jī)掛了影響服務(wù)可不行
2.1 全局唯一
作為ID的特性
2.2 盡量保證ID遞增(并無(wú)嚴(yán)格要求)
查詢的時(shí)候有分頁(yè)或者排序類似的需求,如果主鍵ID本身能體現(xiàn)出時(shí)序效率會(huì)更
查詢的時(shí)候,往往有分頁(yè)或者排序的需求
1.可以給添加一個(gè)時(shí)間字段,并在其上建立普通索引
2.ID按照時(shí)間粗略有序
但是普通索引的訪問效率比聚集索引慢,所以傾向于方案2
但是做不到嚴(yán)格排序,因?yàn)橥蟾鱾€(gè)機(jī)器以master-slave形式交互,影響可用性以及QPS
2.3 其他
1.ID盡可能的短
減少存儲(chǔ)的空間以及增加查詢的效率,但是往往都按照64位來(lái)算就足夠
2.可用的時(shí)間足夠久
一些類SNOWFLAKE的算法會(huì)在64位的ID中利用部分位數(shù)(如41)代表時(shí)間戳,當(dāng)這部分時(shí)間戳的空間用完了,這個(gè)服務(wù)就不work了
3.容錯(cuò)性
假如一臺(tái)機(jī)器的時(shí)間機(jī)器被人工往前調(diào)了怎么辦
4.允許批量生成
允許業(yè)務(wù)方batch請(qǐng)求一次拿多個(gè)ID,提高效率
5.QPS盡可能高
類SNOWFLAKE算法會(huì)在64位ID中利用部分位數(shù)(如12)表示單位時(shí)間內(nèi)生成的ID序號(hào),這部分序號(hào)用完了,這個(gè)單位時(shí)間就不能再生成序號(hào)了
3.思路
3.1幾種方案
1.利用mysql表 主鍵ID auto_increment的特性
缺點(diǎn):主從同步,受限于主庫(kù)的寫入性能
2.UUID
缺點(diǎn):完全不保證遞增
3.獲取當(dāng)前時(shí)間
缺點(diǎn):不管時(shí)間是精確到秒還是微秒,都代表一個(gè)單位時(shí)間只能生成一個(gè)ID,無(wú)法滿足批量的請(qǐng)求
4.類SNOWFLAKE算法
本文主要針對(duì)該類算法進(jìn)行講解
3.2 SNOWFLAKE算法
先對(duì)SNOWFLAKE算法簡(jiǎn)單講解,參照下面的refer講解的
思路如下:
1.一個(gè)ID由64位生成
2.41bit作為時(shí)間戳,記錄當(dāng)前時(shí)間到標(biāo)記的起始時(shí)間(如到2018.1.1)差,精確到毫秒,那么服務(wù)可用時(shí)長(zhǎng)為(1<<41)/(1000* 60 * 60 * 24 *365) = 69.73年
3.10bit作為機(jī)器ID,也就是可以有1024臺(tái)機(jī)器
4.12bit作為序列號(hào),代表單位時(shí)間(這里是毫秒)內(nèi)允許生成的ID總數(shù),也就是1ms內(nèi)允許生成4096個(gè)ID
代碼可以直接參考refer
如何滿足分布式ID生成器的要求
高可用:1024臺(tái)機(jī)器互不影響的工作,單臺(tái)掛就掛
全局唯一:省略
盡量保證ID遞增:時(shí)間戳放在高位
可用時(shí)間長(zhǎng)度:69.73年
容錯(cuò)性:下面再探討
允許批量生成:同一單位時(shí)間(ms)內(nèi),最多允許生成4096個(gè)ID,支持批量
QPS盡可能高:在ID設(shè)計(jì)上,只能代表1ms最多允許有4096個(gè)ID生成,但實(shí)際用起來(lái),其實(shí)看代碼的設(shè)計(jì),并發(fā),鎖控制等等,
3.3 類SNOWFLAKE算法
SNOWFLAKE給出的主要是一個(gè)思想,把ID劃分為多個(gè)段,有不同的含義,可以結(jié)合自己的要求進(jìn)行重新劃分。按照個(gè)人理解,時(shí)間戳位數(shù)少了,機(jī)器位數(shù)多了,序列號(hào)位數(shù)多了。
3.3.1 時(shí)間 or 時(shí)間差
上面SNOWFLAKE算法是時(shí)間差來(lái)算的,也就是67.93年,換種思路,用絕對(duì)時(shí)間,要求位數(shù)開大一點(diǎn),不用每次都減去初始時(shí)間。
假設(shè)開到42位,精確到ms,那么就是 2^42=4398046511104,去https://currentmillis.com/ 查詢,夠用到2109年
3.3.2 單位時(shí)間的序號(hào)上限
SNOWFLAKE算法里面1ms最多允許有4096個(gè)ID生成。但是代碼實(shí)際執(zhí)行過(guò)程中,性能瓶頸以及現(xiàn)實(shí)場(chǎng)景要求,往往不會(huì)要求這么高的位數(shù),可以改成256個(gè),這樣就是QPS上限256000了(空間上允許,真正執(zhí)行還是取決于代碼效率),基本不會(huì)要求這么高。也就是序號(hào)需要8位就夠
3.3.3 工作機(jī)器個(gè)數(shù)
原算法支持1024臺(tái),當(dāng)然看各業(yè)務(wù)需求了,個(gè)人感覺4臺(tái)都?jí)蛄?,想?*256000=100w,1s生成100w個(gè)ID(還是那句話,QPS取決于代碼執(zhí)行效率),考慮機(jī)器掛掉以及代碼執(zhí)行性能,32臺(tái)也滿足大部分公司要求了。也就是5位ID給機(jī)器
3.3.4 拓展字段
上面一共42位(時(shí)間戳) + 8位(序列號(hào)) + 5位(機(jī)器ID) =55位,還有9位可以作為其他用途
標(biāo)志業(yè)務(wù)線
比如公司有A業(yè)務(wù)線,B業(yè)務(wù)線,xx,更有甚者是兩級(jí)的業(yè)務(wù)線A.a1業(yè)務(wù),A.a2業(yè)務(wù)。反正根據(jù)要求進(jìn)行一個(gè)映射,適合要求即可,假定這里分配32個(gè)業(yè)務(wù)線(5位)
3.3.5 保留字段
還剩下4位不知道有啥用,保留用吧。
有的文檔寫可以標(biāo)明機(jī)房ID,有的寫可以為后續(xù)業(yè)務(wù)拓展預(yù)留,比如32臺(tái)機(jī)器不夠用了要64臺(tái),比如1ms要512個(gè)ID了而不是256個(gè)ID
保留字段是用于這些未來(lái)發(fā)展可能要用的
4.方案討論
4.1 ID為何不能嚴(yán)格有序
因?yàn)楦鱾€(gè)機(jī)器保證不了時(shí)間同步,即使有NTP server,也保證不了ms內(nèi)各個(gè)機(jī)器同步
4.2 時(shí)間回退了怎么辦,比如人為修改了系統(tǒng)時(shí)間
每個(gè)機(jī)器記錄生成的最后一個(gè)ID,新生成ID的時(shí)候,拿當(dāng)前時(shí)間和最后一個(gè)ID解析出來(lái)的時(shí)間(高42位)進(jìn)行對(duì)比,不合理就報(bào)錯(cuò)
4.3 其他優(yōu)化
比如并發(fā)控制,CAS,鎖等,這里不展開代碼細(xì)節(jié)的討論
4.4 單機(jī)能生成全局唯一id嗎
不能,因?yàn)闆]有單機(jī)標(biāo)識(shí),無(wú)論時(shí)間戳,隨機(jī)數(shù),所有機(jī)器都是等價(jià)的
MAC地址有用嗎?有,但是MAC地址可以偽造,無(wú)法保證唯一
所以一定要有一個(gè)分布式集群統(tǒng)一分配機(jī)器編號(hào)來(lái)保證
5.總結(jié)
本文主要介紹了ID生成器的一些思路,并且對(duì)SNOWFLAKE方法進(jìn)行簡(jiǎn)單講解,從64位的構(gòu)成方式上進(jìn)行調(diào)整,進(jìn)行針對(duì)性的改動(dòng),適合自己的要求。來(lái)達(dá)到分布式ID生成器組件的各個(gè)要求。對(duì)于方案設(shè)計(jì)提出一些思路
refer
http://www.lxweimin.com/p/955909e1bd71 對(duì)于各種背景都有介紹
https://tech.meituan.com/MT_Leaf.html 美團(tuán)的實(shí)現(xiàn)
https://soulmachine.gitbooks.io/system-design/content/cn/distributed-id-generator.html
https://blog.csdn.net/u010372981/article/details/68924830 類snowflake的算法,代碼直白易懂