下一篇文章PHP+MySQL处理产品闪购、降库存【高并发】闪购场景
如果秒杀产品并发量不是太高,可以直接使用mysql事务处理来达到目的。 通常,实现10到20人同时在线是没有问题的(主要取决于带宽和服务器配置),如果并发数超过100人,就得考虑添加缓存了。 Mysql应付不了,性能是个大问题。
使用redis实现一个简单的处理场景。 购买单品时,判断缓存中是否有商品信息。 如果没有,则从数据库中获取并存储在缓存中。 上次直接从缓存中取,只要库存充足,就可以让用户正常工作。 下单并提示下单成功
$redis = new redis(); $res = $redis->connect('127.0.0.1', 6379); if (!$res) { die('connect error!'); } $redis->select(6); //第一次取库存,先用保存到缓存中 $goods_id = 1; $stock_key = 'goods_id_stock_' . $goods_id; $stock = $redis->get($stock_key); if ($stock === false) { echo 'set goodslist'; $info = $db->table('Goods')->get(1); $stock = $info['stock']; // 这个地方一定要用setnx,防止一开始并发的时候重复设置 $redis->setnx($stock_key, $info['stock']); } if ($stock > 0) { $redis->decr($stock_key); //把这个产品信息添加到这个用户的订单中 //....一些逻辑代码 echo 'success'; } else { echo 'fail'; }
前面的代码还可以优化最后一步。 下单成功后,可以先将用户信息保存到redis集合中,然后秒判断是否所有商品都售完再保存到mysql数据库中。
高并发超买问题
我在上一篇文章中提到mysql在英超热销,这些情况在redis中也存在。 如下,减少库存时加一个延迟,因为我这里没有任何逻辑处理,但是实际项目中有一些逻辑处理。 ,所以我直接加一秒的延迟然后减少库存如下
$redis = new redis(); $res = $redis->connect('127.0.0.1', 6379); if (!$res) { die('connect error!'); } $redis->select(6); //第一次取库存,先用保存到缓存中 $goods_id = 1; $stock_key = 'goods_id_stock_' . $goods_id; $stock = $redis->get($stock_key); if ($stock === false) { echo 'set goodslist'; $info = $db->table('Goods')->get(1); $stock = $info['stock']; // 这个地方一定要用setnx,防止一开始并发的时候重复设置 $redis->setnx($stock_key, $info['stock']); } if ($stock > 0) { //加一些延时,模拟耗时的逻辑处理////////////////////// sleep(1); $redis->decr($stock_key); //把这个产品信息添加到这个用户的订单中 //....一些逻辑代码 echo 'success'; } else { echo 'fail'; }
并发压力测试,如右图,100个用户同时访问,时间间隔非常接近
查看redis中的库存数据,已经变成-8了
出现这种情况的原因是redis的命令操作是原子操作php处理高并发,请注意,代码中redis命令和php代码混在一起执行redis命令然后执行php代码再执行redis减法操作,不执行减法运算这段时间很可能是有问题的
Redis 事务和慷慨的锁
redis事务的作用是保持操作的原子性,也就是说保证一批redis命令的执行不会受到其他情况的干扰,并且它不像关系型数据库那样有回滚操作。 如果其中某些命令执行失败后,下面的其他命令仍然会被执行。 处理方法是加一个慷慨的锁,(使用watch)来监视你要处理的键值,如果在提交事务之前该值被其他线程或用户更改,则将其丢弃。 这个事务操作,也就是说,不执行事务中的命令。 如果此事务之前还有其他数据库修改操作,则会回滚(虽然本来是为了提高性能,但这里应该没有数据库操作)
$redis = new redis(); $res = $redis->connect('127.0.0.1', 6379); if (!$res) { die('connect error!'); } $redis->select(6); //第一次取库存,先用保存到缓存中 $goods_id = 1; $stock_key = 'goods_id_stock_' . $goods_id; //先把库存取出来 $stock = $redis->get($stock_key); //监控key $redis->watch($stock_key); //开启事务 $redis->multi(); if ($stock === false) { echo 'set goodslist'; $info = $db->table('Goods')->get(1); $stock = $info['stock']; // 这个地方一定要用setnx,防止一开始并发的时候重复设置 $redis->setnx($stock_key, $info['stock']); } if ($stock > 0) { //加一些延时 sleep(1); $redis->decr($stock_key); //把这个产品信息添加到这个用户的订单中 //....一些逻辑代码 //提交事务 $res = $redis->exec(); if ($res === false) { //实际业务中上面如果有其它对数据库进行的操作这里要使用事务回滚 echo 'unknow'; } else { echo 'success'; } } else { echo 'fail'; }
经过这样的治疗,疗效如下
黄牛刷订单处理
无非就是大量黄牛来证明你的业务能力强,而且应该投入的硬件成本也高。 这时候你就已经成功了,你就不会在意那些成本了! 如果是有人恶意刷你的活动php处理高并发,我相信他们刷的成本不会低,那就努力抵抗吧,如果抵抗不了,那就洗漱睡觉休息别玩了。
相关文章推荐 redis主从数据库分布式集群配置[高并发] mysql主从数据库配置实现分布式读写负载均衡[高并发]