跳转到主内容
极星编程网:以代码为星,赴技术山海!

mysql数据写入时发生锁竞争_通过减少事务锁粒度提升执行并发

UPDATE会锁整张表是因为WHERE条件未命中索引,导致InnoDB退化为表级锁;正确使用索引、避免隐式转换、优化事务粒度可避免该问题。 为什么
UPDATE
会锁整张表而不是只锁行 MySQL 的 InnoDB 引擎默认走行级锁,但前提是必须命中索引。如果
UPDATE
或
DELETE
语句的
WHERE
条件没走索引(比如用了函数、隐式类型转换、或字段无索引),InnoDB 就会退化为锁全表——这时并发写入直接排队,看着像卡死。 实操建议: 用
EXPLAIN
看执行计划,确认
type
是
ref
/
range
而非
ALL
,且
key
列显示实际使用的索引 避免在
WHERE
里对索引列做运算:
WHERE YEAR(create_time) = 2024
→ 改成
WHERE create_time >= '2024-01-01' AND create_time
检查字段类型是否一致:比如
user_id
是
BIGINT
,但传了字符串
'123'
,会触发隐式转换导致索引失效
SELECT ... FOR UPDATE
锁范围比你想的更大 很多人以为加了主键条件就只锁那一行,其实 InnoDB 的间隙锁(Gap Lock)和临键锁(Next-Key Lock)会让锁覆盖“值之间的空隙”。比如表里有
id
值 1、5、10,执行
SELECT * FROM t WHERE id > 3 FOR UPDATE
,可能锁住 (3,5)、(5,10)、(10,+∞) 这几个间隙,阻塞其他事务插入 4、6、11 等值。 实操建议: 业务允许时,把
SELECT ... FOR UPDATE
换成
UPDATE ... WHERE
原子操作,减少锁持有时间 确认隔离级别:如果业务能接受可重复读以外的一致性,可设为
READ COMMITTED
,它不启用间隙锁(但要注意幻读风险) 高并发更新单条记录时,优先用带主键等值条件的
UPDATE
,避免范围条件 事务太长是并发杀手,不是锁粒度问题 锁粒度再小,如果事务从开连接、查数据、算逻辑、再更新,耗时 200ms,那这 200ms 内锁就一直占着。很多“锁竞争”本质是事务臃肿,不是 SQL 没优化。 MySQL(Linux) MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。 下载 实操建议: 把非数据库操作(比如调外部 API、格式化字符串)全挪到事务外,
BEGIN
和
COMMIT
之间只留必要 SQL 批量写入别用循环单条
INSERT
,改用
INSERT INTO ... VALUES (...), (...), (...)
一条语句搞定 查+改场景慎用“先查后更”:如果只是计数器自增,直接
UPDATE t SET cnt = cnt + 1 WHERE id = ?
更安全 唯一索引冲突会引发额外的插入意向锁 当多个事务同时
INSERT
同一个唯一键(比如重复注册邮箱),即使还没提交,InnoDB 也会加插入意向锁(Insert Intention Lock),它和间隙锁冲突,导致事务互相等待——现象是
SHOW ENGINE INNODB STATUS
里看到
lock_mode X insert intention waiting
。 实操建议: 应用层提前校验:插入前先
SELECT
一次(不加锁),命中则直接返回,避免撞唯一索引 用
INSERT IGNORE
或
ON DUPLICATE KEY UPDATE
替代普通
INSERT
,失败不抛异常,减少事务回滚开销 如果业务允许,把唯一约束从数据库下推到应用缓存(如 Redis
SETNX
)做第一道过滤 锁竞争真正的复杂点不在“怎么加锁”,而在于“锁什么时候释放”和“锁住了什么”。很多问题表面看是并发低,实际是某条慢查询拖住了整个事务,或者一个没注意的全表扫描让行锁变表锁。盯住
INFORMATION_SCHEMA.INNODB_TRX
和
INNODB_LOCK_WAITS
,比盲目调参数有用得多。

相关文章