虾皮面试原题:深入探讨MySQL中的表级锁与行级锁及其关键区别,提升数据库性能与安全性
理解表级锁与行级锁的差异
MyISAM 仅支持表级锁(table-level locking),一旦加锁,整张表都会被锁定,这在并发写操作时性能非常低下。而 InnoDB 则同时支持表级锁和行级锁(row-level locking),默认情况下采用行级锁。行级锁的粒度更小,仅对相关记录加锁(可以是单行或多行),因此在进行并发写操作时,InnoDB 的性能显著提高。
表级锁与行级锁的对比:
- 表级锁:这是 MySQL 中粒度最大的一种锁,主要用于非索引字段。加锁时会锁定整张表,操作简单,资源消耗也较少,加锁速度快,不容易导致死锁。然而,由于其锁定粒度较大,触发锁冲突的概率也相对较高,导致并发度较低。MyISAM 和 InnoDB 引擎都支持此类型锁。
- 行级锁:这是 MySQL 中粒度最小的锁,主要用于索引字段。行级锁只会锁定当前操作的记录,大幅减少数据库操作之间的冲突。虽然其加锁粒度最小,支持更高的并发,但加锁的开销也最大,因此加锁速度相对较慢,并且可能会出现死锁现象。
行级锁使用时的注意事项
InnoDB 的行锁主要针对索引字段,而表级锁则用于非索引字段。当我们执行 UPDATE
或 DELETE
语句时,如果 WHERE
条件中的字段没有命中索引或索引失效,就会导致全表扫描,从而对所有记录进行加锁。这是日常开发中常见的问题,我们需要特别注意!
有时,即使使用了索引,也可能会导致全表扫描,这通常是由于 MySQL 优化器的原因。
共享锁与排他锁的区别
无论是表级锁还是行级锁,都存在两种锁:共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁):
- 共享锁(S 锁):又称读锁,事务在读取记录时获取共享锁,允许多个事务同时获取(锁兼容)。
- 排他锁(X 锁):又称写锁或独占锁,事务在修改记录时获取排他锁,不允许其他事务同时获取。这意味着,如果某个记录已经被加了排他锁,其他事务不能再对该记录加任何类型的锁(锁不兼容)。
排他锁与任何其他锁都不兼容,而共享锁仅与共享锁兼容。
S 锁 | X 锁 | |
---|---|---|
S 锁 | 不冲突 | 冲突 |
X 锁 | 冲突 | 冲突 |
由于 MVCC 的存在,对于普通的 SELECT
语句,InnoDB 不会加任何锁。但您可以通过以下语句显式加共享锁或排他锁:
# 共享锁
SELECT ... LOCK IN SHARE MODE;
# 排他锁
SELECT ... FOR UPDATE;
意向锁的作用
在需要使用表锁时,如何判断表中的记录是否被行锁占用呢?逐行遍历显然效率不高,这时候就可以用到意向锁来快速判断是否可以对某个表加锁。
意向锁是一种表级锁,主要有两种:
- 意向共享锁(Intention Shared Lock,IS 锁):表示事务有意向对表中的某些记录加共享锁,在加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(Intention Exclusive Lock,IX 锁):表示事务有意向对表中的某些记录加排他锁,加排他锁前必须先取得该表的 IX 锁。
意向锁由数据引擎自己维护,用户无法手动操作。在为数据行加共享或排他锁之前,InnoDB 会首先获取该数据行所在表的意向锁。意向锁之间是相互兼容的。
IS 锁 | IX 锁 | |
---|---|---|
IS 锁 | 兼容 | 兼容 |
IX 锁 | 兼容 | 兼容 |
意向锁与共享锁和排他锁是互斥的(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的共享锁和排他锁互斥)。
IS 锁 | IX 锁 | |
---|---|---|
S 锁 | 兼容 | 互斥 |
X 锁 | 互斥 | 互斥 |
InnoDB 的行锁种类
MySQL 的 InnoDB 存储引擎支持三种行锁定方式:
- 记录锁(Record Lock):也称为记录锁,锁定单个行记录。
- 间隙锁(Gap Lock):锁定一个范围,不包括记录本身。
- 临键锁(Next-key Lock):结合了记录锁和间隙锁,锁定一个包含记录本身的范围。记录锁只能锁定已存在的记录,为了避免插入新记录,需要依赖间隙锁。
InnoDB 的默认隔离级别 REPEATABLE-READ(可重读)能够有效防止幻读问题,主要有以下两种情况:
- 快照读:通过 MVCC 机制确保幻读不会出现。
- 当前读:采用 Next-Key Lock 进行加锁以确保不会出现幻读。