山东大学移动信息门户13

本文详细探讨了MySQL中的乐观锁和悲观锁机制,以及它们在读多写少和写多读少场景下的适用性。重点讲解了行锁与表锁的区别,包括共享锁和排他锁的使用,并通过实例展示了在不同事务操作下的锁行为,如死锁和幻读问题的解决。此外,还讨论了更新、插入和删除操作中自动加锁的行为以及如何避免死锁。
摘要由CSDN通过智能技术生成

2021SC@SDUSC

mysql的锁

乐观锁:假设本次操作,没有冲突,本质上就是CAS操作,需要我们自己实现,本质上是加一个version字段,在一个事务中,
              先获取某一行的version字段,等到更新的时候查看version是否跟刚才的version一样。 

             一般不会产生死锁,适合用于读多写少的情况

悲观锁:先假设这次操作一定有冲突,然后加锁(共享锁或者排他锁), 共享锁或者排他锁都是悲观锁
              共享锁也叫读锁(会排斥其他写操作,不排斥读操作), lock in share mode 指定
              排他锁:写锁,会排斥其他写和读操作,for update 指定 (update insert delete 自动加入排他锁,根据sql的编写情况决定是锁行还是锁表)

             适用于写多读少的情况

mysql通过分析sql ,隐式的采用行级锁或者表锁:好像mysql变聪明了,什么时候用行锁,什么时候用表锁,好像不能明确。。。

好像是这样的,用索引做的条件限制,一定用行锁,但是不是用索引做的条件限制,不一定不用行锁

看到过这样一句话:mysql默认隔离级别是可重复读,众所周知可重复读是可能导致幻读的,但是innodb引擎 通过mvvc和间隙锁 解决了幻读的问题!!!

读锁和写锁描述的是 锁之间的相互作用

行锁和表锁描述的是 锁的范围(对象)

表锁:锁住整个表,可能是被写锁锁定的,也可能是被读锁锁定的,可以减少死锁
行锁:锁住某一行,可能是被写锁锁定的,也可能是被读锁锁定的

一些测试

 mysql8.0 

T1表示第一个事务    T2 表示第二个事务

表结构,注意看user_id不是主键!!

 

我还插入了两个一样的user_id,并且我下面所有的测试,都是用user_id(非主键)来做where条件查询的!!!

共享锁的测试:结论:select 也就是读操作,不显示的加共享锁,是不会加锁的
 

T1对某一行加共享锁 T2也加共享锁 结果:成功

T1

SET AUTOCOMMIT=0;
BEGIN;
    SELECT * FROM account where user_id = '1' lock in share mode;


T2 

SET AUTOCOMMIT=0;
BEGIN;
    SELECT * FROM account where user_id = '1' lock in share mode;


T1不显式的加共享锁,然后做查询,T2做更新,成功

T1

SET AUTOCOMMIT=0;
begin;
select * from account where user_id = 1;

T2

SET AUTOCOMMIT=0;
begin;
UPDATE account set money = money + 100 where user_id = '1';

T1显式的加共享锁,然后做查询,T2做更新,失败

T1

SET AUTOCOMMIT=0;
begin;
select * from account where user_id = 1 lock in share mode;

T2

SET AUTOCOMMIT=0;
begin;
select * from account where user_id = 1 lock in share mode;

排他锁测试:update insert delete 自动加排他锁,排他锁允许不加锁的读

不能加 lock in share mode

UPDATE account set money = money - 100 where user_id = '2'  lock in share mode
> 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1

update自动加行锁的测试,并导致了死锁

T1

SET AUTOCOMMIT=0;
begin;
UPDATE account set money = money + 100 where user_id = '1';//步骤1,给用户1(两条记录)加了一个行锁,正常
UPDATE account set money = money  - 100 where user_id = '2';//步骤3,请求用户2的一个行锁,正常
COMMIT;//步骤五commit,T2事务导致的死锁被mysq解除了,不影响我T1的执行

T2

SET AUTOCOMMIT=0;
begin;
UPDATE account set money = money  - 100 where user_id = '2';//步骤2,给用户2加了一个行锁,正常,也说明步骤一,虽然没有用主键来做条件查询,但他还是是行锁,不是表锁
UPDATE account set money = money + 100 where user_id = '1';//步骤4,请求用户1的一个行锁,当我执行这一句的时候报了下面的错:死锁

UPDATE account set money = money + 100 where user_id = '1'
> 1213 - Deadlock found when trying to get lock; try restarting transaction
> 时间: 0.031s

> 1213 - Deadlock found when trying to get lock; try restarting transaction
> 时间: 0.031s

T1 update

SET AUTOCOMMIT=0;
begin;
UPDATE account set money = money + 100 where user_id = '1';

T2 select 并尝试加共享锁 失败

SET AUTOCOMMIT=0;
begin;
select * from account where user_id = 1 lock in share mode;

T2 select 但不显示加共享锁 成功

SET AUTOCOMMIT=0;
begin;
select * from account where user_id = 1 ;

所以我们这里也得出一个结论,排他锁会排斥其他排他锁和共享锁,但是不排斥不加锁的读操作!!!排他锁允许不加锁的读!!!

delete

步骤一 在T1中对第三条数据做删除但没提交

SET AUTOCOMMIT=0;
begin;
DELETE from account where user_id = '1' and money = 2200;

步骤二 在T2中做和步骤一一样的操作,阻塞,说明步骤一确实隐式的加锁了

SET AUTOCOMMIT=0;
begin;
DELETE from account where user_id = '1' and money = 2200;

步骤三 在T2中对第二条数据做更新,正常,说明步骤一是一个行级锁
 

SET AUTOCOMMIT=0;
begin;
UPDATE account set money = money - 100 where user_id = '2';

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值