1、数据库悲观锁

认为数据在被修改的时候一定会存在并发问题,因此在整个数据处理过程中将数据锁定。

数据库的行锁、表锁、排他锁等都是悲观锁,通过使用select...for update语句, 执行该语句后,会在表上加持行锁,一直到事务提交,解除行锁。

1.1 使用场景举例:

在秒杀案例中,生成订单和扣减库存的操作,可以通过商品记录的行锁,进行保护。通过使用select...for update语句,

在查询商品表库存时将该条记录加锁,待下单减库存完成后,再释放锁。

1.2 示例的SQL如下:

//0.开始事务
begin; 	
//1.查询出商品信息
select stockCount from seckill_good where id=1 for update;
//2.根据商品信息生成订单
insert into seckill_order (id,good_id) values (null,1);
//3.修改商品stockCount减一
update seckill_good set stockCount=stockCount-1 where id=1;
//4.提交事务
commit;

在对id = 1的记录修改前,先通过for update的方式进行加锁,然后再进行修改。

同一时间只有一个线程可以开启事务并获得id=1的锁,其它的事务必须等本次事务提交之后才能执行。

1.3 注意:

使用select_for_update,另外一定要写在事务中

要使用悲观锁,必须关闭mysql数据库中自动提交的属性,命令set autocommit=0

 

2、数据库乐观锁

2.1 主要就是两个步骤:冲突检测和数据更新。比较典型的就是Compare and Swap(CAS)技术

多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新成功,失败的线程并不会被挂起,并可以再次尝试。

2.2 做法:在表中增加一个version字段,操作前先查询version信息,在数据提交时检查version字段是否被修改。

2.3 示例的SQL如下:

//1.查询出商品信息			
select stockCount, version from seckill_good where id=1;			
//2.根据商品信息生成订单
insert into seckill_order (id,good_id) values (null,1);
//3.修改商品库存
update seckill_good set stockCount=stockCount-1, version = version+1 where id=1, version=version;

在更新之前,先查询一下库存表中当前版本(version),然后在做update的时候,以version 作为一个修改条件。

CAS 乐观锁有两个问题:

a. CAS 存在一个比较重要的问题,即ABA问题. 解决的办法是version字段顺序递增。

b. 乐观锁的方式,在高并发时,只有一个线程能执行成功,会造成大量的失败,这给用户的体验显然是很不好的。

 

 

博客借鉴https://www.cnblogs.com/crazymakercircle/p/14731826.html

posted on 2021-09-01 00:00  smile学子  阅读(1928)  评论(0编辑  收藏  举报