在TP下的实验:
public function mysql_a()
{
db('goods')->where('id',2)->setInc('num');
$info = db('goods')->where('id',1)->find();
sleep(1);
if( $info['num']>0 ){
db('goods')->where('id',1)->setDec('num');
}
}
模拟并发请求:
执行前:
执行后:
压测结果:
如果当前为抢购或秒杀场景,此时就会出现超卖情况。
解决方案:
public function mysql_a()
{
// 启动事务
\Db::startTrans();
try {
\Db::name('goods')->where('id',2)->setInc('num');
$num = \Db::name('goods')->where('id',1)->lock(true)->value('num');
sleep(1);
if( $num>0 ){
\Db::name('goods')->where('id',1)->setDec('num');
}
// 提交事务
\Db::commit();
} catch (\Exception $e) {
// 回滚事务
\Db::rollback();
}
}
压测结果:
加上lock(true)的实际就是在查询语句最后加上 for update(必须跟事务同时使用),此时新开窗口再次查询加锁这条数据,你会发现另一个窗口的查询会一直等待,直到第一个窗口的事务提交。