分布式ID-号段模式
我們可以使用號段的方式來獲取自增ID,號段可以理解成批量獲取,比如DistributIdService從數(shù)據(jù)庫獲取ID時,如果能批量獲取多個ID并緩存在本地的話,那樣將大大提供業(yè)務(wù)應(yīng)用獲取ID的效率。
比如DistributIdService每次從數(shù)據(jù)庫獲取ID時,就獲取一個號段,比如(1,1000],這個范圍表示了1000個ID,業(yè)務(wù)應(yīng)用在請求DistributIdService提供ID時,DistributIdService只需要在本地從1開始自增并返回即可,而不需要每次都請求數(shù)據(jù)庫,一直到本地自增到1000時,也就是當(dāng)前號段已經(jīng)被用完時,才去數(shù)據(jù)庫重新獲取下一號段。
所以,我們需要對數(shù)據(jù)庫表進行改動,如下:
CREATE TABLE id_generator (id int(10) NOT NULL,current_max_id bigint(20) NOT NULL COMMENT '當(dāng)前最大id',increment_step int(10) NOT NULL COMMENT '號段的長度',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;這個數(shù)據(jù)庫表用來記錄自增步長以及當(dāng)前自增ID的最大值(也就是當(dāng)前已經(jīng)被申請的號段的最后一個值),因為自增邏輯被移到DistributIdService中去了,所以數(shù)據(jù)庫不需要這部分邏輯了。
這種方案不再強依賴數(shù)據(jù)庫,就算數(shù)據(jù)庫不可用,那么DistributIdService也能繼續(xù)支撐一段時間。但是如果DistributIdService重啟,會丟失一段ID,導(dǎo)致ID空洞。
為了提高DistributIdService的高可用,需要做一個集群,業(yè)務(wù)在請求DistributIdService集群獲取ID時,會隨機的選擇某一個DistributIdService節(jié)點進行獲取,對每一個DistributIdService節(jié)點來說,數(shù)據(jù)庫連接的是同一個數(shù)據(jù)庫,那么可能會產(chǎn)生多個DistributIdService節(jié)點同時請求數(shù)據(jù)庫獲取號段,那么這個時候需要利用樂觀鎖來進行控制,比如在數(shù)據(jù)庫表中增加一個version字段,在獲取號段時使用如下SQL:
update id_generator set current_max_id=#{newMaxId}, version=version+1 where version = #{version}因為newMaxId是DistributIdService中根據(jù)oldMaxId+步長算出來的,只要上面的update更新成功了就表示號段獲取成功了。
為了提供數(shù)據(jù)庫層的高可用,需要對數(shù)據(jù)庫使用多主模式進行部署,對于每個數(shù)據(jù)庫來說要保證生成的號段不重復(fù),這就需要利用最開始的思路,再在剛剛的數(shù)據(jù)庫表中增加起始值和步長,比如如果現(xiàn)在是兩臺Mysql,那么
mysql1將生成號段(1,1001],自增的時候序列為1,3,4,5,7…
mysql1將生成號段(2,1002],自增的時候序列為2,4,6,8,10…
更詳細的可以參考滴滴開源的TinyId:鏈接
在TinyId中還增加了一步來提高效率,在上面的實現(xiàn)中,ID自增的邏輯是在DistributIdService中實現(xiàn)的,而實際上可以把自增的邏輯轉(zhuǎn)移到業(yè)務(wù)應(yīng)用本地,這樣對于業(yè)務(wù)應(yīng)用來說只需要獲取號段,每次自增時不再需要請求調(diào)用DistributIdService了。
總結(jié)
以上是生活随笔為你收集整理的分布式ID-号段模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分布式ID-数据库多主模式
- 下一篇: 分布式ID-雪花算法