




InnoDB行锁退化为表锁是因为WHERE条件未命中索引,导致全表扫描并逐行加锁;常见原因包括非索引字段查询、隐式类型转换、函数操作列等。
InnoDB 默认用行锁,但锁的粒度实际取决于是否命中索引。如果 WHERE 条件没走索引(比如对非索引字段查

常见诱因包括:
SELECT * FROM user WHERE JSON_EXTRACT(profile, '$.age') = 25:JSON 字段上无虚拟列索引,无法走索引SELECT * FROM order WHERE order_no = 12345,但 order_no 是 VARCHAR 类型,传入的是整数 12345 → 触发隐式转换,索引失效UPDATE 前先做了 SELECT ... FOR UPDATE 但没加索引条件,导致锁住所有聚簇索引记录验证方式:执行 SHOW ENGINE INNODB STATUS\G,关注 TRANSACTIONS 部分的 lock_mode X locks rec but not gap 行数,若远超预期更新行数,大概率是锁范围过大。
长事务不仅占用锁资源,还会阻碍 purge 线程清理 undo log,导致 ibdata1 持续膨胀、历史版本链过长,最终使 SELECT 也变慢(需遍历更长的版本链)。
关键控制点:
INSERT ... ON DUPLICATE KEY UPDATE 或分页 LIMIT + 循环,单次事务控制在 500 行以内information_schema.INNODB_TRX 表,设置告警阈值:事务运行时间 > 60 秒 或 TRX_ROWS_LOCKED > 10000示例:错误写法 BEGIN; SELECT balance FROM account WHERE id = 1001; sleep(5); UPDATE account SET balance = balance - 100 WHERE id = 1001; COMMIT; —— 这 5 秒内余额行一直被锁,高并发下直接卡死。
innodb_buffer_pool_size 不是越大越好。设太高会导致系统内存不足,触发 Linux OOM Killer 杀 MySQL;设太低则频繁磁盘读,Innodb_buffer_pool_wait_free 计数器飙升,QPS 断崖下跌。
实操建议:
free -h 中 available 是否稳定 > 1GBInnodb_buffer_pool_read_requests / Innodb_buffer_pool_reads,比值 5000 说明 buffer pool 利用率高,但不意味着还能继续加innodb_buffer_pool_instances = 8(当 pool > 1GB 时),减少内部 mutex 争用从库延迟不是主库慢,而是从库 SQL 线程(apply relay log)跟不上。常见根因不是 CPU 或磁盘,而是单线程回放瓶颈或锁等待。
排查路径:
SHOW SLAVE STATUS\G,重点看 Seconds_Behind_Master 和 Slave_SQL_Running_State —— 若长期卡在 Waiting for dependent transaction to commit,说明启用了 slave_parallel_type = LOGICAL_CLOCK 但事务依赖太强performance_schema.events_statements_history_long,过滤 THREAD_ID 对应 SQL 线程,找执行时间最长的语句(常是大 UPDATE 或 ALTER TABLE)innodb_flush_log_at_trx_commit = 1 + sync_binlog = 1:这种强一致性配置会让从库每次事务都刷盘,在机械盘上极易成为瓶颈WHERE id BETWEEN ? AND ? 的小批次,避免单事务锁表太久真正治本的方式不是调参,而是从业务侧避免「一个事务改几万行」,这类操作在主从架构下天然就是扩展瓶颈。