介绍秒杀想必大家都知道。短时间内,请求访问量会激增。同时要保证不会超卖,数据准确。仍然存在一些技术挑战。可惜一直没有机会在项目中实现。看了一些资料,打算实验一下。以下代码仅用于测试,环境比较简单,请根据实际情况修改。创建秒杀队列在开始秒杀之前,先将商品放入队列,如下/***创建秒杀列表*/publicfunctioncreateList(){$count=30;$redisKey='商品列表';for($i=1;$i<=$count;$i++){//用于测试,防止数据错误if(Redis::llen($redisKey)>=$count){break;}Redis::rpush($redisKey,$i);}}执行后Redis中有30个商品ID,数据正常。接下来就是关键的秒杀步骤,利用Redis的lpop命令获取产品ID,利用了Redis的原子性。/***Seckill*/publicfunctionbuy(){//随机用户名,无意义,标记即可$username=Hash::make(now());if($goodsId=Redis::lpop('goods_list')){//购买成功Redis::hset('buy_success',$goodsId,$username);}else{//购买失败Redis::incr('buy_fail');}}简化代码如上,purchase之后,成功或失败只是一个记录。在实际应用中,当然会比较复杂,但是需要注意的是Mysql不要同步操作。还有一点,Hash:make(now())即使值相同也不会生成相同的数据,参考这里。最后测试是测试,使用ab测试,执行ab-c300-n3000http://localhost/buy/,上面的命令表示300并发,一共执行了3000个请求,速度不是快了,还有794次访问失败。让我们看看数据是否正确。在页面中打印buy_success值30次成功。再来看看秒杀失败的次数不是一个准确的数字,2165+30是所有请求成功的次数,加上失败的794次,一共是2989次,还是不到3000次。结论以上测试都有不足,比如响应慢速度、请求失败和不准确的失败计数。看来要优化的地方很多,不仅仅是代码层面。测试的时候忘记关闭访问记录存储,应该会有一些影响。好在闪杀成功的次数准确,没有超卖。更新重新配置环境,测试相同的代码。不同的是每次访问都加入判断是否在黑名单中(实时查询MySQl),访问记录改为排队写入MySQL。与下面的结果相比,还是有进步的。最重要的是数据终于对上了,访问失败355,抢购成功30,抢购失败2615(忘记截图了),总和正好是请求总数。而MySQL中记录的访问记录也是3000条。参考资料:Redis实现高并发下的抢购和秒杀功能,基于云原生的秒杀系统设计思路,秒杀架构设计
