虾皮面试原题:深入探讨MySQL中的表级锁与行级锁及其关键区别,提升数据库性能与安全性

理解表级锁与行级锁的差异

MyISAM 仅支持表级锁(table-level locking),一旦加锁,整张表都会被锁定,这在并发写操作时性能非常低下。而 InnoDB 则同时支持表级锁和行级锁(row-level locking),默认情况下采用行级锁。行级锁的粒度更小,仅对相关记录加锁(可以是单行或多行),因此在进行并发写操作时,InnoDB 的性能显著提高。

表级锁与行级锁的对比

  • 表级锁:这是 MySQL 中粒度最大的一种锁,主要用于非索引字段。加锁时会锁定整张表,操作简单,资源消耗也较少,加锁速度快,不容易导致死锁。然而,由于其锁定粒度较大,触发锁冲突的概率也相对较高,导致并发度较低。MyISAM 和 InnoDB 引擎都支持此类型锁。
  • 行级锁:这是 MySQL 中粒度最小的锁,主要用于索引字段。行级锁只会锁定当前操作的记录,大幅减少数据库操作之间的冲突。虽然其加锁粒度最小,支持更高的并发,但加锁的开销也最大,因此加锁速度相对较慢,并且可能会出现死锁现象。

行级锁使用时的注意事项

InnoDB 的行锁主要针对索引字段,而表级锁则用于非索引字段。当我们执行 UPDATEDELETE 语句时,如果 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 进行加锁以确保不会出现幻读。