myisam高并发写入卡死因仅支持表级锁,写操作锁全表,读写均需排队;innodb主键应选自增整型避免随机值导致页分裂;innodb_file_per_table=on必须开启;count(*)性能差异源于实现方式而非引擎本质;innodb默认具备事务与崩溃恢复能力。

MyISAM 为什么在高并发写入时会卡死
因为 MyISAM 只支持表级锁,一次 INSERT 或 UPDATE 就会锁住整张表,其他写操作必须排队。读操作虽然可以并发,但只要有一个写请求进来,所有后续的读、写都得等它完成。
常见错误现象:SHOW PROCESSLIST 里一堆 Locked 状态;慢查询日志中大量 Waiting for table level lock;QPS 上不去,但 CPU 和磁盘都不高。
- 适用场景仅限于:几乎只读、数据量小、允许崩溃后手动修复(比如静态配置表、日志归档表)
-
INSERT DELAYED在 MySQL 5.6+ 已被移除,别再依赖它缓解写压力 - 即使加了
READ LOCAL,也只能让部分INSERT不阻塞读,无法解决写写冲突
InnoDB 的主键设计不当会导致全表扫描
InnoDB 的聚簇索引把数据按主键物理排序存储,如果主键是随机值(比如 UUID 或 MD5),新记录会频繁插入到页中间,引发大量页分裂和碎片;更严重的是,没有合适主键时,MySQL 会悄悄创建隐藏的 row_id,但这个值不暴露、不可索引、无法用于范围查询。
典型表现:明明有 WHERE created_at > '2024-01-01',执行计划却显示 type: ALL;ORDER BY id DESC LIMIT 20 越往后越慢。
- 推荐用自增
INT或BIGINT作主键,避免业务字段(如手机号、邮箱)直接当主键 - 如果必须用字符串 ID,至少保证有序性,比如
Snowflake生成的数字字符串,而非UUID() -
innodb_file_per_table=ON必须开启,否则删表后空间不释放,且无法单独优化单表
count(*) 性能差异不是引擎问题,而是实现方式问题
MyISAM 把总行数存在磁盘元数据里,SELECT COUNT(*) FROM t 是 O(1);InnoDB 每次都得扫索引树统计,所以看起来“慢”。但这只是全表无条件统计的特例,一旦加了 WHERE,两者都要走索引或扫描,差距就没了。
容易踩的坑:EXPLAIN 显示 rows 是估算值,不能代替真实 COUNT;用 COUNT(1) 或 COUNT(id) 并不比 COUNT(*) 快——MySQL 优化器早就做了等价处理。
- 真要快速估算?用
SHOW TABLE STATUS LIKE 't'查Rows字段,但注意它是采样估算,误差可能达 40% - 高频精确计数场景(如分页总数),建议用缓存(Redis)或冗余字段(如
total_count在父表里维护) -
InnoDB下count(*)会优先走最小二级索引(如果有),而不是主键索引,所以建个INDEX(status)有时反而加速count(*)
事务与崩溃恢复能力不是可选项,而是默认开关
MyISAM 没有事务、没有 CRASH SAFE,断电或 kill -9 后表很可能损坏,只能靠 myisamchk 修复,且无法保证数据一致性;InnoDB 默认开启 innodb_flush_log_at_trx_commit=1 和 sync_binlog=1,只要 redo log 写成功,崩溃重启就能恢复到事务提交点。
关键区别不在“是否支持”,而在“是否默认启用并可靠生效”:很多老项目把 InnoDB 当成 MyISAM 用,关掉双写缓冲(innodb_doublewrite=OFF)、调大日志刷盘间隔(innodb_flush_log_at_trx_commit=2),结果既没换来多少性能,又丢了可靠性。
- 除非你明确接受丢失最近 1 秒事务的风险,否则不要动
innodb_flush_log_at_trx_commit -
innodb_log_file_size太小(如默认 48MB)会导致频繁 checkpoint,影响写入吞吐;建议设为 1–2GB,但修改前必须停库、删除旧日志文件 - 备份时用
mysqldump --single-transaction对InnoDB有效,对MyISAM无效——后者仍需全局读锁
真正难的不是选引擎,而是选完之后敢不敢关掉 MyISAM 的遗留配置(比如 key_buffer_size 调太高),以及愿不愿意为 InnoDB 的正确用法重构主键和索引逻辑。











