The doublewrite buffer has the worst naming problem in InnoDB: the name literally says it writes everything twice. Sooner or later, on every write-heavy cluster I have operated, someone proposes turning it off for free throughput. The first time I watched that proposal win, the server ran beautifully for eleven months. Then a kernel panic landed in the middle of a flush batch and tore a page inside a hot index. Crash recovery could not repair it, because the one thing doublewrite exists for had been switched off. We restored from the previous night's backup and replayed binlogs for most of a day. The throughput gain had been real. So was the restore.
This is the audit I now run before anyone touches innodb_doublewrite: what the protection actually is, where its cost really shows up, how to measure that cost on your own hardware instead of quoting forum percentages, and the narrow set of cases where disabling it is engineering rather than superstition.
What you are actually paying for
InnoDB's default page is 16 KB. The atomicity your storage guarantees is almost always smaller: 4 KB sectors on modern drives, sometimes 512 bytes, sometimes something stranger through a hypervisor. A single page write is physically several device writes, and if power or the kernel dies partway through, the page on disk is a chimera: a few sectors of the new version stitched to the rest of the old one. Checksums detect it at the next read, which is the source of the famous page corruption message at startup.
The part that surprises people is that the redo log cannot fix this. InnoDB redo is physiological: most records describe a change to apply to a page, not the page itself. Applying an insert-at-offset record requires a self-consistent starting image, and a torn page is not one. So before flushing a batch of dirty pages to their real locations, InnoDB writes the whole batch sequentially into the doublewrite area and syncs it, and only then writes the pages to their final homes. Crash recovery restores any checksum-failing page from its doublewrite copy, then applies redo on top. The invariant is simple: at every instant, at least one intact copy of every in-flight page exists somewhere durable. The flushing pipeline this rides on is the same dirty-page machinery I described from the memory side in the buffer pool field guide; doublewrite is one sequential stop added to every page's journey to disk.
Where the cost actually shows up
The naive accounting says doublewrite doubles write volume, and byte-for-byte that is true: every flushed page gets written twice. But storage throughput is priced in I/O patterns, not bytes. The doublewrite copies are large sequential writes batched across many pages, while the final writes are the same scattered random writes you were paying for anyway. On drives where sequential bandwidth is abundant relative to random IOPS, which describes most SSDs and essentially all cloud block storage, the marginal cost is far below the two-times figure the name suggests.
The real overhead concentrates in three places. First, raw bandwidth: on throughput-capped cloud volumes every extra byte counts against the cap, and doublewrite bytes are real bytes. Second, extra fsyncs on the doublewrite files, which add latency to the flush path. Third, serialization: at very high page-flush rates the doublewrite area itself can become the bottleneck that flush batches queue behind. Anyone quoting you a universal percentage overhead is guessing. The honest answer spans unmeasurable to painful depending on flush rate, storage, and batching efficiency, which is exactly why the only number worth acting on is one you measured.
8.0.20 moved it out of the system tablespace
Before MySQL 8.0.20 the doublewrite area lived inside ibdata1, contending with everything else in the system tablespace and offering no tuning surface at all. Since 8.0.20 it lives in dedicated doublewrite files, named along the lines of #ib_16384_0.dblwr, placed in the data directory by default or wherever innodb_doublewrite_dir points, with the file count scaling from buffer pool instances. This mattered more than the release notes made it sound: contention on the system tablespace disappeared, and placement became a real knob.
The practical consequence is my favorite unglamorous optimization: put the doublewrite files on a separate low-latency device. You keep the full protection and shave the fsync cost, because the sequential sync writes to the doublewrite area stop competing with everything else on the data volume. It is the legitimate alternative almost nobody tries before reaching for the OFF switch.
Measuring the overhead honestly
Two status counters describe doublewrite activity directly, and together with the global write counters they support a real audit. Sample them as deltas over a busy window:
SELECT variable_name, variable_value
FROM performance_schema.global_status
WHERE variable_name IN ('Innodb_dblwr_pages_written',
'Innodb_dblwr_writes',
'Innodb_data_written');
Innodb_dblwr_pages_written divided by Innodb_dblwr_writes is pages per doublewrite batch. High values, dozens of pages per operation, mean the sequential batching is doing its job and the tax is low. Values collapsing toward one mean single-page flushes dominate, usually LRU-driven, and the per-page tax rises accordingly. That collapse is a flushing-behavior problem worth fixing on its own merits, not a reason to abandon protection.
The share of total write bytes is one more division away, estimating doublewrite bytes as pages times the page size against Innodb_data_written. And then run the honest experiment: an identical staging box, your real write workload replayed, innodb_doublewrite on versus off, comparing p99 commit latency, page flush rate, and device utilization. If you cannot measure a difference at production write rates, keeping the protection costs you nothing, and the audit is over.
ON, OFF, and DETECT_ONLY
Since MySQL 8.0.30 the setting is no longer a plain boolean. ON is equivalent to DETECT_AND_RECOVER: full protection, torn pages detected and repaired from the doublewrite copy during recovery. DETECT_ONLY writes only metadata rather than full page content, so recovery can detect torn pages but cannot repair them; it is cheaper, and it is most useful as a migration or evaluation stance, as long as you stay clear-eyed that detection without repair still means a restore on the day it fires. OFF is no protection at all.
One operational detail matters during incidents: you can move between ON and the detect modes dynamically, but re-enabling from OFF requires a restart. The state you booted with constrains the state you can reach under pressure, so this is a decision to make deliberately in a change window, not something to improvise while a queue builds.
When disabling it is actually defensible
The defensible case fits in one sentence: disable doublewrite only when your storage stack already guarantees that a 16 KB page write is atomic. The canonical example is copy-on-write filesystems. ZFS never overwrites a block in place, so a page write either commits completely or the old block survives, torn pages cannot occur, and running doublewrite on ZFS with recordsize matched to the InnoDB page size is paying for duplicate insurance. The same logic covers Btrfs with copy-on-write enabled, and storage layers that document atomic writes at the page size. ext4 or XFS on generic block storage provides no such guarantee, and a feeling that the SSDs are probably fine is not a mechanism.
My runbook rule is that OFF requires the guaranteeing mechanism named in writing, plus a rehearsed restore plan for the day the guarantee turns out to be marketing. The trade is asymmetric and deserves to be made consciously: the savings are small and continuous, the failure is rare, silent, and lands on the busiest tables at the worst possible time.
What I watch in production
Three signals cover it. Pages per doublewrite batch, trended, because a collapse toward one tells you single-page flushing is taking over before latency does. Doublewrite's share of total write bytes, because a creeping share on a throughput-capped volume is a capacity conversation waiting to happen. And after every unclean restart, the error log: recovery messages about pages restored from the doublewrite area are the feature quietly paying for itself, and worth knowing about before someone proposes turning it off again.
MonPG and MySQL, stated plainly
A word about where I come from: I work on MonPG, which monitors PostgreSQL, and only PostgreSQL, today; MySQL support is on the way. The audit above is the stance the MySQL product is taking: dblwr batch depth and write-share trended as first-class series, flush behavior correlated against them, so the move-the-files-versus-fix-the-flushing decision starts from your numbers rather than forum percentages. You can follow that build on the MySQL monitoring (coming soon) page. If your fleet runs both engines, the PostgreSQL analog of this tradeoff, full_page_writes and the post-checkpoint WAL bursts it causes, is already measured on the PostgreSQL side; see the comparisons, or browse more MySQL field notes on the blog.