MyISAM 表一写就锁全表因其仅支持表级锁,任何写操作均对整表加写锁,阻塞其他读写请求;转InnoDB需检查磁盘空间、外键及全文索引兼容性。

MyISAM 表为什么一写就锁全表
MyISAM 只支持表级锁,INSERT、UPDATE、DELETE 任意一个写操作都会对整张表加写锁,其他写请求排队,读请求也得等——哪怕只更新一行,也会卡住所有并发写入。这不是配置问题,是存储引擎设计决定的。
常见现象:SHOW PROCESSLIST 里一堆 Locked 状态;慢查询日志里大量语句等待 Waiting for table level lock;QPS 上不去但 CPU 不高。
- 不能靠调大
key_buffer_size或优化索引缓解锁问题,那是缓存层面的事 -
CONCURRENT INSERT仅对尾部追加有效,且要求表中间没空洞(没 DELETE 过),实际场景覆盖有限 - 用
LOW_PRIORITY或DELAYED(已废弃)只是让写让路,并不减少锁持有时间
把 MyISAM 表转成 InnoDB 的实操要点
核心命令是 ALTER TABLE tbl_name ENGINE=InnoDB,但直接跑可能失败或引发意外,得提前检查几件事:
- 确认磁盘空间足够:InnoDB 行格式默认用
COMPACT,但转换过程会先建新表再拷数据,临时空间至少要等于原表大小 - 检查外键和全文索引:MyISAM 支持全文索引但不支持外键;InnoDB 支持外键但 5.6 之前不支持全文索引(5.7+ 才完整支持),若用了
FULLTEXT且 MySQL 版本 - 主键必须显式定义:InnoDB 强依赖聚簇索引,若原表没主键,MySQL 会自动加隐藏 ROW_ID,但会导致二级索引变大、查询变慢;务必在转换前补上合理主键(比如自增
id或业务唯一键) - 大表建议用
pt-online-schema-change或gh-ost在线改,避免锁表时间过长;小表可直接ALTER,但别在业务高峰执行
InnoDB 写并发提升的关键配置项
换引擎只是第一步,InnoDB 默认配置偏保守,需根据硬件和负载调优才能真正释放并发能力:
-
innodb_buffer_pool_size:设为物理内存的 50%–75%,太小会导致频繁刷脏页,抵消行锁优势 -
innodb_log_file_size:增大能减少 checkpoint 频率,降低写阻塞;但调整需停库重建日志文件,建议单个 log 文件 ≥ 256M(如innodb_log_file_size = 512M) -
innodb_flush_log_at_trx_commit = 1是默认值,保证 ACID,但每事务刷一次磁盘;若允许极短时间内的崩溃丢失(如日志系统),可设为2(每秒刷一次)提升吞吐 -
innodb_thread_concurrency = 0(默认),表示不限制并发线程数;设成非零值反而可能成为瓶颈,除非明确遇到线程调度争抢
注意:innodb_lock_wait_timeout 默认 50 秒,不是锁粒度控制参数,只是超时抛错时间,别误以为调小就能“减少锁”。
转换后还要验证什么
引擎改完不代表万事大吉,得确认行锁真生效、没隐性退化:
- 用
SELECT * FROM information_schema.INNODB_TRX查当前活跃事务,配合INNODB_LOCK_WAITS和INNODB_LOCKS(5.7+ 已移除,改用performance_schema.data_locks)看是否真锁到行而非表 - 执行
EXPLAIN FORMAT=JSON查关键 UPDATE/DELETE 语句,确认key字段有命中索引;没走索引的 UPDATE 会升级为表锁(InnoDB 的“伪表锁”) - 监控
Innodb_row_lock_waits和Innodb_row_lock_time_avg(来自SHOW STATUS),数值持续升高说明存在热点行竞争,得从应用层拆分或加缓存 - 留意
innodb_status输出里的SEMAPHORES和TRANSACTIONS部分,若看到大量s-lock on RW-shared或长时间未提交事务,可能是应用没正确 commit/rollback
最常被忽略的一点:应用代码里显式开启事务后,忘记 COMMIT 或异常没 ROLLBACK,会让锁一直挂着——InnoDB 行锁生命周期绑定事务,不是语句执行完就释放。


















