在工作中,我们经常会遇到这样的问题,需要更新库存,当我们查询到可用的库存准备修改时,这时,其他的用户可能已经对这个库存数据进行修改了,导致,我们查询到的数据会有问题,下面我们就来看解决方法。
如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT ... UPDATE。
举个例子
假设商品表单 products 内有一个存放商品数量的 quantity,在订单成立之前必须先确定 quantity 商品数量是否足够( quantity > 0 ),然后才把数量更新为 1 。
SELECT quantity FROM products WHERE id = 3; UPDATE products SET quantity = 1 WHERE id = 3;
开启事务
为什么不安全呢?
高并发时会出现问题,如果我们需要在 quantity > 0 的情况下才能扣库存,假设程序在第一行 SELECT 读到的 quantity 是 2,但是当 MySQL 正准备要 UPDATE 的时候,可能已经有人把库存扣成 0 了,但是程序却浑然不知,将错就错的 UPDATE 下去了。因此必须利用事务机制来确保读取及提交的数据都是正确的。
BEGIN WORK; SELECT quantity FROM products WHERE id = 3 FOR UPDATE;
此时 products 数据中 id = 3 的数据被锁住,其它事务必须等待此次事务提交后才能执行。
SELECT * FROM products WHERE id = 3 FOR UPDATE;
确保 quantity 在事务中读取的数据是可靠的。
UPDATE products SET quantity = '1' WHERE id = 3; COMMIT WORK;
提交 (Commit) 事务,products 解锁。
Tips:
- BEGIN / COMMIT 为事务的起始及结束点;
- 在事务进行当中,只有
SELECT ... FOR UPDATE
或LOCK IN SHARE MODE
会等待事务结束后才执行,一般的SELECT ...
则不受此影响。
简单用法
为了防止处理不当,根据业务实际情况,如果我们需要先查询,后更新数据的话,可以这样使用语句:
UPDATE products SET quantity = '1' WHERE id = 3 AND quantity > 0;