带外键的表插入操作是否会影响关联同一张父表的其他表?
InnoDB外键关联下的锁行为详解
嘿,这个问题问到了InnoDB外键锁机制里很容易被忽略的关键点,我来给你掰扯清楚!
先明确基础逻辑
当你往log表插入(或更新、删除)包含外键id_user的记录时,InnoDB会自动触发外键约束检查:它会去user表校验对应的id_user记录是否存在。为了保证在当前事务完成前,这条关联的user记录不会被破坏(比如被删除、修改主键),InnoDB会给user表中匹配的那条记录自动加上共享锁(S锁)——这个锁是隐式添加的,不需要你手动写锁语句。
不同事务锁请求的表现
1. 另一事务请求共享锁(S锁):完全兼容
S锁和S锁是互相兼容的,多个事务可以同时持有同一条记录的S锁。比如:
- 另一个事务也往
log表插入关联同一个id_user的记录 - 或者执行
SELECT * FROM user WHERE id_user=1 LOCK IN SHARE MODE主动加S锁
这些操作都能正常执行,不会被阻塞。
2. 另一事务请求排他锁(X锁):直接阻塞
S锁和X锁是互斥的,如果另一事务尝试对这条user记录加X锁,或者执行会触发X锁的操作,就会被卡住,直到第一个插入log的事务提交/回滚、释放S锁为止。常见的触发场景包括:
- 执行
SELECT * FROM user WHERE id_user=1 FOR UPDATE主动加X锁 - 尝试删除这条
user记录:DELETE FROM user WHERE id_user=1 - 尝试修改这条
user记录的主键id_user(虽然一般主键不会这么改,但逻辑上会触发X锁)
举个实际场景例子
事务1(插入日志,触发外键S锁)
START TRANSACTION; -- 插入关联user表id=1的日志 INSERT INTO log (id_user, info) VALUES (1, '用户1发起了登录操作'); -- 此时user表id=1的记录被InnoDB隐式加上了S锁,事务未提交,锁未释放
事务2的不同操作结果
START TRANSACTION; -- 情况1:请求S锁,正常执行 SELECT * FROM user WHERE id_user=1 LOCK IN SHARE MODE; -- 情况2:请求X锁,被阻塞,直到事务1提交/回滚 SELECT * FROM user WHERE id_user=1 FOR UPDATE; -- 情况3:尝试删除user记录,被阻塞 DELETE FROM user WHERE id_user=1;
额外注意点
- 如果插入
log时,id_user对应的user记录不存在,InnoDB会直接抛出外键约束错误,不会给user表加任何锁。 - 这种隐式外键锁很容易被忽略,如果你发现
user表的某些记录莫名被锁,先检查是不是关联表(log、message)的事务在操作时触发了外键锁。
内容的提问来源于stack exchange,提问作者Luís Henrique Faria
相关产品推荐
相关产品推荐

