一、锁总览类
Q1:InnoDB 有哪些锁?怎么分类?
按粒度:
全局锁:
FLUSH TABLES WITH READ LOCK,整库只读。表锁:表级 S/X 锁、意向锁、MDL。
行锁:记录锁、间隙锁、临键锁、插入意向锁。
按模式:
共享锁 S:读锁,
LOCK IN SHARE MODE。排他锁 X:写锁,
FOR UPDATE、UPDATE、DELETE。
Q2:共享锁和排他锁的区别?
S 锁:多个事务可同时持有,互相不阻塞。
X 锁:与任何锁互斥,一个事务持有,其他事务读写都阻塞。
兼容矩阵:
Q3:意向锁是什么?为什么需要?
表级锁,分意向共享锁 IS和意向排他锁 IX。
作用:快速判断表中是否有行锁,避免逐行检查。
当要给表加表级 S/X 锁(如
LOCK TABLES ... READ/WRITE、ALTER TABLE)时,如果表里已有行锁,就必须等待。若没有意向锁,数据库只能逐行扫描判断是否存在行锁,效率极低。
有了意向锁,只需检查表级意向锁标志即可快速判断"表里是否有行级锁",无需逐行检查。
意向锁是为了协调表级锁与行级锁——让表级锁能快速感知行级锁的存在。
加行 S 锁前先加 IS,加行 X 锁前先加 IX。
意向锁之间兼容,只与表级 S/X 锁冲突(IS 只与表级 X 冲突;IX 与表级 S、X 都冲突)。
Q4:MDL(元数据锁)是什么?
保护表结构,防止 DDL 与 DML 冲突。
读时加 MDL 读锁,写时加 MDL 写锁。
长事务持有 MDL 读锁,会阻塞 DDL,导致后续查询排队。
二、行锁类型类
Q5:记录锁、间隙锁、临键锁的区别?
Q6:间隙锁的作用是什么?
锁住一个区间,阻止其他事务在区间内插入。
目的:防止幻读(当前读场景)。
只在 RR 下生效,RC 下没有间隙锁。
Q7:临键锁和间隙锁的关系?
临键锁 = 记录锁 + 间隙锁。
RR 下默认加临键锁,锁住记录和它前面的间隙。
唯一索引等值命中时会退化为记录锁。
Q8:插入意向锁是什么?
一种特殊的间隙锁,表示“想在某个间隙插入”。
多个插入意向锁之间兼容,但与间隙锁冲突。
作用:提高插入并发。
插入意向锁(Insert Intention Lock)是 InnoDB 中一种特殊的间隙锁,专门用于
INSERT操作。理解它的关键在于:它不是独立的一种锁模式,而是间隙锁在插入场景下的具体表现。
三、加锁规则类
Q9:RR 下不同查询怎么加锁?
Q10:为什么没走索引会锁全表?
无法定位具体行,只能扫描并锁定扫描到的所有记录和间隙。
并发急剧下降,容易死锁。
所以 UPDATE/DELETE 的 WHERE 一定要走索引。
Q11:RC 和 RR 加锁有什么区别?
RC:没有间隙锁,只加记录锁,并发更高,但可能幻读。
RR:有间隙锁/临键锁,防幻读,但并发低、易死锁。
很多互联网业务用 RC + binlog ROW 提升并发。
Q12:SELECT ... FOR UPDATE 和 LOCK IN SHARE MODE 区别?
FOR UPDATE:加 X 锁,阻塞其他读写。LOCK IN SHARE MODE:加 S 锁,允许其他读,阻塞写。都是当前读,读最新数据。
四、MVCC 与锁的关系类
Q13:MVCC 能解决写写冲突吗?
不能。MVCC 只解决读写冲突,让读不加锁。
写写冲突仍靠锁,后写的事务阻塞。
Q14:快照读和当前读分别加锁吗?
快照读:普通 SELECT,走 MVCC,不加锁。
当前读:FOR UPDATE、UPDATE、DELETE,加锁读最新版本。
Q15:为什么 RR 下快照读不会幻读,当前读会?
快照读复用 ReadView,看不到新插入的行。
当前读读最新数据,会看到新插入的行,靠间隙锁阻止插入。
五、死锁类
Q16:死锁是怎么产生的?
两个或多个事务互相持有对方需要的锁,循环等待。
四个必要条件:互斥、持有并等待、不可抢占、循环等待。
Q17:如何排查死锁?
SHOW ENGINE INNODB STATUS,看LATEST DETECTED DEADLOCK。查看
information_schema.innodb_trx、innodb_locks、innodb_lock_waits。开启
innodb_print_all_deadlocks记录到 error log。
Q18:如何避免死锁?
按固定顺序访问资源。
小事务、快提交,减少持锁时间。
走索引,避免锁太多行。
降低隔离级别到 RC,减少间隙锁。
设置
innodb_lock_wait_timeout,超时自动放弃。
Q19:死锁发生后 MySQL 怎么处理?
InnoDB 有死锁检测,发现后回滚代价小的事务。
被回滚的事务报
Deadlock found,可重试。也可设
innodb_deadlock_detect=OFF,靠超时解决,但风险高。
Q20:锁等待超时和死锁的区别?
锁等待超时:等锁超过
innodb_lock_wait_timeout,报超时。死锁:循环等待,InnoDB 主动检测并回滚一个。
超时是被动等待,死锁是主动检测。
六、并发控制类
Q21:乐观锁和悲观锁的区别?
Q22:乐观锁怎么实现?
加版本号字段,更新时
WHERE version = ?,同时version = version + 1。影响行数为 0 说明被改过,重试。
也可用时间戳或状态字段。
Q23:如何提高并发?
降低隔离级别到 RC。
缩短事务,快提交。
走索引,减少锁范围。
读写分离,读走从库。
热点行拆分、排队、异步化。
Q24:热点行更新怎么优化?
拆分热点行,如账户余额拆多个子账户。
用队列串行化。
乐观锁 + 重试。
减少事务持有时间。
七、实战与陷阱题
Q25:UPDATE t SET x=1 WHERE id=1 加了什么锁?
id 是主键,唯一索引等值命中,加记录锁。
只锁 id=1 这一行。
Q26:UPDATE t SET x=1 WHERE name='a',name 有普通索引,加什么锁?
普通索引等值,加临键锁 + 间隙锁。
可能锁住 name='a' 的记录和相邻间隙。
Q27:UPDATE t SET x=1 WHERE age>20,age 有索引,加什么锁?
范围查询,加临键锁,锁住扫描到的区间。
如果 age 无索引,锁全表。
Q28:为什么 RR 下容易死锁?
间隙锁导致锁范围大,多个事务交叉加锁易循环等待。
降到 RC 可减少间隙锁,降低死锁概率。
Q29:SELECT 会加锁吗?
普通 SELECT 不加锁,走 MVCC。
FOR UPDATE/LOCK IN SHARE MODE加锁。
Q30:表锁和行锁怎么选?
InnoDB 默认行锁,并发高。
无索引或全表操作会退化为表锁。
MyISAM 只有表锁。
评论