MySQL中的锁、隔离级别、MVCC

最近在学习MySQL的过程中,对锁、隔离级别以及MVCC这块小有理解,因为这里的概念很多很杂,初学的时候容易一头雾水,所以在这里把自己的理解总结一下,希望能帮助大家梳理一个大体的框架,如果需要再自行深入研究。

1.MySQL中的锁

1.1按照粒度划分

MySQL 中提供了两种封锁粒度: 行级锁以及表级锁。
应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。 但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大。 在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。

1.2按照类型划分

1.2.1读写锁(行读写锁)

排它锁(Exclusive),简写为 X 锁,又称写锁。
共享锁(Shared),简写为 S 锁,又称读锁。
有以下两个规定: 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。

SELECT ... LOCK In SHARE MODE;//读锁
SELECT ... FOR UPDATE;//写锁
1.2.2意向锁(表读写锁)

使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。
在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。
意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。
有以下两个规定: 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁; 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。
通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。

1.3MySQL隐式与显示锁定

MySQL的引擎会根据隔离级别的需要自动的加锁和释放锁,这称为隐式锁定。这一点我们会在后面提到。也可以通过特定的语句进行显式的锁定,如上文提到的:

SELECT ... LOCK In SHARE MODE;//读锁
SELECT ... FOR UPDATE;//写锁

2.隔离级别

隔离级别是老生常谈的话题了,这里就做一个简单介绍,不再赘述。
未提交读(READ UNCOMMITTED) 事务中的修改,即使没有提交,对其它事务也是可见的。
提交读(READ COMMITTED) 一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
可重复读(REPEATABLE READ) 保证在同一个事务中多次读取同样数据的结果是一样的。
可串行化(SERIALIZABLE) 强制事务串行执行。
而每个隔离级别中,针对当前读和快照读都有相应的措施来实现。
这里要引入一个概念,快照读和当前读,快照读可以通过MVCC来实现,这样可以减少加锁所带来的开销,当前读就必须加锁

select * from table ...;//快照读
//以下都是当前读
select * from table where ? lock in share mode;//加读锁
select * from table where ? for update;//以下都加写锁
insert;
update;
delete;

InnoDB存储引擎使用MVCC来解决RC和RR下的快照读,当前读需要加锁。

3.MVCC

多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。
这里推荐一个视频,如果不想继续看下去可以直接观看视频,因为以下的讲解也是基于这个视频的。
https://www.bilibili.com/video/BV1hL411479T
MVCC是基于undolog版本链来实现的,在表中每一行的数据中有两个隐藏字段,一个是最后一次开启的事务id,还有一个指向上一个版本变化的事务的指针
在这里插入图片描述
进行快照读的时候根据生成的ReadView,按照规则读取数据。
在这里插入图片描述
在这里插入图片描述
RC和RR的区别就是ReadView的生成时机不同。RC是每次commit都生成一个ReadView,而RR是每次都复用第一次生成的ReadView。
在这里插入图片描述
在这里插入图片描述
最后还有一个问题,RR级别下通过MVCC能避免幻读问题吗?
答案是不能完全避免。
如果连续快照读,因为ReadView复用,就不会存在幻读问题。如果两次快照读之间存在当前读,ReadView就会重新生成,可能会出现幻读问题。那么问题该如何解决呢?InnoDB采用MVCC+Next-Key Locks解决幻读问题。
这里又要引入新的概念,InnoDB中的三种行锁:
Record lock:记录锁,单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 临键锁,锁定一个范围,包含记录本身
注意:行锁是针对索引的锁,如果无索引则升级为表锁!

4.总结

其实总结下来真的用很短的一句话就能概括:
不管是锁还是MVCC,都是为了服务于各种隔离级别的。针对四种隔离级别,有不同的措施。而未提交读隔离级别总是读取最新的数据行,可串行化隔离级别需要对所有读取的行都加锁,所以可以不讨论。RC和RR的快照读使用MVCC,当前读需要加锁。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值