業(yè)務(wù)
大轉(zhuǎn)盤抽獎(jiǎng)活動(dòng) 獎(jiǎng)品分實(shí)物和紅包 限制用戶只能中一個(gè)實(shí)物
使用redis
防同一用戶並發(fā)
超領(lǐng)實(shí)物 即中了多個(gè)實(shí)物
獲得獎(jiǎng)金池
AwardPool chooseAwardFromPool(){ //得到獎(jiǎng)池
// 查詢所有有效獎(jiǎng)品
// 若用戶之前已中了實(shí)物 排除實(shí)物獎(jiǎng)品
if(hasWinedRealObject && award.type==實(shí)物)
continue;
//...
}
award = chooseAwardFromPool(pool); //從獎(jiǎng)池中隨機(jī)選擇一個(gè)獎(jiǎng)品
若用戶還未抽中實(shí)物, 且同一用戶並發(fā)進(jìn)入, 存在隨機(jī)選擇的獎(jiǎng)品都為實(shí)物的可能, 如同一用戶10個(gè)並發(fā)請(qǐng)求進(jìn)來, 其中3個(gè)請(qǐng)求碰巧隨機(jī)選擇的獎(jiǎng)品均為實(shí)物, 於是該使用者就能中3個(gè)實(shí)物, 於是需要引入redis
來防並發(fā)
超中實(shí)物. 如下所示
//選中獎(jiǎng)品后處理
if(award.type == 實(shí)物){ // 若獎(jiǎng)品為實(shí)物
key = "user_"+userId+"_實(shí)物_count";
count = redis.incr(key);
if(count == 1){
redis.expire(key, 10*60); //設(shè)置過期時(shí)間10分鐘
}
if(count > 1){ //同一用戶中了多個(gè)實(shí)物
award = 未中獎(jiǎng); //此時(shí)默認(rèn)替換為未中獎(jiǎng)獎(jiǎng)品
}
}
對(duì)過期時(shí)間
我始終不知該如何評(píng)估, 設(shè)定多長(zhǎng)時(shí)間合適, 因?yàn)榛旧鲜轻槍?duì)惡意用戶並發(fā)請(qǐng)求才引入redis
的, 正常用戶的正常頁(yè)面操作無需做任何處理, 因?yàn)槿羟耙淮沃辛藢?shí)物,後面再來抽獎(jiǎng)的話, 一開始取得獎(jiǎng)池時(shí)就會(huì)排除掉實(shí)物獎(jiǎng)品, 故後面抽獎(jiǎng)獎(jiǎng)池中壓根就沒有實(shí)物獎(jiǎng)品了, 也就不會(huì)中實(shí)物了.
為什麼設(shè)置10分鐘呢? 因?yàn)槲矣X得兩個(gè)並發(fā)請(qǐng)求--且是均中了實(shí)物的兩個(gè)請(qǐng)求--不可能執(zhí)行redis.incr(key)
時(shí)相隔了10分鐘, 如下所示
#請(qǐng)求1 用戶尚未中實(shí)物 從獎(jiǎng)池中隨機(jī)返回了一個(gè)實(shí)物獎(jiǎng)品
award = chooseAwardFromPool(pool); //從獎(jiǎng)池中隨機(jī)選擇一個(gè)獎(jiǎng)品
#請(qǐng)求n 用戶尚未中實(shí)物 也從獎(jiǎng)池中隨機(jī)返回了一個(gè)實(shí)物獎(jiǎng)品
award = chooseAwardFromPool(pool); //從獎(jiǎng)池中隨機(jī)選擇一個(gè)獎(jiǎng)品
#請(qǐng)求1 執(zhí)行redis操作
count = redis.incr(key);
#請(qǐng)求n 執(zhí)行redis操作
count = redis.incr(key);
我覺得10分鐘能夠保證請(qǐng)求n執(zhí)行redis
操作時(shí), key
不會(huì)過期, 故能夠防超中實(shí)物.
但又不是很篤定, 怎覺得存在請(qǐng)求2執(zhí)行時(shí)key
會(huì)過期的情況, 但又想不出什麼情況下會(huì)有這樣的情況.
請(qǐng)求數(shù)並發(fā)量特別大的情況下會(huì)存在這種可能嗎? 如
ab -n 1000000 -c 1000 -T "application/x-www-form-urlencoded" -p post_draw http://localhost:8080/draw
如請(qǐng)求1過來的時(shí)候隨機(jī)選中了一個(gè)實(shí)物, 等到請(qǐng)求n過來的時(shí)候, 請(qǐng)求1還沒有提交到數(shù)據(jù)庫(kù)中, 於是請(qǐng)求n有可能隨機(jī)返回一個(gè)實(shí)物獎(jiǎng)品, 等到請(qǐng)求n執(zhí)行redis.incr(key )
時(shí), 已經(jīng)過了10分鐘了, 於是請(qǐng)求n仍能中實(shí)物.於是同一用戶中了兩個(gè)實(shí)物.
1.按照你的設(shè)計(jì)用redis來處理,為什麼不把時(shí)間直接設(shè)為永久呢?等請(qǐng)求A事物提交後再清除,接著就全走你正常的邏輯判斷
2.不用redis,借助數(shù)據(jù)庫(kù)實(shí)現(xiàn)多並發(fā)控制,首先你肯定有個(gè)獎(jiǎng)池表,裡面會(huì)有字段表示獲獎(jiǎng)人,每次用戶中獎(jiǎng)時(shí)會(huì)去更新獎(jiǎng)池表設(shè)定獲獎(jiǎng)人,然後你在進(jìn)行sql更新的時(shí)候加上實(shí)物獎(jiǎng)數(shù)量控制條件