一、数据库悲观锁
认为数据在被修改的时候一定会存在并发问题,因此在整个数据处理过程中将数据锁定。
数据库的行锁、表锁、排他锁等都是悲观锁,通过使用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.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