InnoDB has been the default storage engine since MySQL 5.5 — so why does MyISAM still show up in production incidents, and what actually breaks when a table you assumed was InnoDB turns out to be MyISAM?
Quick Answer
MyISAM tables survive in production mostly through inherited legacy schemas, tables created explicitly with ENGINE=MyISAM by an old migration script, or MySQL system tables in very old versions — and the failure mode is that MyISAM has no crash recovery and no transaction support, so a MyISAM table can silently end up corrupted or partially written after an unclean shutdown in a way InnoDB's redo log would have prevented outright.
Detailed Answer
Think of InnoDB like a bank that keeps a detailed transaction journal of every step of every transfer, so that if the power goes out mid-transfer, the bank can replay the journal on restart and know exactly which transfers were fully completed, which were only half-done and must be reversed, and which never started. MyISAM is like a bank that just directly edits the ledger in place with no journal — if the power goes out mid-edit, there's no record of what state the edit was in, and the ledger page itself might be left half-written or corrupted with no way to automatically detect or repair it.
InnoDB became the default engine in MySQL 5.5 specifically because it provides ACID transactions (Atomicity, Consistency, Isolation, Durability — the guarantees that a transaction either fully happens or not at all, and survives a crash), row-level locking for concurrency, and crash recovery via a redo log and doublewrite buffer that let it detect and repair incomplete writes on restart. MyISAM predates all of this: it uses table-level locking (an entire table is locked for any write, killing concurrency under write-heavy load), has no transaction support at all, and has no crash recovery mechanism — a MyISAM table that was mid-write during a crash can come back corrupted, requiring a manual REPAIR TABLE that itself can lose data.
MyISAM tables persist in real environments for a few specific, recurring reasons: schemas migrated from very old MySQL installations that were never explicitly converted; ORM or migration tooling that hardcoded ENGINE=MyISAM years ago and nobody revisited; and historically, MyISAM's fulltext search support being a reason teams chose it before InnoDB gained fulltext indexing in 5.6. The danger is that a table's engine is easy to overlook in day-to-day work — SELECT and INSERT statements behave identically regardless of engine until something goes wrong, so a MyISAM table can sit unnoticed in a schema for years until the exact moment it matters most: during a crash, a failover, or a concurrency spike.
In production, the actual incident pattern looks like this: a server experiences an unclean shutdown (OOM kill, power loss, forced restart), and on restart, InnoDB tables recover automatically and transparently via redo log replay — completely invisible to the team, no action needed. MyISAM tables in the same crash come back marked as crashed or, worse, silently return incorrect results without being marked as crashed at all, and the team only discovers it when a report's numbers look wrong or a query errors out days later. Engineers audit engine usage across a schema periodically with information_schema.TABLES specifically because engine choice is invisible in normal query behavior and only surfaces at the worst possible moment.
The gotcha that catches teams doing an ENGINE=InnoDB conversion under time pressure: ALTER TABLE ... ENGINE=InnoDB on a large MyISAM table is not a quick metadata change — it's a full table rebuild that copies every row into a new InnoDB table under the hood, which can take hours on a large table, hold significant locks depending on MySQL version and settings, and requires enough free disk space to hold both the old and new copies of the table simultaneously mid-conversion. Teams that discover a critical MyISAM table during an incident and try to 'just convert it real quick' as part of the fix often turn a data-integrity incident into a second, self-inflicted availability incident because the conversion itself locks or slows the table for far longer than expected.