業(yè)務(wù)
大轉(zhuǎn)盤(pán)抽獎(jiǎng)活動(dòng) 獎(jiǎng)品分實(shí)物和紅包 限制用戶(hù)只能中一個(gè)實(shí)物
使用redis
防同一用戶(hù)并發(fā)
超領(lǐng)實(shí)物 即中了多個(gè)實(shí)物
獲得獎(jiǎng)池
AwardPool chooseAwardFromPool(){ //得到獎(jiǎng)池
// 查詢(xún)所有有效獎(jiǎng)品
// 若用戶(hù)之前已中了實(shí)物 排除實(shí)物獎(jiǎng)品
if(hasWinedRealObject && award.type==實(shí)物)
continue;
//...
}
award = chooseAwardFromPool(pool); //從獎(jiǎng)池中隨機(jī)選擇一個(gè)獎(jiǎng)品
若用戶(hù)還未抽中實(shí)物, 且同一用戶(hù)并發(fā)進(jìn)入, 存在隨機(jī)選擇的獎(jiǎng)品都為實(shí)物的可能, 如同一用戶(hù)10個(gè)并發(fā)請(qǐng)求進(jìn)來(lái), 其中3個(gè)請(qǐng)求碰巧隨機(jī)選擇的獎(jiǎng)品均為實(shí)物, 于是該用戶(hù)就能中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){ //同一用戶(hù)中了多個(gè)實(shí)物
award = 未中獎(jiǎng); //此時(shí)默認(rèn)替換為未中獎(jiǎng)獎(jiǎng)品
}
}
對(duì)過(guò)期時(shí)間
我始終不知該如何評(píng)估, 設(shè)置多長(zhǎng)時(shí)間合適, 因?yàn)榛旧鲜轻槍?duì)惡意用戶(hù)并發(fā)請(qǐng)求才引入redis
的, 正常用戶(hù)的正常頁(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 用戶(hù)尚未中實(shí)物 從獎(jiǎng)池中隨機(jī)返回了一個(gè)實(shí)物獎(jiǎng)品
award = chooseAwardFromPool(pool); //從獎(jiǎng)池中隨機(jī)選擇一個(gè)獎(jiǎng)品
#請(qǐng)求n 用戶(hù)尚未中實(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í)物.于是同一用戶(hù)中了兩個(gè)實(shí)物.
1. Selon votre conception, utilisez Redis pour le gérer. Pourquoi ne définissez-vous pas simplement l'heure sur permanent?? Attendez que la demande A soit soumise avant de l'effacer, puis suivez votre jugement logique normal
2. Sans Redis, utilisez la base de données pour réaliser un contr?le multi-concurrence Tout d'abord, vous devez disposer d'une table de cagnotte, qui le fera. avoir des champs indiquant les gagnants. Chaque fois qu'un utilisateur gagne un prix, le tableau de la cagnotte sera mis à jour pour définir les gagnants, puis vous ajouterez les conditions de contr?le de la quantité physique du prix lors de la mise à jour du SQL