Recently I was doing a flash sale event, and for the sake of performance and response speed, I used redis. When I was writing, I paid special attention to preventing supernormal phenomena. I used cas (check and set) optimistic locking based on redis theory. I thought that this problem should be able to eliminate it, but it still occurred. I was very confused and asked for help. The specific code is roughly as follows:
<code><?php header("content-type:text/html;charset=utf-8"); $redis = new redis(); $result = $redis->connect('10.10.10.119', 6379); $mywatchkey = $redis->get("mywatchkey"); $rob_total = 100; //抢购数量 if($mywatchkey<$rob_total){ $redis->watch("mywatchkey"); $redis->multi(); //设置延迟,方便测试效果。 sleep(5); //插入抢购数据 $redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time()); $redis->set("mywatchkey",$mywatchkey+1); $rob_result = $redis->exec(); if($rob_result){ $mywatchlist = $redis->hGetAll("mywatchlist"); echo "抢购成功!<br/>"; echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>"; echo "用户列表:<pre class="brush:php;toolbar:false">"; var_dump($mywatchlist); }else{ echo "手气不好,再抢购!";exit; } } ?> </code>
Recently I was doing a flash sale event, and for the sake of performance and response speed, I used redis. When I was writing, I paid special attention to preventing supernormal phenomena. I used cas (check and set) optimistic locking based on redis theory. I thought that this problem should be able to eliminate it, but it still occurred. I was very confused and asked for help. The specific code is roughly as follows:
<code><?php header("content-type:text/html;charset=utf-8"); $redis = new redis(); $result = $redis->connect('10.10.10.119', 6379); $mywatchkey = $redis->get("mywatchkey"); $rob_total = 100; //抢购数量 if($mywatchkey<$rob_total){ $redis->watch("mywatchkey"); $redis->multi(); //设置延迟,方便测试效果。 sleep(5); //插入抢购数据 $redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time()); $redis->set("mywatchkey",$mywatchkey+1); $rob_result = $redis->exec(); if($rob_result){ $mywatchlist = $redis->hGetAll("mywatchlist"); echo "抢购成功!<br/>"; echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>"; echo "用户列表:<pre class="brush:php;toolbar:false">"; var_dump($mywatchlist); }else{ echo "手气不好,再抢购!";exit; } } ?> </code>
I think this code will still be oversold under high concurrency conditions. Suppose: When there is only 1 prize left, three people execute $redis->watch("mywatchkey")
at the same time and the data they get is 99, then there is an oversold phenomenon.
Since redis
is a single-threaded read, then use the simplest queue implementation.
Before drawing the lottery, write the number of prizes into the redis
queueaward:100
// A list with a length of 100, the value is only used as whether you win the prize
Concurrent Lottery
<code>$award = $redis->lpop('award:100'); // 由于队列只有100个值,可以确保只有100个人中奖 if(!$award){ echo "手气不好,再抢购!";exit; } // 剩下就是中奖操作的事情了</code>
sleep(5);
Does your company still accept people?
Think about this situation.
mywatchkey=99
User A requests mywatchkey and gets 99.
User B requests mywatchkey and gets 99.
When A and B requests are completed, what should mywatchkey be? . 101, or 100?
This is a classic Check-then-Act error
First of all, let me ask you if the "oversold" result of this code is $mywatchkey > 100 (I think it is impossible)
Or when $mywatchkey = 100, two pages appear at the same time "Successful purchase!"
I have no idea about this The code is tested, I just guess whether it will be the following situation:
1.Session A: Perform mywatchkey check, at this time mywatchkey = 99
2.Session B: After processing mywatchkey, mywatchkey=100 at this time, but it does not affect the subsequent operations of Session A
Add two more connections, take a look, you might gain something
transactions-and-watch-statement-in-redis
redis-watch-multi-exec-by-one-client
If you want to fundamentally solve this matter, please study first:
1. Lock.
2. Traditional database transactions.
Then learn the difference between Redis and traditional databases.