1. 电商网站里都少不了减库存的操作,当然什么时候减各有各的处理,有的下单就减,有的发起支付就减少,有的支付完成后回调时减。对于这个减库存的时间点,因产品而已,比如秒杀类必须下单就减。
减库存时就不可避免会碰到一个用户从读取出当前库存,判断库存数量是否足够,执行减少库存(减商品表的库存、可能还需要减少商品sku表的库存)这步结事的过程中,如果有另外一个用户也读取了库存(前面的用户尚未减库存),并且也会随后减库存,此时就出现了脏读以及写脏数据了。
这里的处理一般都是加锁处理,锁可以是文件锁,也可是以数据库库锁,一般数据库锁见得多,数据库的锁里又有悲观锁和乐观锁,对于业务量不大的站,建议用悲观锁。我们这里也就只说悲观锁的处理。在减库存前的第一步是查询商品表的库存(如果有SKU再查sku表的库存)所以我们需要在查商品表库存时加锁,
如下: 2.
1,使用DB查询时: $product = DB::table('product')->where('id', $productId)->lockForUpdate()->first(); 2,使用Eloquent的MODEL查询 $product = Product::where('id', $productId)->lockforupdate()->first(); 我们都看到里了里面有一个lockForUpdata方法,这是laravel封装的,实际也很简单,就是最终的SQL就是select * from product where id= {$productId} for update,但这里要注意几点: 1,for update针对的是innodb引擎,对于myIsAM是没有这样的方法的。myisam只提供表锁,
2,上面的锁只在Transaction(事务)中才起作用。因为forupdate并不是在等待另外一句forupdate查询结束,而是在等着另外含有forupdate查询子句的事务提交或者回滚。
3,innodb是行锁,但行锁加成功与否依赖于是否是主键查询,上面的查询中id是商品表的主键,所以加锁成功,但如果换成未做索引的商品名称,就不是行锁。而是会自动转化成表锁。
4,在这步操作中执行完毕一定要执行事务提交或回滚,不然其它的forupdate就会一直在等待。mysql 中有一项配置:innodb_lock_wait_timeout=50 ,标示锁等待多少秒后,报错。
报的错误提示内容是:Lock wait timeout exceeded。此时很不好排查问题,多数业务是直接重启mysql,但这会造成数据不可逆的丢失异常。所有一定要提交或者回滚事务。
当然有一个好办法可查看是哪里没有提交或者回滚事务。在5.5中,
information_schema 库中增加了三个关于锁的表(MEMORY引擎);
innodb_trx 当前运行的所有事务innodb_locks 当前出现的锁innodb_lock_waits 锁等待的对应关系可以查看这三张表看当前事务被阻塞的情况,并结束掉死锁的事务。
本文地址:http://www.04007.cn/article/111.html 未经许可,不得转载.