【每日八股】MySQL篇(五):锁
目录
- MySQL 的全局锁有什么作用?
- 回顾:什么是 MVCC?
- 回顾:什么是 Read View?它和 MVCC 的关系是什么?
- MySQL 的表级锁有哪些?作用是什么?
- 表锁(Table Locks)
- 共享读锁(S Lock)
- 独占写锁(X Lock)
- 元数据锁(Metadata Lock,MDL)
- 意向锁(Intention Locks)
- AUTO-INC 锁
- MySQL 的行级锁有哪些?作用是什么?
- 记录锁(Record Locks)
- 间隙锁(Gap Locks)
- 临键锁(Next-Key Lock)
- 插入意向锁(Insert Intention Locks)
- MySQL 如何加锁?
MySQL 的全局锁有什么作用?
作用:让整个数据库处于只读状态,增删改会被阻塞。
使用场景:全局锁主要用于做全库逻辑备份,不会因为数据或表结构的更新出现备份文件的数据与预期不符的情况。
缺陷:在数据库中数据非常多的情况下,备份会花很多时间。备份期间,业务只能读数据,而不能更新数据,会造成业务停滞。
改进:在可重复读的隔离级别下,备份数据库之前先开启事务,会先创建 Read View,然后整个事务执行期间都在使用这个 Read View,由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作。即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,这样备份期间备份的数据一直是在开启事务时的数据。
回顾:什么是 MVCC?
MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库中用于实现高并发访问的一种技术,其核心思想是通过维护数据的多个版本来减少读写操作的冲突,从而提升系统的并发性能。
数据版本化
每次数据修改时,不直接覆盖数据,而是生成一个新的版本。旧版本数据保留,供正在运行的事务按需读取。
事务可见性规则
每个事务启动时分配唯一标识(如事务 ID 或时间戳)。事务只能看到以下数据:
- 在事务开始前已提交的版本;
- 本事务自身修改的版本(未提交的修改仅自身可见);
非阻塞读写
- 读操作:读复合自身可见性规则的版本时,无需等待写锁;
- 写操作:创建新版本,通过版本号或时间戳控制提交顺序,避免覆盖冲突。
回顾:什么是 Read View?它和 MVCC 的关系是什么?
Read View 是数据库实现 MVCC 的关键机制,用于确定事务在特定时间点能看到哪个版本。二者的核心关系是:MVCC 通过 Read View 实现事务隔离性,控制不同事务对数据版本的可见性。
MySQL 的表级锁有哪些?作用是什么?
表锁(Table Locks)
共享读锁(S Lock)
允许其它会话读取表,但禁止写入。适用于全表扫描等读密集型操作。
独占写锁(X Lock)
禁止其他会话读写表,仅当前会话可读写。适用于数据迁移、批量更新等需要独占访问的操作。
元数据锁(Metadata Lock,MDL)
作用:保护表结构(Schema)的一致性,防止在查询或事务中表结构被修改。
意向锁(Intention Locks)
作用:协调表级锁与行级锁,避免行锁与表锁的冲突。
类型:
- 意向共享锁(IS):事务计划在表中某些行加共享锁(S);
- 意向排他锁(IX):事务计划在表中某些行加排他锁(X);
规则:事务在加行锁前,必须先获取对应的意向锁。
意向共享锁和意向独占锁是表级锁,不会和行级的共享锁及独占锁发生冲突,意向锁之间也不会发生冲突,只会和共享表锁以及独占表锁发生冲突。意向锁的目的是为了快速判断表里是否有记录被加锁。
AUTO-INC 锁
作用:确保自增列(AUTO_INCREMENT)的唯一性,在插入时阻塞其他事务获取自增值。
表里的主键通常会被设置为自增的,之后在插入数据时,不指定主键的值,数据库会自动给主键赋值,递增的值通过 AUTO-INC 锁实现。具体来说,在插入数据时,会加入表级的 AUTO-INC 锁,然后被 AUTO_INCREMENT 修饰的字段会自增,插入语句完成后,AUTO-INC 锁释放。其他事务在对表插入数据时会阻塞,从而保证插入数据时字段的值是连续递增的。
缺陷:对大量数据进行插入时,会影响插入的性能,因为其他事务中的插入会被阻塞。
改进:InnoDB 存储引擎提供了一种轻量级的锁来实现自增。插入数据时,会为被 AUTO_INCREMENT 修饰的字段加上轻量锁,然后给该字段一个自增的值,之后释放轻量锁,而不需要等到整个插入语句执行完成后才释放锁。
MySQL 的行级锁有哪些?作用是什么?
记录锁(Record Locks)
作用:锁住表中单行记录,若表无索引,则退化为表锁。记录锁分为排他锁和共享锁。
间隙锁(Gap Locks)
作用:锁定索引记录之间的间隙(比如id
在 5 和 10 之间的区间),防止其它事务插入数据,解决幻读问题。
具体来说,间隙锁只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象(幻读:在一个事务中,两次相同的查询返回了不同的结果,尽管两次查询之间没有修改或删除现有的数据,但可能有新的插入)。间隙锁之间是兼容的,两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系。
临键锁(Next-Key Lock)
临键锁是 记录锁 + 间隙锁 的组合,锁定一左开右闭区间,并且锁定记录本身。临键锁既能保护该记录,又能阻止其他事务将新记录插入到被保护记录前面的间隙中。
临键锁是 InnoDB 默认的行锁类型,用于解决幻读问题。适用于范围查询以及非唯一索引的等值查询。
插入意向锁(Insert Intention Locks)
作用:插入意向锁是一种特殊的间隙锁,表示事务准备在某个间隙插入数据,等待其他事务释放该区间的间隙锁。
需要注意的是,插入意向锁不会阻止其他事务在同意间隙的不同位置插入数据,即允许并发插入。
在执行INSERT
语句前,会自动加插入意向锁。
MySQL 如何加锁?
首先需要明确的一点是,临键锁是 MySQL 数据引擎 InnoDB 的默认行锁类型,因此下面频繁提到的“退化”,实际上是在 InnoDB 默认类型的基础上进行的。
当查询的记录是存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「记录锁」。
当查询的记录是不存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「间隙锁」。
当查询的记录存在时,用非唯一索引进行等值查询除了会加 next-key lock 外,还额外加间隙锁。
当查询的记录不存在时,用非唯一索引进行等值查询只会加 next-key lock,然后会退化为间隙锁。
非唯一索引范围查,next-key lock 不会退化为间隙锁和记录锁。