還可以使用號(hào)段的方式來(lái)獲取自增 ID婶溯,號(hào)段可以理解成批量獲取间景。比如從數(shù)據(jù)庫(kù)獲取 ID 時(shí)佃声,就可以批量獲取多個(gè) ID 并緩存在本地,提升效率倘要。
比如每次從數(shù)據(jù)庫(kù)獲取 ID 時(shí)圾亏,就獲取一個(gè)號(hào)段,如 (1,1000]封拧,這個(gè)范圍表示1000個(gè) ID召嘶,業(yè)務(wù)應(yīng)用在請(qǐng)求提供 ID 時(shí),只需要在本地從1開(kāi)始自增并返回哮缺,而不需要每次都取請(qǐng)求數(shù)據(jù)庫(kù)弄跌,一直到本地自增到1000時(shí),也就是當(dāng)前號(hào)段已經(jīng)用完了尝苇,才去數(shù)據(jù)庫(kù)重新獲取下一號(hào)段铛只。
對(duì)數(shù)據(jù)庫(kù)表進(jìn)行改動(dòng)如下:
CREATE TABLE Test (
id int(10) NOT NULL,
current_max_id bigint(20) NOT NULL COMMENT '當(dāng)前最大Id',
increment_step int(10) NOT NULL COMMENT '號(hào)段的長(zhǎng)度',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
這個(gè)數(shù)據(jù)表是用來(lái)記錄自增步長(zhǎng)埠胖,以及當(dāng)前自增 ID 的最大值(也就是當(dāng)前已被申請(qǐng)?zhí)柖蔚淖詈竽莻€(gè)值),而自增邏輯就移動(dòng)到業(yè)務(wù)里頭去實(shí)現(xiàn)淳玩,所以數(shù)據(jù)庫(kù)不需要這部分邏輯直撤。
這種方案不再?gòu)?qiáng)依賴(lài)數(shù)據(jù)庫(kù),就算數(shù)據(jù)庫(kù)不可用蜕着,那么系統(tǒng)也能繼續(xù)支撐一段時(shí)間谋竖,但如果系統(tǒng)重啟,就會(huì)丟失一段 ID承匣,導(dǎo)致 ID 空洞蓖乘。
為提高可用性,需要做一個(gè)集群韧骗,業(yè)務(wù)在請(qǐng)求集群獲取 ID 時(shí)嘉抒,會(huì)隨機(jī)的選擇某個(gè)節(jié)點(diǎn)進(jìn)行獲取,對(duì)每個(gè)節(jié)點(diǎn)來(lái)說(shuō)袍暴,數(shù)據(jù)庫(kù)連接的是同個(gè)數(shù)據(jù)庫(kù)些侍,那么就可能會(huì)產(chǎn)生多個(gè)節(jié)點(diǎn)同時(shí)請(qǐng)求數(shù)據(jù)庫(kù)獲取號(hào)段,這時(shí)就可以利用樂(lè)觀鎖來(lái)進(jìn)行控制政模,比如在數(shù)據(jù)庫(kù)表中增加一個(gè) version 字段岗宣,在獲取號(hào)段時(shí)使用如下 SQL:
update Test set current_max_id = #{newMaxId}, version = version+1 where version = #{version}
以上 newMaxId 是根據(jù) oldMaxId + 步長(zhǎng)
算出來(lái)的,只要上面的 update 更新成功淋样,也就表示號(hào)段獲取成功狈定。