業務
大轉盤抽獎活動 獎品分實物和紅包 限制用戶只能中一個實物
使用redis
防同一用戶並發
超領實物 即中了多個實物
獲得獎金池
AwardPool chooseAwardFromPool(){ //得到奖池
// 查询所有有效奖品
// 若用户之前已中了实物 排除实物奖品
if(hasWinedRealObject && award.type==实物)
continue;
//...
}
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品
若用戶還未抽中實物, 且同一用戶並發進入, 存在隨機選擇的獎品都為實物的可能, 如同一用戶10個並發請求進來, 其中3個請求碰巧隨機選擇的獎品均為實物, 於是該使用者就能中3個實物, 於是需要引入redis
來防並發
超中實物. 如下所示
//选中奖品后处理
if(award.type == 实物){ // 若奖品为实物
key = "user_"+userId+"_实物_count";
count = redis.incr(key);
if(count == 1){
redis.expire(key, 10*60); //设置过期时间10分钟
}
if(count > 1){ //同一用户中了多个实物
award = 未中奖; //此时默认替换为未中奖奖品
}
}
對過期時間
我始終不知該如何評估, 設定多長時間合適, 因為基本上是針對惡意用戶並發請求才引入redis
的, 正常用戶的正常頁面操作無需做任何處理, 因為若前一次中了實物,後面再來抽獎的話, 一開始取得獎池時就會排除掉實物獎品, 故後面抽獎獎池中壓根就沒有實物獎品了, 也就不會中實物了.
為什麼設置10分鐘呢? 因為我覺得兩個並發請求--且是均中了實物的兩個請求--不可能執行redis.incr(key)
時相隔了10分鐘, 如下所示
#请求1 用户尚未中实物 从奖池中随机返回了一个实物奖品
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品
#请求n 用户尚未中实物 也从奖池中随机返回了一个实物奖品
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品
#请求1 执行redis操作
count = redis.incr(key);
#请求n 执行redis操作
count = redis.incr(key);
我覺得10分鐘能夠保證請求n執行redis
操作時, key
不會過期, 故能夠防超中實物.
但又不是很篤定, 怎覺得存在請求2執行時key
會過期的情況, 但又想不出什麼情況下會有這樣的情況.
請求數並發量特別大的情況下會存在這種可能嗎? 如
ab -n 1000000 -c 1000 -T "application/x-www-form-urlencoded" -p post_draw http://localhost:8080/draw
如請求1過來的時候隨機選中了一個實物, 等到請求n過來的時候, 請求1還沒有提交到數據庫中, 於是請求n有可能隨機返回一個實物獎品, 等到請求n執行redis.incr(key )
時, 已經過了10分鐘了, 於是請求n仍能中實物.於是同一用戶中了兩個實物.
PHP中文网2017-04-25 09:04:25
1.按照你的設計用redis來處理,為什麼不把時間直接設為永久呢?等請求A事物提交後再清除,接著就全走你正常的邏輯判斷
2.不用redis,借助數據庫實現多並發控制,首先你肯定有個獎池表,裡面會有字段表示獲獎人,每次用戶中獎時會去更新獎池表設定獲獎人,然後你在進行sql更新的時候加上實物獎數量控制條件