【面试八股总结】MySQL 锁:全局锁、表级锁、行级锁

1. 全局锁

        顾名思义,全局锁就是对整个数据库实例加锁。

MySQL 提供了⼀个加全局读锁的方法:

flush tables with read lock

释放全局锁,执行命令:

unlock tables

需要让整个库处于只读状态的时候,可以使用全局锁命令,之后其他线程的以下语句会被阻塞:

  • 数据更新语句(数据的增删改)
  • 数据定义语句(包括建表、修改表结构等)
  • 更新类事务的提交语句

典型使用场景:全库逻辑备份,即把整个库的表都select出来存成文本

2. 表级锁

MySQL 里面表级别的锁有这几种:表锁;元数据锁(MDL); 意向锁;AUTO-INC 锁;

(1)表锁

特点: 每次操作锁住整张表,开销小,加锁快,并发度最低

表锁的语法是:

 lock tables … read/write

        可以使用unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。 需要注意: lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。

(2)元数据锁(meta data lock,MDL)

MDL 不需要显式使用,在访问⼀个表的时候会被自动加上。

  • 对一张表进行 CRUD 操作时,加的是 MDL 读锁
  • 对一张表做结构变更操作的时候,加的是 MDL 写锁

MDL 是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。  

        当有线程在执行 select 语句( 加 MDL 读锁)的期间,如果有其他线程要更改该表的结构( 申请 MDL 写锁),那么将会被阻塞,直到执行完 select 语句( 释放 MDL 读锁)。

        反之,当有线程对表结构进行变更( 加 MDL 写锁)的期间,如果有其他线程执行了 CRUD 操作( 申请 MDL 读锁),那么就会被阻塞,直到表结构变更完成( 释放 MDL 写锁)。

        MDL 是在事务提交后才会释放,这意味着事务执行期间,MDL 是一直持有的,容易产生死锁问题。申请 MDL 锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现 MDL 写锁等待,会阻塞后续该表的所有 CRUD 操作。

(3)意向锁

        意向锁用于指示⼀个事务在未来可能会请求对某些资源(如数据行)的锁定。目的是为了快速判断表里是否有记录被加锁。

  • 意向共享锁表示事务打算在资源上获得共享锁。其他事务可以继续获得共享锁,但不能获得排他锁。
  • 意向排他(独占)锁: 表示事务打算在资源上获得排他锁。

        意向共享锁和意向独占锁是表级锁,不会和行级的共享锁和独占锁发⽣冲突,意向锁之间也不会发生冲突,只会和共享表锁和独占表锁发生冲突。

(4)AUTO-INC 锁

        表里的主键通常都会设置成自增的,这是通过对主键字段声明 AUTO_INCREMENT 属性实现的。之后可以在插入数据时,可以不指定主键的值,数据库会自动给主键赋值递增的值,这主要是通过 AUTO-INC 锁实现的。

        在插入数据时,会加一个表级别的 AUTO-INC 锁,然后为 AUTO_INCREMENT 修饰的字段赋值递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。一个事务在持有 AUTO-INC 锁的过程中,其他事务的如果要向该表插入语句都会被阻塞,从而保证插入数据时,被 AUTO_INCREMENT 修饰的字段的值是连续递增的。

        AUTO-INC 锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是在执行完插入语句后就会立即释放

缺陷: 

        AUTO-INC 锁再对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。

改进:

        InnoDB 存储引擎提供了一种轻量级的锁来实现自增。在插⼊数据的时候,会为被 AUTO_INCREMENT 修饰的字段加上轻量级锁,然后给该字段赋值⼀个自增的值,就把这个轻量级锁释放了,⽽不需要等待整个插入语句执行结束后才释放锁。

3. 行级锁

InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。

        行锁就是针对数据表中行记录的锁(也称为记录锁)。行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;
  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

(1)Record Lock 记录锁

        Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的:

  • 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);
  • 当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。

当事务执行 commit 后,事务过程中生成的锁都会被释放。

(2)Gap Lock 间隙锁

        Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

        间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的

假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

(3)Next-Key Lock 临键锁

        Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

        Next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的

        比如,一个事务持有了范围为 (1, 10] 的 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,就会被阻塞。

        虽然相同范围的间隙锁是多个事务相互兼容的,但对于记录锁,我们是要考虑 X 型与 S 型关系,X 型的记录锁与 X 型的记录锁是冲突的。

注意事项:

        在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,要等到事务结束时才释放。这就是两阶段锁协议。 如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。

加锁规则:

  • 加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间
  • 查找过程中访问到的对象才会加锁。
  • 索引上的等值查询,给唯⼀索引加锁的时候,next-key lock 退化为行锁。
  • 索引上的等值查询,向右遍历时且最后⼀个值不满足等值条件的时候,next-key lock 退化为间隙锁。

        以上规则是在可重复读隔离级别 (repeatable-read) 下验证的。同时,可重复读隔离级别遵守两阶段锁协议,所有加锁的资源,都是在事务提交或者回滚的时候才释放的。如果切换到读提交隔离级别 (read-committed) 的话,就好理解了,过程中去掉间隙锁的部分,也就是只剩下行锁的部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值