上一篇:PHP面试大总结(2019-05-08 18:38:04)
文章大纲

redis高并发秒杀

2019-05-09 10:25:58
<p>先直接上脚本代码:</p><pre>&lt;?php<br>//seckill.php<br>include('../db.php');<br>$redis = new Redis();<br>$conn = $redis-&gt;connect('localhost', '6379');<br><br>if( !$conn ){<br> exit('redis connection failed!');<br>}<br><br>$product_id = 111;<br><br>//此处秒杀数量,在秒杀整个过程是个不变的值,在秒杀前从db里查询库存得到。<br>$stock_num = 5;<br><br><br>$p_key = 'sales_'.$product_id;<br>$redis-&gt;watch($p_key);<br><br>$sale_num = $redis-&gt;get($p_key);<br>if( $sale_num &gt;= $stock_num ){<br> exit('活动结束');<br>}<br><br>$redis-&gt;multi();<br>$redis-&gt;incr($p_key);<br>$res = $redis-&gt;exec();<br><br>if( $res ){<br> $sql = "update seckill_products set store = store - 1 where id = ".$product_id;<br> $res_u = $mod-&gt;exec($sql);<br> if( $res_u ){<br> echo '秒杀成功';<br> }<br>}else{<br> file_put_contents("second_kill.txt", $sale_num."\n", FILE_APPEND);<br>}<br><br></pre><p>上面脚本里,防止超卖的关键代码就是redis的watch方法。</p><p>watch命令解释:</p><blockquote><p>Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断</p></blockquote><p>为了让上面解释显得更加直白,我建了两段测试代码:</p><pre>&lt;?php<br>//watch1.php<br>$redis = new Redis();<br>$conn = $redis-&gt;connect('localhost', '6379');<br><br>if( !$conn ){<br> exit('redis connection failed!');<br>}<br><br><br>$test_key = "zhai14";<br>$redis-&gt;watch($test_key);<br>$redis-&gt;multi();<br>$redis-&gt;set($test_key, "first watch");<br>sleep(10); //模拟较其它请求先开始执行multi,但较其它请求后执行exec的情形<br>$redis-&gt;exec();<br></pre><p><br></p><pre>&lt;?php<br>//watch2.php<br>$redis = new Redis();<br>$conn = $redis-&gt;connect('localhost', '6379');<br><br>if( !$conn ){<br> exit('redis connection failed!');<br>}<br><br><br>$test_key = "zhai14";<br>$redis-&gt;watch($test_key);<br>$redis-&gt;multi();<br>$redis-&gt;set($test_key, "second watch");<br>$redis-&gt;exec();<br><br></pre><p>打开两个git bash窗口,在一个窗口先执行php watch1.php, 然后再在另一个窗口执行 php watch2.php.</p><p>最后检查redis里zhai14这个键的值是:<b>second watch。</b></p><p><b><br></b></p><p>所以表示watch1.php文件里的redis事务根本没有执行。无法理解?下面有更详细的解释:</p><div>重要关键点:</div><blockquote><div>事务执不执行,就看watch的key值在watch到exec这段时间之间有没有产生变化。</div></blockquote><div>》先执行的watch1.php, 此时zhai14键值为null,假定有一列代表数据版本号,且此时版本号为001.</div><div>》接着执行watch2.php,也是监视zhai14键值,数据无变化,所以版本号仍为001.</div><div>》watch2.php比watch1.php先执行exec方法,键zhai14此时值就变为second watch, 版本号更新为002.</div><div>》再回到watch1.php,10秒过了,开始执行exec方法,先要检查监视的键值,发现版本号从001变为002了,从而exec执行失败。(注意exec还没执行,修改值为first watch的命令代码并不会执行的)</div><div><br></div><p>利用如上思路拓展一下,如果watch2.php脚本改成如下,结果如何呢?</p><pre>&lt;?php<br>//watch2.php<br>$redis = new Redis();<br>$conn = $redis-&gt;connect('localhost', '6379');<br><br>if( !$conn ){<br> exit('redis connection failed!');<br>}<br><br>$test_key = "zhai14";<br>$redis-&gt;set($test_key, "second watch");<br></pre><p>不太确定的话,大家自己实践去吧。<br></p><p><br></p><p>ok,现在回到上面的秒杀脚本seckill.php.<br></p><p>无论多高并发,这些请求一开始获取的销量值一样,但总有一个请求是最先执行事务的exec方法(也许是微秒甚至纳秒级别,但总有一个是最先的),这样子id为111的产品销量就已经+1了,即监视的键的值产生变化了,其它并发的请求的redis事务都将取消。<br></p><p><br></p><p><br></p>
上一篇:PHP面试大总结(2019-05-08 18:38:04)
我要评论
评论列表