최근 플래시세일 이벤트를 진행했는데, 성능과 응답속도를 위해 redis를 사용했습니다. 글을 쓸 때 초자연적인 현상을 방지하는 데 특별한 주의를 기울였습니다. Redis 이론을 기반으로 하는 cas(check and set) 낙관적 잠금을 사용하면 이 문제를 해결할 수 있을 것이라고 생각했지만 여전히 매우 혼란스럽습니다. 구체적인 코드는 대략 다음과 같습니다.
<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>
최근 플래시세일 이벤트를 진행했는데, 성능과 응답속도를 위해 redis를 사용했습니다. 글을 쓸 때 초자연적인 현상을 방지하는 데 특별한 주의를 기울였습니다. Redis 이론을 기반으로 하는 cas(check and set) 낙관적 잠금을 사용하면 이 문제를 해결할 수 있을 것이라고 생각했지만 여전히 매우 혼란스럽습니다. 구체적인 코드는 대략 다음과 같습니다.
<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>
이 코드는 높은 동시성 조건에서는 여전히 과매도될 것이라고 생각합니다. 상금이 1개만 남았을 때 세 사람이 동시에 $redis->watch("mywatchkey")
을 실행하여 얻은 데이터가 99라면 과매도 현상이 발생한다고 가정해 보겠습니다.
redis
은 단일 스레드 읽기이므로 가장 간단한 대기열 구현을 사용해 보겠습니다.
추첨 전 redis
queueaward:100
에 경품 개수를 적습니다. // 길이가 100인 목록, 값은 경품 당첨 여부에만 사용됩니다
동시추첨
<code>$award = $redis->lpop('award:100'); // 由于队列只有100个值,可以确保只有100个人中奖 if(!$award){ echo "手气不好,再抢购!";exit; } // 剩下就是中奖操作的事情了</code>
sleep(5);
귀사에서는 아직도 인력을 수용하나요?
이런 상황을 생각해 보세요.
mywatchkey=99
사용자 A가 mywatchkey를 요청하고 99를 받았습니다.
사용자 B가 mywatchkey를 요청하고 99를 받았습니다.
A와 B의 요청이 완료되면 mywatchkey는 무엇이어야 합니까? . 101인가요, 100인가요?
이것은 전형적인 확인 후 실행 오류입니다
먼저 이 코드의 "과매도" 결과가 $mywatchkey > 100인지(불가능하다고 생각합니다)
아니면 $mywatchkey =100일 때 동시에 두 페이지가 나오는지 여쭤보겠습니다. "구매 성공!"
이 코드는 테스트해보지 않았으며 다음과 같을지 추측만 했습니다.
1.세션 A: mywatchkey 확인을 수행합니다. 이때 mywatchkey = 99
2.Session B: mywatchkey를 처리한 후 현재 mywatchkey=100이지만 Session A의 후속 작업에는 영향을 미치지 않습니다
두 개의 연결을 더 추가하고 살펴보겠습니다. 뭔가 얻을 수 있습니다
redis의 트랜잭션 및 감시 명령문
redis-watch-multi-exec-by-one-client
이 문제를 근본적으로 해결하고 싶다면 먼저 공부해보세요.
1. 잠금.
2. 기존 데이터베이스 트랜잭션.
그런 다음 Redis와 기존 데이터베이스의 차이점을 알아보세요.