一、锁类型
1. 行锁(Record Lock)

核心定义:锁定单条记录的锁,InnoDB的默认锁类型,基于索引实现。

分类:

  • 共享锁(S锁):读锁,多个事务可同时持有,不阻塞其他S锁,阻塞X锁
  • 排他锁(X锁):写锁,仅一个事务可持有,阻塞其他所有锁

实现原理:

  • 基于索引实现,无索引时降级为表锁
  • 通过**锁槽(Lock Slot)**记录锁定信息,存储在索引页的锁链表中
  • 支持行级粒度,并发性能高

适用场景:

  • 高并发写操作,如电商订单更新、用户余额修改
  • 精准条件查询,如where id=1(id为主键)
2. 表锁(Table Lock)

核心定义:锁定整个表的锁,MyISAM的默认锁类型,InnoDB也支持。

分类:

  • 表共享锁(Table S Lock):所有事务可读,不可写
  • 表排他锁(Table X Lock):仅持有锁的事务可读写,其他事务不可读写
  • 元数据锁(MDL Lock):用于保护表结构,如ALTER TABLE时加MDL锁

实现原理:

  • 直接锁定表的元数据,不依赖索引
  • 锁定开销小,但并发性能差

适用场景:

  • 全表扫描或批量更新,如update table set column=value(无索引条件)
  • 表结构修改,如ALTER TABLE
  • MyISAM引擎的表(仅支持表锁)
3. 间隙锁(Gap Lock)

核心定义:锁定记录间的间隙,防止插入新记录,InnoDB特有的锁,用于解决幻读。

分类:

  • Gap Lock:仅锁定间隙,不包括记录本身
  • Next-Key Lock:Gap Lock+Record Lock,锁定间隙和记录本身(InnoDB默认的行锁算法)
  • Insert Intention Lock:插入操作时的意向锁,用于检测插入冲突

实现原理:

  • 基于索引的范围锁定,如where id between 1 and 10,锁定(1,10)的间隙
  • 锁定范围包括:
    • 索引记录间的间隙
    • 索引第一个记录前的间隙
    • 索引最后一个记录后的间隙

适用场景:

  • 范围查询,如select * from table where id > 10 for update
  • 防止幻读,确保同一事务内多次查询同一范围的结果一致

面试题:什么是间隙锁?
回答模板:

  • 定义:间隙锁是InnoDB特有的锁,用于锁定记录间的间隙,防止插入新记录
  • 作用:解决幻读问题,确保同一事务内多次查询同一范围的结果一致
  • 实现:基于索引的范围锁定,默认使用Next-Key Lock(Gap Lock+Record Lock)
  • 示例:查询where id between 1 and 10 for update时,锁定(1,10)的间隙,禁止插入id在2-9之间的新记录
二、意向锁(Intention Lock)
1. 核心定义

意向锁是表级锁,用于协调行锁和表锁,避免表锁检查所有行的锁状态,提高性能。

2. 分类
  • 意向共享锁(IS Lock):事务准备给行加共享锁前,先加IS锁
  • 意向排他锁(IX Lock):事务准备给行加排他锁前,先加IX锁
3. 作用机制
  • 当事务需要加表锁时,只需检查是否存在冲突的意向锁,无需遍历所有行的锁状态
  • 锁兼容性:
    请求锁类型已有IS已有IX
    IS锁✅ 兼容✅ 兼容
    IX锁✅ 兼容✅ 兼容
    表S锁✅ 兼容❌ 冲突
    表X锁❌ 冲突❌ 冲突
4. 与行锁/表锁的关系
  • 加锁顺序:先加意向锁(表级),再加行锁
  • 释放顺序:先释放行锁,再释放意向锁
  • 性能优化:表锁检查时,只需检查意向锁,无需遍历所有行,大幅减少锁检查开销
三、死锁产生与解决
1. 死锁定义

死锁是指两个或多个事务在执行过程中,因争夺锁资源而互相等待,无法继续执行的状态。

产生条件(四个同时满足):

  • 互斥条件:锁资源不可共享,一次只能被一个事务持有
  • 请求与保持条件:事务持有锁的同时,继续请求新的锁
  • 不剥夺条件:事务持有的锁只能由自己释放,不可被其他事务剥夺
  • 循环等待条件:多个事务形成循环等待链,如事务A等待事务B的锁,事务B等待事务A的锁
2. 死锁检测与处理

死锁检测:

  • 超时检测:当事务等待时间超过innodb_lock_wait_timeout(默认50秒),自动回滚超时事务
  • 死锁检测算法:InnoDB使用等待图(Wait-for Graph) 算法,定期检测循环等待,发现死锁后选择回滚代价最小的事务

处理方式:

  • 自动回滚:InnoDB自动回滚代价最小的事务(通常是修改行数最少的事务)
  • 手动干预:通过SHOW ENGINE INNODB STATUS查看死锁日志,手动终止冲突事务
3. 面试题:如何避免死锁?

回答模板:

  • 顺序加锁:所有事务按固定顺序请求锁,避免循环等待
    • 示例:事务A先锁表1再锁表2,事务B也先锁表1再锁表2
  • 超时机制:设置合理的innodb_lock_wait_timeout,避免长时间等待
  • 避免长事务:尽量缩短事务执行时间,减少锁持有时间
  • 使用乐观锁:高并发场景下,用版本号或时间戳替代悲观锁,如update table set value=value+1 where id=1 and version=2
  • 批量操作拆分:将大事务拆分为多个小事务,减少锁冲突概率
  • 使用索引:避免无索引导致的表锁,减少锁粒度
  • 避免范围查询:范围查询可能导致间隙锁,增加死锁概率,尽量使用精确查询
  • 定期清理死锁:通过监控工具(如Prometheus)定期检测死锁,及时处理
四、锁机制对比总结
锁类型粒度并发性能适用场景实现引擎
行锁行级高高并发写、精准查询InnoDB
表锁表级低全表扫描、批量更新MyISAM、InnoDB
间隙锁间隙级中范围查询、防止幻读InnoDB
意向锁表级高协调行锁和表锁InnoDB
五、InnoDB锁机制最佳实践
  1. 优先使用行锁:通过合理设计索引,避免行锁降级为表锁
  2. 避免无索引条件:所有写操作尽量使用索引,如where id=1而非where name='张三'(name无索引)
  3. 合理设计事务:缩短事务执行时间,减少锁持有时间
  4. 使用乐观锁替代悲观锁:高并发场景下,乐观锁性能更好
  5. 避免长事务:长事务容易导致锁积累,增加死锁概率
  6. 监控锁状态:通过SHOW ENGINE INNODB STATUS、information_schema.innodb_locks等监控锁状态

总结

锁机制是数据库并发控制的核心,不同锁类型适用于不同场景。行锁适合高并发写操作,表锁适合全表扫描,间隙锁用于解决幻读。意向锁协调行锁和表锁,提高性能。死锁的预防和处理是数据库运维的重要内容,通过顺序加锁、缩短事务、使用乐观锁等方式可有效避免死锁。

掌握锁机制的原理和最佳实践,对于优化数据库并发性能、避免死锁至关重要,是面试中的高频考点。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐