mysql 锁知识

一、加锁常用命令

1.修改隔离级别
set session transaction isolation level read uncommitted; 修改全局隔离级别为 读未提交
set global transaction isolation level read uncommitted; 修改当前事务隔离级别为读未提交

–1:Read Uncommitted

–2:Read Committed

–4:Repeatable Read

–8:Serializable 

2.查询数据库隔级别
select @@global.tx_isolation;

 

一、加锁测试

1.开启一个事务,让等待10s 后提交释放锁
START TRANSACTION;  

SELECT idd from iodn_area where idd>7 FOR UPDATE;  

select SLEEP(10); 

COMMIT; 
2.在另一个事务中更新某一行记录
update iodn_area set area_name ='wuqu' where idd=119;

3.总结:当第一个事务加锁命中索引后,锁定的是一行记录,当在第二个事务中更新同一行记录是要等待第一个事务执行完毕,如果没有命中索引,则锁定整个表,无论第二个事务更新哪行记录都是不行的。

 二、不可重复读测试

A 事务
START TRANSACTION;  

SELECT city_id from iodn_area where idd=107 ; 

查询后使用B 事务更新city_id

SELECT city_id from iodn_area where idd=107 ; 
COMMIT; 

总结:首先不可重复读是在一个事务内而定义的,在A事务内的两次查询结果不一样,而本次测试结果是两次查询结果一样,原因是mysql 默认的隔离级别是repeatable-read ,通过mvcc多版本控制已经解决了不可重复读的问题

 三、读未提交 测试

读未提交比较简单,主要是A 事务在事务内 读到了 B 事务未提交的数据,当B 事务故障后发生故障导致回滚,那么A 事务就读到了脏数据

四、读已经提交测试

读已经提交和未提交差不多,只不过A事务读取到了B事务已经提交的数据,导致A 事务不可重复读。

五、乐观锁的使用

方案一(CAS):更新时拿原来的值 比较,如果相等就修改,不相等说明被其他线程修改过,则报错返回,sql 伪代码如下:

update a set value = newValue where value = #{oldValue}//oldValue就是我们执行前查询出来的值

当然CAS 并不能保证该数据没有被修改过,比如经典的ABA问题,比如线程A修改了值为1 线程B修改了值为0,线程A在修改值为1,当相乘C进来判断时返现值没有更新,也就更新了值为新值,这样线程C并不知道值是否发生过改变,那么哪些场景会要求知道他是否发生过改变呢,比如要求知道值的修改记录,金钱的变动等,如果不要求过程的变动,只要求最终一致,那么CAS 就可以解决。

方案二(多版本控制):每次判断加版本号一起判断,每次更新的同时修改版本号,伪代码如下:

update a set value = newValue ,vision = vision + 1 where value = #{oldValue} and vision = #{vision} // 判断原来的值和版本号是否匹配,中间有别的线程修改,值可能相等,但是版本号100%不一样

方案三(时间戳):每次判断时间是否一致,每次更新的同时更新修改时间,伪代码如下:

update a set value = newValue ,modify_time = #{newTime} where value = #{oldValue} and modify_time = #{oldTime} // 判断原来的值和时间戳是否匹配,中间有别的线程修改,值可能相等,但是时间戳会不一样

 

posted @ 2021-01-12 16:24  狭路相逢智者胜  阅读(66)  评论(0编辑  收藏  举报