PHP编程意识进阶-原子性
代码均为伪码
你接了个任务,写个商城秒杀。你心想,这不是手到擒来。
热点数据先预热进缓存,秒杀时走缓存,轻轻松松搞定。
于是,你在后台编辑商品库存时,这样写:
$skuId = 1;
$stock = 100;
Cache::set('sku-' . $skuId, $stock);
秒杀逻辑你这样写:
$buyCount = $request->post('buy_count');
$skuId = $request->post('sku_id');
if (($stock = Cache::get('sku-' . $skuId)) < $buyCount) {
throw new \Exception('库存不足');
}
//购买逻辑
Cache::set('sku-' . $skuId, ($stock - $buyCount));
结果上线时,你发现超卖了,你只好提桶跑路。聪明的你肯定发现了, 无论是php-fpm还是php-cli模式,worker 进程肯定不止一个,你的项目能支持的并发肯定也不止一个,多个并发过来,这段逻辑同时执行时,扣除逻辑还没执行到,多个并发的判断逻辑已经执行完了,结果就是好好的营销活动,被我们弄砸了。这怎么破?
问题的症结在于判断和扣除逻辑本应该是原子性的,而你分开写了,假设 Cache
用到的适配器是 redis
而不是 file
或者database
啥的。你应该把判断和扣除封装成 lua script
,让 redis server
保证其原子性。
$script = <<< EOF
if (redis.call('get', '$key')>=$buyCount) then
redis.call('decr', '$key', $buyCount)
end
EOF;
Redis::eval($script);
lua 语法不一定正确,凭印象写的
这样就保证了整个操作的原子性,其实原子性是一种方法论,他贯穿在整个编程世界中,包括 mysql 判断和扣除问题,怎么样保证其原子性,分布式锁还是利用数据库锁都。多思考原子性的问题,你的编程技能一定可以更上一层楼。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: