號(hào)碼生成系統(tǒng)的創(chuàng)新實(shí)踐:游戲周周樂(lè)幸運(yùn)碼設(shè)計(jì)
一、業(yè)務(wù)背景
用戶可通過(guò)完成相關(guān)任務(wù)獲取周周樂(lè)幸運(yùn)碼,幸運(yùn)碼的投放規(guī)則如下:
- 基礎(chǔ)投放量:每期100萬(wàn)注唯一無(wú)重復(fù)的6位數(shù)字幸運(yùn)碼
- 動(dòng)態(tài)擴(kuò)容機(jī)制:參與人數(shù)超額時(shí),可實(shí)時(shí)追加100萬(wàn)注
二、幸運(yùn)碼特性
根據(jù)背景介紹,我們可以知道幸運(yùn)碼需要支持如下特性:
- 隨機(jī)性,發(fā)給每個(gè)用戶的幸運(yùn)碼都是隨機(jī)的,同時(shí)每個(gè)用戶領(lǐng)取的多個(gè)幸運(yùn)碼也是隨機(jī)的。
- 唯一性,每一組的幸運(yùn)碼中,各幸運(yùn)碼都是唯一的。
- 范圍性,幸運(yùn)碼嚴(yán)格限定在000000到999999區(qū)間內(nèi)。
- 高并發(fā),幸運(yùn)碼的生成和發(fā)放需要支持高并發(fā),能夠至少達(dá)到300QPS。
- 可追加,在當(dāng)期活動(dòng)非常火爆時(shí),需要可臨時(shí)追加一組幸運(yùn)碼庫(kù)存。
三、方案選型
鑒于幸運(yùn)碼需嚴(yán)格限定在6位數(shù)字范圍內(nèi)(000000-999999),傳統(tǒng)雪花算法因生成超長(zhǎng)ID(64位二進(jìn)制)且依賴時(shí)間戳遞增特性,難以直接適配。以下將對(duì)比三種方案:實(shí)時(shí)隨機(jī)生成模式、預(yù)生成庫(kù)存模式及號(hào)段+子碼模式,并會(huì)根據(jù)生成速度、存儲(chǔ)效率、擴(kuò)容成本三個(gè)核心指標(biāo)進(jìn)行系統(tǒng)性評(píng)估,最終選擇出最優(yōu)解決方案。
3.1 方案一:實(shí)時(shí)隨機(jī)生成模式
實(shí)現(xiàn)邏輯:
- 生成隨機(jī)數(shù)
- 再查詢數(shù)據(jù)庫(kù)是否有該隨機(jī)數(shù)
- 若沒(méi)有則入庫(kù),完成幸運(yùn)碼發(fā)放
- 若有再重新執(zhí)行第一步
缺陷分析:
- 碰撞概率隨庫(kù)存消耗不斷上升
- 數(shù)據(jù)庫(kù)IO壓力隨并發(fā)量線性增長(zhǎng)
- 不滿足高并發(fā)場(chǎng)景性能要求
3.2 方案二:預(yù)生成庫(kù)存模式
實(shí)現(xiàn)邏輯:
采用預(yù)生成幸運(yùn)碼方式:離線生成100萬(wàn)個(gè)幸運(yùn)碼,隨機(jī)打散,寫入數(shù)據(jù)庫(kù),每個(gè)幸運(yùn)碼對(duì)應(yīng)一個(gè)從1自增的序列號(hào),并使用Redis記錄幸運(yùn)碼序列號(hào)索引,初始值為1。
發(fā)放步驟如下:
- 從Redis查詢幸運(yùn)碼序列號(hào)索引
- 使用該索引查詢幸運(yùn)碼,并完成幸運(yùn)碼發(fā)放
- 遞增Redis的序列號(hào)索引,確保序列號(hào)索引關(guān)聯(lián)的幸運(yùn)碼是下一個(gè)可發(fā)放的幸運(yùn)碼
缺陷分析:
- 存儲(chǔ)空間浪費(fèi):未發(fā)放號(hào)碼占用存儲(chǔ)
- 擴(kuò)容效率低下:追加庫(kù)存需重新預(yù)生成
3.3 方案三:號(hào)段+子碼模式
采用號(hào)段+子碼機(jī)制:
- 號(hào)段管理:將10^6號(hào)碼劃分為1000個(gè)號(hào)段(號(hào)段值:0-999)
- 子碼管理:每個(gè)號(hào)段維護(hù)1000個(gè)可用子碼(子碼值:0-999)
- 生成規(guī)則:幸運(yùn)碼=隨機(jī)號(hào)段*1000+隨機(jī)子碼(示例:129358=129*1000+358)
3.4 方案對(duì)比
綜上,我們綜合幸運(yùn)碼生成速度、存儲(chǔ)效率、擴(kuò)容成本等指標(biāo),最終采用了號(hào)段+子碼模式來(lái)生成幸運(yùn)碼。
四、關(guān)鍵技術(shù)實(shí)現(xiàn)
4.1 號(hào)段分層機(jī)制
將100萬(wàn)注幸運(yùn)碼劃分為1000個(gè)號(hào)段(每段1000注),每個(gè)號(hào)段由兩部分組成:
- 號(hào)段ID:號(hào)段ID為唯一且不重復(fù)的整數(shù),范圍介于0到999之間。
- 子碼串:1000位字符串,采用"01"標(biāo)記使用狀態(tài),0表示未使用,1表示已使用,初始全0。
幸運(yùn)碼生成公式:
幸運(yùn)碼 = 號(hào)段ID * 1000 + 子碼位置
該設(shè)計(jì)既保留了生成幸運(yùn)碼的隨機(jī)性(號(hào)段ID隨機(jī)+子碼隨機(jī)),又通過(guò)子碼的類比特位存儲(chǔ)方式提升了存儲(chǔ)效率。
4.2 分布式并發(fā)控制
4.2.1 多級(jí)緩存策略
Redis存儲(chǔ)可用號(hào)段集合,若號(hào)段的子碼使用完,該號(hào)段會(huì)從Redis集合中剔除,同時(shí)本地緩存也會(huì)預(yù)加載可用號(hào)段,確保發(fā)碼時(shí)能更高效地獲取候選號(hào)段。
4.2.2 高效鎖搶占策略
系統(tǒng)為每個(gè)號(hào)段分配了分布式鎖,當(dāng)執(zhí)行發(fā)放幸運(yùn)碼時(shí),會(huì)從本地緩存隨機(jī)獲取15個(gè)候選號(hào)段。然后在遍歷獲取號(hào)段時(shí),將等待鎖的超時(shí)時(shí)間設(shè)置成30ms,確保號(hào)段被占用的情況下能夠快速遍歷到下一個(gè)號(hào)段(根據(jù)實(shí)際場(chǎng)景統(tǒng)計(jì),等待鎖的情況很少發(fā)生,一般最多遍歷到第二個(gè)號(hào)段即可成功搶占)。一旦成功獲得號(hào)段的分布式鎖后,便可進(jìn)一步隨機(jī)獲取該號(hào)段下的可用子碼。
4.2.3 動(dòng)態(tài)庫(kù)存策略
要追加庫(kù)存,只需再創(chuàng)建一組幸運(yùn)碼號(hào)段,并寫入Redis,后續(xù)發(fā)放時(shí)獲取該組的可用號(hào)段生成幸運(yùn)碼即可。從性能和存儲(chǔ)空間上遠(yuǎn)優(yōu)于預(yù)生成方式。
4.3 幸運(yùn)碼發(fā)放
發(fā)放步驟:
- 隨機(jī)獲取至多15個(gè)可用號(hào)段
- 遍歷號(hào)段
- 搶占號(hào)段的分布式鎖
- 若號(hào)段的分布式鎖搶占成功,則隨機(jī)獲取號(hào)段中可用的子碼,再根據(jù)號(hào)段和子碼生成幸運(yùn)碼
- 若號(hào)段的分布式鎖搶占失敗,則遍歷下一個(gè)號(hào)段,并重復(fù)上述步驟
五、總結(jié)
(1)雙重隨機(jī)保障
- 一級(jí)隨機(jī):號(hào)段選擇隨機(jī)(0-999)
- 二級(jí)隨機(jī):子碼選擇隨機(jī)(0-999)
- 通過(guò)號(hào)段隨機(jī)和子碼隨機(jī)方式確保生成的幸運(yùn)碼完全隨機(jī)
(2)數(shù)據(jù)唯一性
- 通過(guò)號(hào)段唯一和號(hào)段內(nèi)的子碼唯一確保生成的幸運(yùn)碼全局唯一
(3)彈性擴(kuò)展能力
- 擴(kuò)容耗時(shí)僅需秒級(jí)別
- 存儲(chǔ)空間相比預(yù)生成方案節(jié)省80%
(4)高性能發(fā)放
- 通過(guò)多重緩存及高效號(hào)段搶占策略大幅提升幸運(yùn)碼生成效率
- 實(shí)測(cè)QPS>300,平均響應(yīng)時(shí)間<15ms
本設(shè)計(jì)方案通過(guò)創(chuàng)新的號(hào)段+子碼管理機(jī)制,在保證號(hào)碼隨機(jī)性和唯一性的同時(shí),實(shí)現(xiàn)了高并發(fā)場(chǎng)景下的穩(wěn)定服務(wù)能力,為類似號(hào)碼生成系統(tǒng)的設(shè)計(jì)提供了可復(fù)用的架構(gòu)范式。