在使用SQL时,大都会遇到这样的问题,你Update一条记录时,需要通过Select来检索出其值或条件,然后在通过这个值来执行修改操作。

但当以上操作放到多线程中并发处理时会出现问题:某线程select了一条记录但还没来得及update时,另一个线程仍然可能会进来select到同一条记录。

å\u009c¨è¿\u0099é\u0087\u008cæ\u008f\u0092å\u0085¥å\u009b¾ç\u0089\u0087æ\u008f\u008fè¿°

如果清楚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;

image-20210808163815892

可以发现使用for update 查询请求也会被禁止

image-20210808164026253

但是没有for update就不会被禁止