業(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à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ù)庫中, 于是請(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. Use redis to handle it according to your design. Why don’t you just set the time to permanent? Wait for request A to be submitted before clearing it, and then follow your normal logical judgment
2. Without redis, use the database to achieve multi-concurrency control. First of all, you must have a prize pool table, which will have fields indicating the winners. Each time the user When you win a prize, the prize pool table will be updated to set the winners, and then you will add the physical prize quantity control conditions when you update the SQL