The fastest migration I ever ran was also the one that taught me the most about crash recovery. The table was a pricing lookup — 190 million rows of postal-code-to-tariff mappings, rebuilt from a vendor feed every night at 03:00 and read all day by an API that never wrote to it. On InnoDB the nightly rebuild took 2 hours 10 minutes and daytime read p99 sat at 11 ms. After we converted it to Aria and switched the loader to a DISABLE KEYS bulk load, the rebuild finished in 34 minutes and read p99 dropped to 4 ms, because the table shrank from 26 GB to 9 GB and the whole thing fit in Aria's page cache. Six weeks later the host had a kernel panic at 14:20 on a Tuesday. Every InnoDB table on the box recovered itself in seconds. The Aria table came up marked crashed and needed a 47-minute repair while the API served stale fallback prices. Both halves of that story are the point.
Aria is neither a toy nor an InnoDB replacement. It is a purpose-built engine with a narrow, real sweet spot, and the only way to use it safely is to know exactly where that spot ends. Here is the field map: what Aria actually is, where it wins, where it loses, what changes operationally, and how to migrate MyISAM-era schemas without surprises.
What exactly is Aria?
Aria is MariaDB's crash-safe evolution of MyISAM, written by the same original author — Michael "Monty" Widenius — under the working name Maria, and renamed Aria in 2010. "Crash-safe" is doing careful work in that sentence. Aria keeps a redo-style log (the aria_log files) of physical page changes, so after a crash the server can replay the log and roll each table back to the start of the interrupted statement. That is a statement boundary, not a transaction boundary: Aria has no multi-statement transactions, no rollback of your logical units of work, no savepoints, and no foreign keys. Each statement commits as it runs, in autocommit fashion, whether you asked for that or not.
The architecture is MyISAM's, modernized. Data lives in .MAD files and indexes in .MAI files alongside the usual table definition, and instead of InnoDB's buffer pool Aria has a page cache sized by aria_pagecache_buffer_size — 128 MB by default — which caches both index and data pages. That is already a step beyond the old MyISAM key cache, which cached index blocks only and left data reads to the filesystem cache. Locking is table-level for writes: a writer takes a table write lock, readers queue behind it, and the one concurrency concession is MyISAM-style concurrent INSERTs appending at the end of a table with no deleted-row holes. There is no MVCC anywhere in the design, which is precisely why it is fast and precisely where it breaks.
One more fact that reframes the whole discussion: you are almost certainly running Aria today. Since MariaDB 10.4, Aria is the default engine for on-disk internal temporary tables — the ones the server creates when a GROUP BY, sort, or derived table outgrows memory — and the mysql.* system tables moved to Aria in the same release. Even on a fleet that is one hundred percent InnoDB by intention, Aria is in the write path of every large reporting query.
Where does Aria beat InnoDB?
It beats InnoDB on workloads that are read-mostly or written by exactly one writer at a time, and our lookup table was the textbook case. The win comes from three places compounding. First, density: without MVCC bookkeeping — no hidden transaction ID and rollback pointer columns per row, no undo records, no secondary-index primary-key appendage — the same 190 million rows took 26 GB in InnoDB and 9 GB in Aria with the FIXED row format. Second, the bulk-load path: ALTER TABLE ... DISABLE KEYS lets the loader stream rows in and rebuild the indexes afterward with a sort, instead of maintaining B-tree pages incrementally, and there is no doublewrite buffer and no InnoDB redo volume on the load path. That is where 2 h 10 m became 34 minutes. Third, cache fit: a 9 GB working set fits in a generously sized page cache, so read p99 fell off a cliff once the cache was warm.
The other legitimate win is the one you get without asking: internal temporary tables. Any workload with big analytical GROUP BYs or sorts that spill to disk is already exercising Aria, and a too-small aria_pagecache_buffer_size silently taxes those queries. If your MariaDB box serves reporting traffic, sizing Aria's page cache is not optional tuning — it is part of the same memory budget conversation as InnoDB's pool, which I have written about in the InnoDB buffer pool sizing field guide.
The honest cost sheet: Aria wins only when the data is rebuildable or the write pattern is serialized. The engine trades transactional machinery for simplicity and speed, and every benefit above is a direct consequence of the machinery it removed. If you cannot answer "how do I rebuild this table from source if it dies," you do not have an Aria candidate — you have an InnoDB table you have not admitted it about yet.
Where does Aria lose?
Concurrent writes, full stop. A table-level write lock means one UPDATE or DELETE serializes every other writer and every reader on that table. InnoDB's row locks plus MVCC let readers read the pre-image while writers work; Aria makes them wait. I have watched teams benchmark Aria with a single-threaded loader, declare victory, and then watch latency fall over the first time two batch jobs touched the table at once. If the workload has more than one concurrent writer, or long-running reads that must not block writes, the conversation is over: InnoDB.
Anything needing transactional semantics loses too, obviously — multi-table updates that must commit together, foreign keys, read-your-own-writes inside a transaction, consistent snapshots. Less obviously, anything needing clustering-wide write support: Galera replication supports InnoDB only, so an Aria table is a single-node affair on a Galera cluster (the tradeoffs of that topology are covered in MariaDB replication vs Galera). And crash behavior is the quiet loss that only shows up at 14:20 on a Tuesday. InnoDB crash recovery is automatic, bounded, and almost always fast — redo apply over the changes since the last checkpoint, the same machinery whose capacity limits I covered in redo log capacity and checkpoint stalls. Aria's log replay is automatic too, but when it is not enough — torn pages from the panic, a table that was mid-bulk-load — the next step is a full table scan and index rebuild by a repair tool, and that duration scales with table size, not with the amount of damage.
How do day-to-day operations differ from InnoDB?
The repair workflow is the biggest difference. InnoDB has no repair command because you are not meant to repair it — corruption means restore from backup. Aria inherited the MyISAM maintenance lineage: CHECK TABLE and REPAIR TABLE work online, and the offline workhorse is aria_chk, the direct successor of myisamchk, run against the table files while the server is stopped or the table is not in use. Our 47 minutes on the crashed lookup table was aria_chk --recover followed by an index rebuild over 9 GB. The saving grace of the rebuildable-data pattern is that repair is optional — we could have dropped the table and rerun the 34-minute loader instead, and in retrospect we should have.
Memory and observability move to different knobs. Innodb_buffer_pool_size does nothing for Aria tables; aria_pagecache_buffer_size is their entire cache world, and aria_sort_buffer_size governs repair and index-creation sorts. The status counters follow suit: watch Aria_pagecache_read_requests versus Aria_pagecache_reads for the cache hit ratio, and Table_locks_waited climbing on Aria-backed tables as the earliest symptom that a workload has outgrown table-level locking. Backup tooling needs a thought as well: physical backup tools handle InnoDB hot, but non-transactional engines have to be brought to a consistent point — copied under a lock or flush phase — so a large Aria table lengthens the locked window of an otherwise-online backup. Keep Aria tables rebuildable and this stops mattering; let one become a system of record and it starts mattering a lot.
-- what engines are actually in play on this server
SELECT ENGINE, COUNT(*) AS tables_count
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
GROUP BY ENGINE;
-- InnoDB side: its cache
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- Aria side: a SEPARATE cache — key_buffer_size does not apply to Aria
SHOW VARIABLES LIKE 'aria_pagecache_buffer_size';
SHOW GLOBAL STATUS LIKE 'Aria_pagecache_read%';
-- conversion is a full table rebuild in both directions
ALTER TABLE geo_lookup ENGINE = Aria;
ALTER TABLE geo_lookup ENGINE = InnoDB;
-- Aria health check and online repair (aria_chk is the offline equivalent)
CHECK TABLE geo_lookup EXTENDED;
REPAIR TABLE geo_lookup;
How do you migrate a MyISAM-era schema to Aria?
The mechanical part is easy: ALTER TABLE ... ENGINE = Aria rewrites the table, and .MYD/.MYI files become .MAD/.MAI. The planning around it is the real work. The ALTER takes a full table copy under a metadata lock, so schedule it like any other blocking DDL and rehearse the duration on a restored copy with production row counts. Pick the row format deliberately: FIXED is the fastest and most compact when your rows have no variable-length columns, DYNAMIC handles VARCHAR and BLOB, and PAGE is Aria's own format with better caching behavior for variable-length data — inherited schemas default to whatever myisam-era settings created, so check rather than assume.
The operational deltas trip people up more than the conversion. Cron jobs calling myisamchk need to become aria_chk — the flags are deliberately similar, so the edit is small, but skipping it means your maintenance silently stops running. Memory that used to justify a big key_buffer_size belongs in aria_pagecache_buffer_size once the tables move, and myisam_sort_buffer_size tuning maps to aria_sort_buffer_size. Before converting any individual table, verify feature parity for what that table actually uses — full-text indexes, GIS columns, unusual index types — against the MariaDB version you run, because engine feature matrices shift between releases and this is exactly the kind of claim to test, not to trust from an article, including this one. And keep the mixed-engine reality in mind: a schema can run InnoDB and Aria side by side indefinitely, but a transaction touching both engines is only transactional on the InnoDB half. If a crash lands mid-write, the Aria half is wherever the last completed statement left it.
Where MonPG fits
The signals worth trending on a mixed-engine MariaDB box are the ones from the middle of this article: Aria page cache hit ratio, Table_locks_waited on Aria-backed tables, and the duration of any CHECK or REPAIR event — the counters that tell you a workload is drifting out of Aria's sweet spot before the users do. Full disclosure, as in every article of this series: I work on MonPG, which monitors PostgreSQL in production today and does not monitor MariaDB yet. MariaDB support is coming soon and in active development — the /mariadb-monitoring page tracks where it stands — and engine-level health is on the list of signals it is being built around: page cache behavior, lock waits, and repair events surfaced continuously instead of discovered during an incident. Until that ships, the status queries above are your early-warning kit. And if PostgreSQL is also in your fleet, that monitoring is live today — see the PostgreSQL overview, or browse more field notes on the blog.