MySQL的锁机制是实现并发控制的核心手段之一,它通过在特定资源(如表、行、页等)上加锁来保护数据的一致性和完整性,防止并发事务间的相互干扰。
1. 锁的分类
MySQL支持多种类型的锁,按照锁定粒度和作用范围大致可分为以下几类:
全局锁
- FLUSH TABLES WITH READ LOCK:对整个数据库实例加全局只读锁,主要用于全库逻辑备份,阻止其他任何对表的写操作。
表级锁
- 表锁:锁定整张表,包括
LOCK TABLES
语句显式锁定和在某些条件下InnoDB自动采取的表锁。 - 元数据锁(MDL, Metadata Lock):在对表进行DDL操作(如ALTER TABLE)时自动加锁,确保在同一时间只有一个会话能修改表结构,其他会话只能读取表的元数据。
行级锁
- 行锁:锁定表中特定的一行或多行数据,主要由InnoDB存储引擎提供,适用于高并发场景,可以最大程度地减少锁争用,提高并发性能。
- 共享锁(S锁):允许事务读取一行数据,多个事务可以同时持有同一数据项的共享锁。
- 排他锁(X锁):允许事务更新或删除一行数据,一个数据项上只能有一个事务持有排他锁,其他事务不能同时持有任何锁。
意向锁
- 意向共享锁(IS锁):事务打算在某些行上加共享锁时,首先在表上加意向共享锁。
- 意向排他锁(IX锁):事务打算在某些行上加排他锁时,首先在表上加意向排他锁。
- 意向锁的存在是为了支持行锁和表锁的同时使用,它们不会与行锁冲突,但会与表锁冲突,从而避免了行锁与表锁的直接交互,简化了锁的管理。
2. 锁的兼容性
不同类型的锁之间存在兼容性关系,决定了一个事务是否可以在已有锁的情况下申请其他锁。一般规则如下:
- 共享锁与共享锁兼容:多个事务可以同时持有同一数据项的共享锁。
- 共享锁与意向共享锁兼容:一个事务持有表的意向共享锁时,其他事务可以继续在该表的行上加共享锁。
- 排他锁与任何锁都不兼容:一旦事务对数据项加了排他锁,其他任何事务都无法再对其加任何类型的锁。
- 意向锁之间兼容:意向锁间相互兼容,且与相应的行锁兼容。
3. 锁的获取与释放
- 显式锁:通过特定SQL语句(如
LOCK TABLES
、SELECT ... FOR UPDATE
、SELECT ... LOCK IN SHARE MODE
等)显式申请锁。 - 隐式锁:在执行DML语句(如INSERT、UPDATE、DELETE)时,InnoDB自动为涉及的数据行加锁。
- 锁的释放:通常在事务结束(提交或回滚)时自动释放所有持有的锁。在某些情况下,如使用
UNLOCK TABLES
或调用存储过程GET_LOCK()
、RELEASE_LOCK()
时也可手动释放锁。
4. 死锁检测与处理
在并发事务中,由于锁请求和释放的顺序不当,可能导致循环等待锁的现象,形成死锁。MySQL通过死锁检测机制发现此类情况,并自动选择牺牲其中一个事务(回滚事务并释放其持有的锁),打破死锁循环。
5. 锁升级与降级
在某些条件下,MySQL的锁机制可能会进行锁的升级(如从行锁升级到表锁)或降级(如从表锁降级到行锁),以适应不同场景的并发控制需求,但通常应尽量避免不必要的锁升级以保持高并发性能。
6. 锁与隔离级别的配合
不同的事务隔离级别下,MySQL通过调整锁的使用策略来实现不同的并发访问隔离程度,以满足不同的业务需求。例如,在REPEATABLE READ
隔离级别下,InnoDB使用Next-Key Locking策略和MVCC机制结合,既避免了幻读又保持了较高的并发性能。