数据库与锁机制 (2)

select buy_num from order_test where order_num=105; 返回橙子购买数量为11!!!对于A事务来说,相当于15+1=11!!

 

 

 

由于普通查询语句未加任何锁,事务A未完成时,其他事务仍可对其所查询的语句进行修改操作,mysql实现的可重复读并不绝对可靠。

 

                  

 

测试3(丢失更新):   

事务A

 

事务B

 

select buy_num from order_test where order_num=105;   返回橙子购买数量为15

 

 

 

 

 

update order_test set buy_num= 16 where order_num=105;  commit;

将橙子购买数量+1,并提交,此时数据库中橙子数量为16

 

Update order_test set buy_num=16 where order_num=105;

根据第一步查询出来的橙子数量,业务代码中加1之后,再更新至数据库

 

 

 

两个独立的事务分别对橙子数量+1之后,数据库中的橙子数量只是从15增加到了16!造成了丢失更新的问题。

 

                           

         针对测试2、3的问题,可在第一条查询语句后面加上lock in share mode或者 for update

 

   

X(写锁)

 

S(共享锁)

 

X

 

冲突

 

冲突

 

S

 

冲突

 

兼容

 

 

其中lock in share mode将给数据添加共享锁,容易造成死锁,不推荐用于查询出来之后需要在事务内进行更新的场合;for update将给数据添加写锁,推荐使用。下面具体看看为什么添加共享锁容易造成死锁。

 

测试4(死锁测试):

事务A

 

事务B

 

select buy_num from order_test where order_num=105 lock in share mode;  加共享锁

 

 

 

 

 

Update order_test set buy_num=1 where order_num=105;加共享锁

 

Update order_test set buy_num= 1 where order_num=105; 由于事务B对该条记录加了共享锁,所以只能等待事务B提交

 

 

 

 

 

Update order_test set buy_num=1 where order_num=105; 由于事务A对该条记录也加了共享锁,所以只能等待事务A提交

 

两个事务都不能往下执行,互相等待对方释放锁,造成死锁。

如果将lock in share mode 换成 for update(写锁),则不会出现这个问题。

在查询语句后加for update,适用于先查询数据,再根据查询的结果,做业务处理计算出新值后,直接更新前面数据的场合,可以有效防止丢失更新问题,很重要

 

 

 

测试5(间隙锁):

事务A

 

事务B

 

select * from order_test where order_num >105 and order_num <124 for update;

 

 

 

 

 

update order_test set buy_num=10 where order_num=111; 数据被锁住了,无法更新

 

 

 

insert into order_test values(107,309,'茄子',8,now());  等待执行,范围内的值也无法插入

 

 

 

update order_test set buy_num=10 where order_num=105; 更新成功

 

 

 

insert into order_test values(90,309,'茄子',8,now());  插入成功,范围外的写数据不受影响

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzxxff.html