MyISAM索引更新比InnoDB慢得多,因其采用表级锁、全索引重写、无事务回滚、无change buffer且每次更新立即刷盘;而InnoDB通过聚簇索引原地更新、change buffer延迟写入及合理主键设计大幅提升效率。

MyISAM 的索引更新为什么比 InnoDB 慢得多
MyISAM 使用表级锁,任何 UPDATE、DELETE 或 INSERT 都会锁住整个表的 .MYI 索引文件,期间其他写操作必须排队。即使只改一行,所有索引(包括非目标列上的索引)都会被整体重写——因为 MyISAM 的索引叶节点直接存数据行的物理偏移,行位置一变,所有索引项就得重算地址。
常见错误现象:Waiting for table metadata lock 或长时间 Updating 状态;高并发写入时 QPS 断崖下跌。
- MyISAM 不支持事务,索引更新无法回滚,失败后可能留下不一致的 .MYI 文件
- 没有 change buffer 机制,每次索引变更都立即刷盘,磁盘 I/O 压力直线上升
- 全文索引虽快,但仅限于 MyISAM,且更新时仍触发全表索引重建
InnoDB 的聚簇索引如何让主键更新更高效
InnoDB 把数据和主键索引存在一起(聚簇索引),所以按主键 UPDATE 时,只要新旧记录大小相近,InnoDB 可以原地修改页内记录,无需移动数据行,二级索引也只需更新对应指针。
但注意:如果 UPDATE 导致行长度超过页剩余空间,就会触发页分裂,此时不仅主键索引要调整,所有二级索引的叶节点都要更新指向新位置的指针——这正是“看似只改一个字段,却卡住好几秒”的根源。
- 主键尽量用
BIGINT或INT,避免用长字符串或 UUID,减少二级索引体积和更新开销 -
UPDATE语句里别写无关字段,例如SET name = ?, email = ?却只改了name,MySQL 仍会重写整行 - 使用
innodb_change_buffering = all(默认)可延迟非唯一二级索引更新,降低写放大
唯一索引 vs 普通索引在更新时的性能差异
唯一索引要求实时校验重复性,每次 INSERT 或 UPDATE 都必须立刻查 B+ 树确认无冲突,不能走 change buffer 缓存;而普通二级索引可以延迟合并,批量写入更高效。
典型场景:用户注册表有 email 唯一索引,每插入一条就触发一次索引树搜索 + 加锁;若改成普通索引+应用层去重,写入吞吐能翻倍,但需承担业务侧重复风险。
- 唯一约束本质是强制同步校验,对写密集型表是隐性瓶颈
-
DROP INDEX后再ADD INDEX重建索引,比ALTER TABLE ... MODIFY COLUMN更快——后者会触发行迁移和所有索引重写 - 用
pt-online-schema-change工具做索引变更时,它默认禁用唯一索引的在线 DDL 优化路径,需手动加--no-check-unique-key-change
索引统计信息不准导致更新计划恶化
InnoDB 的 cardinality(基数)统计值如果长期没更新,优化器可能误判索引选择率,选错执行计划——比如该走索引的 UPDATE ... WHERE status = 'pending' 被当成全表扫描,锁住更多行,拖慢整体更新速度。
运行 ANALYZE TABLE 会重新采样,但默认只采样 8 个叶子页,大表误差仍可达 ±30%;innodb_stats_persistent = ON 并配合 innodb_stats_auto_recalc = ON 才能较可靠地维持统计质量。
- 不要依赖
SHOW INDEX查到的Cardinality值做容量预估,它只是估算 -
UPDATE带ORDER BY或LIMIT时,若统计偏差大,可能跳过本该命中的索引范围 - 监控
INFORMATION_SCHEMA.INNODB_SYS_INDEXES中的n_fields和page_size,能辅助判断索引是否膨胀严重
UNIQUE KEY 更快更稳。










