業(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)來(lái), 其中3個(gè)請(qǐng)求碰巧隨機(jī)選擇的獎(jiǎng)品均為實(shí)物, 于是該用戶就能中3個(gè)實(shí)物, 于是需要引入redis
來(lái)防并發(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è)置過(guò)期時(shí)間10分鐘
}
if(count > 1){ //同一用戶中了多個(gè)實(shí)物
award = 未中獎(jiǎng); //此時(shí)默認(rèn)替換為未中獎(jiǎng)獎(jiǎng)品
}
}
對(duì)過(guò)期時(shí)間
我始終不知該如何評(píng)估, 設(shè)置多長(zhǎng)時(shí)間合適, 因?yàn)榛旧鲜轻槍?duì)惡意用戶并發(fā)請(qǐng)求才引入redis
的, 正常用戶的正常頁(yè)面操作無(wú)需做任何處理, 因?yàn)槿羟耙淮沃辛藢?shí)物,后面再來(lái)抽獎(jiǎng)的話, 一開(kāi)始取得獎(jiǎng)池時(shí)就會(huì)排除掉實(shí)物獎(jiǎng)品, 故后面抽獎(jiǎng)獎(jiǎng)池中壓根就沒(méi)有實(shí)物獎(jiǎng)品了, 也就不會(huì)中實(shí)物了.
為什么設(shè)置10分鐘呢? 因?yàn)槲矣X(jué)得兩個(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);
我覺(jué)得10分鐘能夠保證請(qǐng)求n執(zhí)行redis
操作時(shí), key
不會(huì)過(guò)期, 故能夠防超中實(shí)物.
但又不是很篤定, 怎覺(jué)得存在請(qǐng)求2執(zhí)行時(shí)key
會(huì)過(guò)期的情況, 但又想不出什么情況下會(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過(guò)來(lái)的時(shí)候隨機(jī)選中了一個(gè)實(shí)物, 等到請(qǐng)求n過(guò)來(lái)的時(shí)候, 請(qǐng)求1還沒(méi)有提交到數(shù)據(jù)庫(kù)中, 于是請(qǐng)求n有可能隨機(jī)返回一個(gè)實(shí)物獎(jiǎng)品, 等到請(qǐng)求n執(zhí)行redis.incr(key)
時(shí), 已經(jīng)過(guò)了10分鐘了, 于是請(qǐng)求n仍能中實(shí)物.于是同一用戶中了兩個(gè)實(shí)物.
1. Redis を使用して、時(shí)間を永続的に設(shè)定してみてはいかがでしょうか。リクエスト A が送信されるのを待ってからクリアしてから、通常の論理的判斷に従います
2. Redis を使用しない場(chǎng)合は、データベースを使用してマルチ同時(shí)制御を?qū)g現(xiàn)します。まず、フィールドを含む賞金プール テーブルが必要です。ユーザーが賞品を獲得するたびに、賞金プールテーブルが更新されて勝者が設(shè)定され、SQL を更新するときに物理的な賞品數(shù)量制御條件を追加します