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