【MySQL】八股总结五:锁及MVCC

82 阅读9分钟

MySQL 是怎么加锁的? (重点阅读)

64. 进阶-锁-介绍_哔哩哔哩_bilibili (黑马的MYSQL)

表锁和行锁的区别

表锁: 加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。MyISAM 和 InnoDB 引擎都支持表级锁。

行锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁,只针对当前操作的行记录进行加锁。

加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。由存储引擎实现。

> 只有通过索引条件检索的数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁


理解针对索引字段加锁:行锁是加在索引上的,比如某个表中id字段是主键,如果给id=2这条记录加锁,那这把锁是加在主键索引(聚簇索引)。如果为某个没有索引的字段加锁,最终会造成全表扫描主键索引上锁住所有相关的记录(例age=3,age不是索引)。

哪些操作会加表级锁?哪些操作会加行级锁?请简单举例说一下。

执行DDL语句alter table 修改表 或 truncate table清空表 时,会加表锁

一般 update delete insert 对表的写操作语句都加的是行锁。只是如果没有命中索引情况下会全表扫描,导致给记录都加上行锁把全表锁住。(所以说加行锁的前提是 命中索引 ,否则会将所有记录加行锁

mysql中还有什么锁?表锁行锁全局锁

还有全局锁。全局锁:通过flush tables with read lock语句会将整个数据库就处于只读状态了,这时其他线程执行以下操作,DDL或者DML / 增删改或者表结构修改都会阻塞。

全局锁主要应用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

InnoDB 有哪几类行锁?MySQL 是怎么加锁的?

InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:

  • 记录锁(Record Lock) :也被称为记录锁,属于单个行记录上的锁。
  • 间隙锁(Gap Lock) :锁定一个范围,不包括记录本身(只有RR隔离级别)
  • 临键锁(Next-Key Lock) :其实就是记录锁+间隙锁锁定一个范围,包含记录本身,主要目的是为了解决幻读问题(MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。 (只有RR隔离级别)

MySQL 如何使用乐观锁和悲观锁?

我的描述:

因为MYSQL的并发控制是锁和MVCC共同实现的。

MVCC就是乐观锁的实现

行锁啊表锁就是悲观锁的实现

😖Next-Key Lock 的加锁范围?

73. 进阶-锁-行级锁-间隙锁&临键锁1_哔哩哔哩_bilibili

临键锁的加锁范围,一般是左闭右开,举个例子。

有如下数据

执行select * from tb where id >= 92 for update; 或者

select * from tb where id >= 92 lock in share mode;

这时会加一个记录锁,三个临键锁(临键锁=记录锁+间隙锁):

  1. 92的记录锁,锁住92
  2. 94的Next-Key Lock,范围(92,94]
  3. 98的Next-Key Lock,范围(94, 98]
  4. 正无穷的Next-Key Lock,范围(98,+∞)

什么时候加间隙锁?(了解了解)

73. 进阶-锁-行级锁-间隙锁&临键锁1_哔哩哔哩_bilibili

首先需要保证在RR隔离级别下

针对当前读 和 select delete update (黑马mysql课有说)

  1. 当前读 范围查找的时候,这个肯定加间隙锁(有索引的情况下)。
  2. 当前读 (及update、delete)查一个不存在的值的时候(有索引的情况下)。
  3. 在非唯一索引(普通索引上)进行等值查询( select,update、delete )时,也会加间隙锁(这个情况比较特殊,因为非唯一索引可能有多个相等的值,所以不能只加记录锁)。

总结:唯一索引范围查询 或 使用当前读(包括update和delete)一个不存在的值,非唯一索引进行等值查询

拓展:插入语句会加间隙锁吗?(字节)

会加意向锁,间隙锁待考究

表锁中的: 了解意向锁吗?

69. 进阶-锁-表级锁-意向锁_哔哩哔哩_bilibili

  1. 为了避免行锁与表锁的冲突,innodb在加行锁的同时会给表加上意向锁
  2. 后续如果有DDL语句给表加锁的时候会判断与意向锁(分共享和排他)是否冲突
  3. 这样后续表锁就不需要检查每个行是否都加了行锁

哪些语句会加意向锁?

行锁的语句都会加意向锁

意向共享锁(IS): 由语句 select..lock in share mode添加。

意向排他锁(IX): 由 insert、update、delete、select...for update 添加。

表锁中的:元数据锁

系统自己加,防止DML和DDL语句冲突。了解即可。

MVCC

86. 进阶-InnoDB引擎-MVCC-undolog版本链_哔哩哔哩_bilibili (优质课程)

10分钟带你深刻理解MySQL中的MVCC机制_哔哩哔哩_bilibili

MVCC是什么

是多版本并发控制。实现事务的并发控制。

核心思想就是读不加锁,读写不冲突

MVCC的实现依赖于 ****数据快照(其实就是undo log, 也可以叫版本链)和 read view,不同的事务访问不同版本的数据快照,从而实现事务的隔离级别。

MVCC详细一点的实现。InnoDB存储引擎对MVCC的实现

首先要知道MVCC依赖于数据快照(其实就是undo log, 也可以叫版本链)和 read view实现

  • undo log 记录了数据的历史版本,undo log版本链中主要隐藏字段有(回滚指针、修改当前记录的事务id、DB_ROW_ID

  • readView (由事务生成) 用于判断事务对于版本链中的哪些事务可见
    • 首先记住(先不用管为什么),在RC级别下,事务每次select前都会生成一个readview;在RR级别下,事务只有第一次select时会生成readView。
    • readView中主要字段(记住m_ids中记录了什么就行):m_creator_trx_id是创建readview的事务、m_ids是 创建该readview时系统中活跃的事务、m_low_limit_id:目前出现过的最大的事务 ID+1、m_up_limit_id:活跃事务列表 m_ids 中最小的事务 ID。
    • 如何判断:注重理解(以RR隔离级别为例) :如上图,假如有个事务4 需要查这条记录,这时事务2、3均未提交,那我事务4查的时候创建的readView中m_ids字段有 [2, 3] ,事务4查版本链的时候,发现第一条trx_id为3,3在readView的m_id中,那不可见,继续查下一条trx_id为2,也在m_id中所以不可见,下一条trx_id为1,比m_up_limit_id字段小,那可见,所以事务4查到的是最下面那条数据版本。

readView:

m_creator_trx_idm_idsm_up_limit_idm_low_limit_id
42,323+1=4
    • 其实很容易理解: (为了保证RR可重复读 m_ids 是我当前事务第一次读数据的时候系统未提交的事务,那么我如果查到了这些事务(或者比这些事务大的 事务id)修改的版本对我肯定不可见。 我只能查比m_ids中id小的事务修改的数据,因为这些事务在我第一次查的时候肯定已经提交了啊。

ReadView的产生时机

在RC隔离级别下,事务每次select都会生成一个readview。

在RR隔离级别下,事务第一次select生成readView,所以可避免不可重复读的问题。

对ReadView产生时机的理解:

  1. 在RC隔离级别下,每次都生成新的readView,那当然就会出现不可重复读的问题,因为每次读的readView都是新产生的。
  2. 在RR隔离级别,只有第一次读产生readView,后续读都用第一次读的readView,所以不会出现两次读不一致的情况。

MVCC用在哪个隔离级别?

RR和RC

当前读和快照读有什么区别?(前提,先理解MVCC)

快照读不加锁,读的是版本链的历史数据默认的select都是快照读,默认的select。

当前读:读取最新的数据并且加锁 (如果数据已经有锁将阻塞),如update、delete、select for update、select in share mode都是当前读。

锁和MVCC

RC和RR级别的具体实现有什么不同呢?理解(面经)

从锁和MVCC两个方面分析

首先在innoDB中,RR隔离级别是很大程度上解决了幻读

  1. MVCC:MVCC中,RR和RC隔离级别生成readView的时机不同
    1. RC每次都有readView所以会出现不可重复读(两次读的结果不一致)
    2. RR使用第一次的readView可避免可重复读和幻读。
  1. RC只有行锁,RR有行锁间隙锁临建锁
    1. RC只有行锁,不能解决幻读问题(前后两次范围查询结果不一致)。
    2. RR有间隙锁和临键锁,锁行记录和间隙,避免其他事务在范围内插入。

MySQL可重复读隔离级别是否完全解决幻读了,分两种情况。

MySQL 可重复读隔离级别,完全解决幻读了吗?

(很大程度上解决了)从锁和MVCC两个方面分析

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的即使中途有其他事务插入了一条数据, 是查询不出来这条数据的 因为新加入的数据的trx_id在readView的m_ids中或者> m_low_limit_id ,所以就很好了避免幻读问题。
  • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

出现幻读的场景

  1. xxxx