mysql如何处理锁升级问题_mysql行锁转表锁触发条件分析

0次阅读

mysql 如何处理锁升级问题_mysql 行锁转表锁触发条件分析

行锁为什么会“变成”表锁?根本就不是升级,而是退化

InnoDB 从不主动把行锁“升级”为表锁——它只是在无法精准加锁时,被迫对成百上千行逐个加行锁,结果等效于锁了整张表。真正触发这种退化的,是 WHERE 条件没走索引。

  • 常见错误现象:UPDATE t SET status = 1 WHERE name LIKE '%admin%' 执行极慢、阻塞其他事务,EXPLAIN 显示 type: ALL
  • 本质原因:没索引 → 全表扫描 → 每扫描一行就加一个行锁 → 锁住 5000+ 行后,InnoDB 可能直接拒绝执行或报 Lock wait timeout exceeded
  • 别信“阈值 5000 行就升级”的说法——这不是策略切换,是优化器放弃索引后的自然结果
  • 联合索引失效也一样:用 WHERE city = 'SZ' 却建了 (name, city) 索引,照样全扫

哪些操作会直接拿到表锁?不是隐式退化,而是明示加锁

只有显式调用或引擎强制行为才会真正获得表级锁,和行锁机制完全无关。

  • LOCK TABLES t WRITE:会话级独占锁,之后所有 DML 都被拦住;再执行 SELECT …… FOR UPDATE 直接报错 ERROR 1100 (HY000): Table 't' was not locked with LOCK TABLES
  • ALTER TABLETRUNCATE TABLE:DDL 本身不走 InnoDB 行锁逻辑,由 Server 层的元数据锁(MDL)控制,效果等同表锁
  • MyISAM 表任何写操作:该引擎压根不支持行锁,UPDATEINSERT 自动上表写锁
  • AUTO_INCREMENT 插入未命中主键路径时:InnoDB 会对整个自增索引段加锁,高并发下表现接近表锁

怎么确认当前锁是行锁还是表锁?别靠猜,查视图

MySQL 8.0+ 必须看 performance_schema.data_locks,这是唯一权威来源。

  • 查锁类型:SELECT LOCK_TYPE, LOCK_MODE, INDEX_NAME FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'db' AND OBJECT_NAME = 't';
  • LOCK_TYPE = 'TABLE' 是真表锁;LOCK_TYPE = 'RECORD' 是行锁(含间隙锁)
  • 配合 performance_schema.data_lock_waits 查谁在等谁:SELECT * FROM performance_schema.data_lock_waits;
  • 别依赖 SHOW ENGINE INNODB STATUSG 的“HOLDS THE LOCK(S)”—— 它只显示最近事务,漏掉静默持有者

如何避免“像表锁一样卡住”?核心就一条:让每条语句都走索引

不是加更多锁,而是让锁尽可能少、尽可能窄。

  • 模糊查询必须前置通配符?改用 FULLTEXT 或引入 Elasticsearch,别硬扛 LIKE '%x'
  • 批量删 / 改超万行?分批加 LIMITDELETE FROM log WHERE ts,循环执行
  • 字段上有索引但没生效?检查隐式转换:WHERE phone = 138phoneVARCHAR)会丢索引,必须写成 WHERE phone = '138'
  • 事务里混用 DML 和 DDL?绝对禁止。DDL 会触发隐式提交,导致前面的行锁提前释放,后续逻辑出错

最容易被忽略的一点:autocommit = 1 下写 SELECT …… FOR UPDATE 等于没锁——语句一结束锁就没了,必须先 START TRANSACTION 或设 SET autocommit = 0

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-15发表,共计1426字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources