MySQL 常用锁

锁是一种常见的并发事务的控制方式。

表级锁和行级锁

  • 表级锁:MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,消耗资源也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表记锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
  • 行级锁:MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的行记录进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。

MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发写的情况下性非常差。InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level locking),默认为行级锁。

InnoDB 有哪几类行锁

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

  • 记录锁(Record Lock): 也被称为记录锁,属于单个行记录上的锁。
  • 间隙锁(Gap Lock):锁定一个范围,不包括记录本身。
  • 临键锁(Next-Key Lock): Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。

在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。

共享锁和排他锁

不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类。

  • 共享锁(S 锁):又称为读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁(X 锁):又称为写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录以经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。

由于 MVCC 的存在,对于一般的 SELECT 语句,InnoDB 不会加任何锁。不过,可以 通过以下语句显示加共享锁或排他锁。

# 共享锁,可以在 MySQL 5.7 和 MySQL 8.0 中使用。
SELECT ... LOCK IN SHARE MODE;
# 共享锁,可以在 MySQL 8.0 中使用
SELECT ... FOR SHARE;
# 排他锁
SELECT ... FOR UPDATE;	

意向锁

如果需要用到表锁的话,如何判断表中记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东西来快速判断是否可以对某个表使用表锁。

意向锁是表锁,共有两种:

  • 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
  • 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。

意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,InnoDB 会先获取该数据行所在数据表中的对应意向锁。

意向锁之间是互相兼容的。

IS 锁 IX 锁
IS锁 兼容 兼容
IX锁 兼容 兼容

意向锁和共享锁、排他锁互斥(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的共享锁和排他锁互斥)。

IS 锁 IX 锁
S锁 兼容 互斥
X锁 互斥 互斥

当前读和快照读

快照读(一致性非锁定读)就是单纯的 SELECT 语句,但不包括下面这两类。

# 共享锁,可以在 MySQL 5.7 和 MySQL 8.0 中使用。
SELECT ... LOCK IN SHARE MODE;
# 共享锁,可以在 MySQL 8.0 中使用
SELECT ... FOR SHARE;
# 排他锁
SELECT ... FOR UPDATE;	

快照读即记录的历史版本,每行记录可能存在多个历史版本(多版本技术)。
快照读的情况下,如果读取的记录正在执行 UPDATE / DELETE 操作,读取操作不会因此去等待记录上 X 锁的释放,而是会去读取行的一个快照。

只有在事务隔离级别 RC(读已提交)和 RR(可重复读)下,InnoDB才会使用一致性非锁定读。

  • 在 RC 级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据。
  • 在 RR 级别下,对于快照数据,一致性非锁定读总是读取本事务开始时的行数据版本。

当前读(一致性锁定读)就是给记录加 X 锁或 S 锁。

你可能感兴趣的:(MySQL,mysql,数据库)