MyISAM简单查询快因其无事务、无行锁、数据顺序存储且索引直接存物理偏移,但崩溃易丢数据、不支持高并发写与复杂查询。

MyISAM 为什么在简单查询里快?因为没事务、没行锁、数据直接堆着放
MyISAM 查询快,不是因为它“优化得好”,而是它压根没做很多现代数据库要做的事。它不支持事务、不支持行级锁、不维护外键约束,所有数据以固定格式顺序写入文件,MYD 存数据、MYI 存索引,结构像一张大表格贴在磁盘上——读的时候跳过一堆逻辑判断,自然快。
但这只适用于特定场景:单写多读、全表扫描少、WHERE 条件能命中索引、并发更新极低。一旦有写操作或复杂 JOIN,优势立刻反转。
MyISAM 索引是“全索引遍历 + 回表”还是“直接定位”?其实是后者,但仅限主键和唯一索引
MyISAM 的索引文件(MYI)里,每个索引项直接存的是数据行在 MYD 文件里的字节偏移量(offset)。查 id = 100 这种主键时,B+ 树找到对应 offset,直接 fseek 到位置读取整行——没有“回表”概念,因为索引里本来就有物理地址。
但注意:WHERE name = 'xxx' 这种非唯一二级索引,查到的 offset 可能指向多行(如果有重复值),MyISAM 会从该 offset 开始线性扫描后续行,直到不匹配为止——这比 InnoDB 的聚簇索引更脆弱,尤其当重复值多时。
- 唯一索引和主键:一次定位,O(log n) + O(1) 磁盘寻址
- 普通二级索引:可能触发局部线性扫描,最坏退化成 O(n)
- 没有“覆盖索引”优化意识:即使
SELECT id FROM t WHERE name = 'x',仍会读整行,不跳过字段
MyISAM 的 COUNT(*) 为什么秒出?因为它真存了行数
InnoDB 要算 COUNT(*) 得扫聚簇索引或采样估算,而 MyISAM 在表的元信息(.frm 文件 + 内存缓存)里实时维护一个 row_count 计数器。每次 INSERT/DELETE 都增减它,所以 SELECT COUNT(*) FROM t 直接返回内存值,不碰数据文件。
但这带来严重陷阱:
- 如果表被其他客户端用
myisamchk修复或手工修改过,计数器可能失效,需手动ANALYZE TABLE同步 -
WHERE条件下的COUNT(*)(如COUNT(*) WHERE status=1)依然要扫描,不享受这个优化 - 并发写入下,计数器靠表级锁保护,高写场景反而成瓶颈
MyISAM 快的代价是什么?崩溃后数据大概率丢,且无法精确恢复
MyISAM 没有 WAL(预写日志),没有 doublewrite buffer,也没有崩溃恢复机制。写入时先改 MYD,再更新 MYI,中间断电或 kill -9,轻则索引与数据错位(Table is marked as crashed),重则整行数据损坏。修复只能靠 REPAIR TABLE 或 myisamchk,且不能保证逻辑一致性。
这也是为什么现在连只读报表库都很少用 MyISAM:SSD 随机读性能提升让 InnoDB 的“慢”不再明显,而 MyISAM 的不可靠在分布式、容器化、自动扩缩容环境下被无限放大。
真正容易被忽略的点是:MyISAM 的“快”高度依赖操作系统页缓存和磁盘顺序读能力;一旦数据量超过内存、或查询模式变随机,它的优势会迅速消失,甚至比 InnoDB 更慢——因为 InnoDB 至少还有自适应哈希、缓冲池预读等机制兜底。


















