在使用SQL时,大都会遇到这样的问题,你Update一条记录时,需要通过Select来检索出其值或条件,然后在通过这个值来执行修改操作。
但当以上操作放到多线程中并发处理时会出现问题:某线程select了一条记录但还没来得及update时,另一个线程仍然可能会进来select到同一条记录。
如果清楚MySQL是如何执行查询的,就知道为什么会这样子了。
因为执行SELECT count(*) FROM tb1 AS inner_tb1 WHERE inner_tb1.type = outer_tb1.type的时候会获得读锁(共享锁),一旦数据表被加上读锁,其他请求可以对该表再次增加读锁,但是不能增加写锁。(当一个请求在读数据时,其他请求也可以读,但是不能写,因为一旦另外一个线程写了数据,就会导致当前线程读取到的数据不是最新的了。这就是不可重复读现象)
这是也可以通过跳过这个限制,但是这个时候就出现问题了,由于临时表是先查出来,然后在进行更新,如果先查询出来,如果在临时表中加入for update还没用,因为是临时表
set autocommit = 0;
update
wms_ware_sku
set
stock = stock - 100
where
sku_id in
(select sku_id from (SELECT sku_id from wms_ware_sku where stock > 0) as a);
set autocommit = 1;
正确解法,使用悲观锁先锁住数据,然后进行更新
BEGIN;
select sku_id from wms_ware_sku where stock > 0 for update
update
wms_ware_sku
set
stock = stock - 100
where
sku_id in
(select sku_id from wms_ware_sku where stock > 0);
COMMIT;
可以发现使用for update 查询请求也会被禁止
但是没有for update就不会被禁止