MariaDB9 min read

MariaDB's InnoDB Is a Fork, Not a Mirror: What Actually Diverged From MySQL

MariaDB's InnoDB is a maintained fork with its own defaults: the change buffer is gone in 11.0, flushing defaults to O_DIRECT, the redo log was rewritten, and half of every MySQL tuning guide quietly stops applying.

Three years ago I applied a well-known MySQL 8.0 tuning guide to a MariaDB 10.6 server and spent a week chasing ghosts. The guide told me to raise innodb_buffer_pool_instances; the variable did not exist. It told me to watch change buffer merges in INNODB STATUS; there was no change buffer section. It assumed fsync flushing; the server was already running O_DIRECT, so the double-buffering diagnosis I had been handed was wrong from the first sentence. Nothing was on fire, but every recommendation I trusted had been written for a different storage engine wearing the same name.

That is the thing to internalize about MariaDB: InnoDB inside it is a fork with its own maintainer, its own defaults, and its own release notes. It shares a name and a file-format lineage with MySQL's InnoDB, and then it goes its own way with every release. Here is the map I wish someone had handed me — what diverged, in which version, and which piece of MySQL advice quietly stops applying.

Is MariaDB's InnoDB the same code as MySQL's InnoDB?

No, and it has not been for over a decade. From MariaDB 5.5 through 10.1 the default engine was actually Percona XtraDB, a patched InnoDB. Starting with 10.2, MariaDB dropped XtraDB, took Oracle's InnoDB from the 5.7 lineage in-house as the base for its own fork, and kept XtraDB only as a compatibility alias. Since then the two codebases have been maintained by different teams answering to different roadmaps.

You can see the independence in the small print. MariaDB removed the innodb_version variable in 10.10 because the number it reported had become meaningless — the engine no longer tracks upstream versions. It disabled the adaptive hash index by default in 10.5, removed innodb_buffer_pool_instances and the old innodb_concurrency_tickets and innodb_commit_concurrency knobs in 10.6, and ships full_crc32 as its default checksum algorithm where MySQL still defaults to crc32. None of these are tune-aways on the MySQL side; they are different engineering decisions shipped as defaults. A configuration file written for MySQL 8.0 references variables MariaDB deleted years ago, and depending on how strict your startup checks are, the server either warns or refuses to boot.

What happened to the change buffer in MariaDB?

It is gone. MariaDB disabled the change buffer by default in the February 2022 maintenance releases, deprecated and ignored innodb_change_buffering in 10.9, and removed the entire mechanism in 11.0. The stated reason was unusually blunt for a database vendor: the change buffer had been a persistent source of corruption bugs that were extremely hard to diagnose, and its write-amplification savings no longer justified the risk on modern storage. On upgrade from an older release, any changes still sitting buffered in the system tablespace get merged, and then the feature stops existing.

The practical consequence is that every MySQL tuning article telling you to adjust innodb_change_buffering, or to read the INSERT BUFFER section of INNODB STATUS, is dead advice on MariaDB 11.x. MySQL 8.0 still has the feature — Oracle only moved to disabling it by default in 8.4 — so the two engines now have a genuinely different write path for secondary index maintenance. On write-heavy MariaDB, secondary index leaf pages get merged the old-fashioned way: read in, modified, flushed. That is one reason random-write workloads on MariaDB lean harder on a large buffer pool and fast storage than the equivalent MySQL setup, and why "the change buffer will absorb it" is not a plan here.

How do the redo log and flush defaults differ?

MariaDB rewrote the InnoDB redo log in 10.5: one physical file, ib_logfile0, instead of a log group, with a new record format and simpler checkpoint arithmetic. Since 10.9, innodb_log_file_size is dynamic — you resize the log without a restart and the switch happens at the next checkpoint. MySQL went a different route in 8.0.30 with innodb_redo_log_capacity and a directory of numbered log files. Same goal, incompatible implementations; monitoring views written against MySQL 8.0.30's redo plumbing find nothing on MariaDB, and capacity math from one engine does not transfer to the other.

Flushing diverged too. On Linux, MariaDB has defaulted innodb_flush_method to O_DIRECT for years — the 10.6 documentation states it as the default — while MySQL 8.0 still ships fsync. MariaDB has also supported O_DIRECT_NO_FSYNC since the XtraDB era; MySQL only added it in 8.0.14. The trap runs in both directions: a MySQL runbook that says "set O_DIRECT to avoid double buffering" is a no-op on MariaDB because you already have it, and a MariaDB config copied onto MySQL silently changes flush behavior the owner never asked for. Check the running value, never the blog post.

-- what this server actually is, lineage included
SELECT VERSION();

-- flush method, redo size, checksums: running values, not my.cnf values
SHOW VARIABLES WHERE Variable_name IN
  ('innodb_flush_method', 'innodb_log_file_size',
   'innodb_checksum_algorithm', 'innodb_io_capacity');

-- present on 10.11, gone on 11.x: a quick fork-detection probe
SHOW VARIABLES LIKE 'innodb_change_buffering';

Which MySQL 8.0 InnoDB features never landed in MariaDB?

The list matters most when a runbook assumes it. Redo log archiving — innodb_redo_log_archive_dirs, added in MySQL 8.0.17 so backup tools can follow the log — has no MariaDB equivalent; mariabackup copies the redo log live instead, with its own race to manage. The clone plugin, also 8.0.17, does not exist either: provisioning a fresh node on MariaDB means mariabackup or a storage snapshot, full stop. And MySQL 8.0's transactional data dictionary inside InnoDB never landed. MariaDB still keeps table definitions in .frm files on the filesystem and got crash-safe DDL a different way, through the DDL recovery log that powers atomic DDL in 10.6 and later.

This is not a ranking — traffic went the other way too. MariaDB built a first-class InnoDB page compression family that MySQL never matched: the innodb_compression_* variable set and the PAGE_COMPRESSED table attribute, implemented with sparse-file punch holes rather than MySQL's COMPRESSION option. Engine-independent table statistics exist only on the MariaDB side. The point is narrower than who is ahead: the engines now differ at the level of file formats, system tables, and variable namespaces, so "InnoDB" is no longer a portable specification. Any procedure that touches physical files — backups, cloning, crash recovery, redo sizing — is engine-specific and version-specific, and a procedure written for one is a liability on the other.

How do you read MariaDB's INNODB STATUS output?

SHOW ENGINE INNODB STATUS still prints the familiar sections — SEMAPHORES, TRANSACTIONS, FILE I/O, LOG, BUFFER POOL AND MEMORY, ROW OPERATIONS — but the contents drift per release. On 11.x there is no INSERT BUFFER change-buffer block to read at all. The LOG section reports MariaDB's own LSN and checkpoint lines, and the number that matters is unchanged in spirit: the distance between the current log sequence number and the last checkpoint, measured against your log capacity. That gap is your write headroom. When it approaches the log size, InnoDB starts aggressive flushing and write latency spikes within seconds. The formula is the same as MySQL's; the line labels and the default capacities are not, so thresholds copied from a MySQL dashboard alert at the wrong waterline in both directions.

For per-cause detail, MariaDB keeps the information_schema.INNODB_METRICS counters with its own names, and SHOW ENGINE INNODB MUTEX still works for latch contention. My habit on a new MariaDB box is to capture one clean INNODB STATUS snapshot at peak and one at idle, and learn that server's baseline before touching anything. The fork means your MySQL instincts are directionally right and specifically wrong — good enough for hypotheses, useless for thresholds.

Where MonPG fits

The signals worth trending here are the physical ones: checkpoint age versus log capacity, buffer pool free-page starvation, and flush-list stalls — the places where the fork's defaults bite first. 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 InnoDB engine health is high on the list of signals it is being built around: redo headroom, flush behavior, and the status counters above, graphed continuously instead of sampled during an incident. Until that ships, the STATUS and metrics queries here are your early-warning kit. And if your fleet also runs PostgreSQL, that monitoring is live today — see the PostgreSQL overview, or browse the field notes on the blog.