在PHP的生态中 ,是通过多进程的方式去优化程序性能的。在单机架构情况下防止超卖不像JAVA那样可以使用自身的锁机制实现。需要借助第三方程序来实现,如:数据库、Redis等。
接下来我们通过一个基于Redis实现的分布式锁来看下思路。
串行化实现
思路
一个商品建立一个分布式锁的key,用户请求每次购买商品,都需要先获取这个key,然后执行创建订单的逻辑;如果key获取不成功,则循环3次,三次都失败则退出。
代码
ceshi.php
connect('redis', 6379);
$i=0;
while (true) {
// 这里使用 redis 的set方法,增加nx、ex
// nx:在key存在时返回false;不存在时返回设置成功,并且返回true
// ex:设置KEY过期时间,redis中单个命令是原子性操作,过期时间是为了防止死锁
$re = $redis->set("product_lock_1", 2, ['nx', 'ex'=>5]);
if ($re) {
echo "执行减库存逻辑
";
echo "创建订单
";
$redis->del("product_lock_1"); // 删除锁
break;
} else {
if ($i >= 2) {
file_put_contents("product.txt", "失败\n", FILE_APPEND);
exit("失败");
}
$i++;
}
}
压力测试
结果
100的并发,请求1000次,失败请求30次,成功请求中有22次未获得锁而失败。
上面的例子,我们仔细分析下,接口请求需要先获取锁,然后才能执行后面的逻辑。 如果一个php-fpm进程获取得到了这个锁,在锁释放之前,其他php-fpm进程只能等待 。这是一个串行化的执行过程,并不能善用多核CPU。所以接口的性能偏低。
接下来我们来看下优化后的方案。
高并发方案
思路
该方案的核心是将商品的库存同步到Redis,Redis通过 decr 原子性扣减商品库存,然后执行商品的创建订单操作。有两个后台任务,一个查看商品库存是否真正用完了,如果完了就将状态更新到Redis中;另一个不停地将可用的库存数量(商品库存 – 支付未超时的订单库存)更新到Redis中。
代码
connect('redis', 6379);
// 这一步是用来给后台任务的,如果商品库存真正为0了,
// 通过设置这个redis字段,直接给用户返回商品已售完.
$is_end = $redis->exists("product_lock_1_end");
if($is_end) {
// echo "库存真正不足";
return;
}
// 这一步原子性操作,将库存是否足够的判断交给redis
// 这里的库存可以通过后台任务,将可用的
// 库存数量(商品库存 - 支付未超时的订单库存)
// 更新到Redis中。
$stock = $redis->decr("product_lock_1_stock");
if ($stock < 0) {
// echo "库存==冻结数量";
// 将扣减的数量加回去
$redis->incr("product_lock_1_stock");
return;
}
// echo "执行减库存逻辑
";
// echo "创建订单
";
$redis->incr("order_number");
压力测试
测试之前在redis里面执行下面命令,设置商品库存。
set product_lock_1_stock 100
然后再执行压力测试命令:
ab -n 1000 -c 100
结果
100的并发,1000次请求,失败为0。
每个订单一个商品,并未发生超卖现象。
海报
0 条评论
131
相关文章
本站已关闭游客评论,请登录或者注册后再评论吧~