锁是一种常见的并发事务的控制方式。
MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发写的情况下性非常差。InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level locking),默认为行级锁。
InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:
在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。
不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类。
由于 MVCC 的存在,对于一般的 SELECT
语句,InnoDB 不会加任何锁。不过,可以 通过以下语句显示加共享锁或排他锁。
# 共享锁,可以在 MySQL 5.7 和 MySQL 8.0 中使用。
SELECT ... LOCK IN SHARE MODE;
# 共享锁,可以在 MySQL 8.0 中使用
SELECT ... FOR SHARE;
# 排他锁
SELECT ... FOR UPDATE;
如果需要用到表锁的话,如何判断表中记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东西来快速判断是否可以对某个表使用表锁。
意向锁是表锁,共有两种:
意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,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才会使用一致性非锁定读。
当前读(一致性锁定读)就是给记录加 X 锁或 S 锁。