MySQL Crash Recovery
`kill -9` on the server, and 400,000 rows are still there afterwards. The error log shows XA crash recovery running, and both replicas reconnect on their own - durability demonstrated rather than asserted.
Troubleshooting Guide 39 of 45 Advanced
- OSUbuntu 26.04 LTS (resolute)
- MySQL8.4.10-0ubuntu0.26.04.1
- Topologydb-a01 source, db-b01 and db-c01 replicas
- TimeAbout 16 min
- Reviewed27 August 2026
Written against the versions above. This depends on `innodb_flush_log_at_trx_commit = 1`, the default. At 0 or 2 the server is faster and a crash can lose up to a second of committed transactions.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| db-a01 | 192.168.0.81 | Ubuntu 26.04 LTS | Primary / Source / Replica Set Member 1 | 2 Core | 4 GB | 50 GB |
| db-b01 | 192.168.0.82 | Ubuntu 26.04 LTS | Standby / Replica / Replica Set Member 2 | 2 Core | 4 GB | 50 GB |
| db-c01 | 192.168.0.83 | Ubuntu 26.04 LTS | Replica Set Member 3 / Cascading and Delayed Replica | 2 Core | 4 GB | 50 GB |
Before you start
- The
eventstable with 400,000 rows. - Replication running from db-a01 - see the replication track.
- A server you can crash.
-
Record what is there, then kill the server outright
400,000 rows before.
pkill -9 mysqldsends SIGKILL. There is no shutdown, no flush, no chance to close files - the process stops mid-instruction. This is the worst case a power loss or an OOM kill produces.Then
systemdrestarts it, because the unit has aRestart=policy - the same policy that produced the restart loop in the service management guide. Here it does exactly what it should:activewithin seconds, with no intervention.bash Example session sudo mysql --table appdb -e "SELECT COUNT(*) AS before_crash FROM events"+--------------+| before_crash |+--------------+| 400000 |+--------------+sudo pkill -9 mysqld ; echo "killed=$?" ; sleep 6 ; systemctl is-active mysqlkilled=0activeExpected result400,000 rows,
killed=0, thenactiveafter the restart.Success conditionThe server was killed without warning and came back on its own.
-
Count the rows again
400,000. Not one row lost.
This is InnoDB's redo log doing its job. Every committed change is written to the log and flushed to disk before the commit returns to the client, so a crash can lose work in progress but never work that was acknowledged. On restart, InnoDB replays the log to bring the data files up to date.
It depends entirely on
innodb_flush_log_at_trx_commit = 1, the default. The settings that make MySQL faster by relaxing this are the settings that turn this step into data loss.bash Example session sudo mysql --table appdb -e "SELECT COUNT(*) AS after_crash FROM events"+-------------+| after_crash |+-------------+| 400000 |+-------------+Expected result400,000 rows again.
Success conditionYou have evidence that committed data survives a hard kill.
-
Read the recovery in the error log
The error log is the only place the server explains itself, and after a crash it is the first thing to read.
The sequence: the process starts,
InnoDB initialization has started, then ended, thenStarting XA crash recoveryandXA crash recovery finished. Those last two are the tell - a clean shutdown does not produce them.Here recovery took about four milliseconds, because there was little in flight. On a busy server with a large redo log it can take minutes, and the server refuses connections throughout. A long startup after a crash is usually recovery working, not the server hanging - a distinction worth knowing before you kill it again out of impatience.
bash Example session sudo grep -iE "crash recovery|Starting|shutdown|InnoDB initialization" /var/log/mysql/error.log | tail -82026-08-27T13:06:39.132118Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.4.10-0ubuntu0.26.04.1) starting as process 175892026-08-27T13:06:39.138989Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.2026-08-27T13:06:39.495069Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.2026-08-27T13:30:31.014533Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.4.10-0ubuntu0.26.04.1) starting as process 216252026-08-27T13:30:31.019429Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.2026-08-27T13:30:31.581786Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.2026-08-27T13:30:31.696384Z 0 [System] [MY-010229] [Server] Starting XA crash recovery...2026-08-27T13:30:31.700533Z 0 [System] [MY-010232] [Server] XA crash recovery finished.Expected resultInnoDB initialisation followed by XA crash recovery starting and finishing.
Success conditionYou can confirm from the log that recovery ran and completed.
-
See what the replicas did about it
Both replicas, checked while db-a01 was down and restarting.
Replica_IO_Running: Connecting- notNo. The IO thread lost its connection and is retrying, which is the correct behaviour and the reason no intervention is needed.Last_IO_Errno: 2003is the same connection-refused error the foundations track produced by hand, arriving here because the source genuinely was not listening.Replica_SQL_Running: Yesthroughout - the applier had nothing left to apply but never failed.Seconds_Behind_Source: NULL, which here means "not currently connected" rather than a fault.So a source crash does not break replication. It interrupts it, and the replicas resume by themselves.
bash Example session sudo mysql -e "SHOW REPLICA STATUS\G" | grep -E "Replica_IO_Running:|Replica_SQL_Running:|Last_IO_Errno:|Seconds_Behind_Source:" Replica_IO_Running: Connecting Replica_SQL_Running: Yes Seconds_Behind_Source: NULL Last_IO_Errno: 2003sudo mysql -e "SHOW REPLICA STATUS\G" | grep -E "Replica_IO_Running:|Replica_SQL_Running:|Seconds_Behind_Source:" Replica_IO_Running: Connecting Replica_SQL_Running: Yes Seconds_Behind_Source: NULLExpected result
Connectingon both,Last_IO_Errno: 2003, SQL thread stillYes.Success conditionYou know what a source outage looks like from a replica.
-
Prove all three still agree
The check that ends any incident: not "is it up" but "is it right".
A count alone would not catch divergence - two servers can hold the same number of different rows, which is exactly what happened in the replication track. Summing a
CRC32over the row contents does catch it.10 rows and checksum 22885343858 on all three. The source crashed, both replicas lost their connection, everything reconnected, and the data is identical.
Keep a check like this. It is cheap, it is the only real definition of a healthy topology, and it is the thing
SHOW REPLICA STATUScannot tell you.bash Example session sudo mysql -N appdb -e "SELECT CONCAT(COUNT(*),' rows / checksum ',COALESCE(SUM(CRC32(CONCAT_WS('|',id,name,email))),0)) FROM customers"10 rows / checksum 22885343858Expected resultIdentical row count and checksum from db-a01, db-b01 and db-c01.
Success conditionYou have verified consistency rather than assuming it from a green status.
Troubleshooting
The server takes a long time to start after a crash.
Why: Crash recovery replaying the redo log.
Fix:Watch the error log. Do not kill it - the next recovery starts from further back.
Rows committed just before a crash are missing.
Why:
innodb_flush_log_at_trx_commitis 0 or 2.Fix:Set it to 1 for durability. The performance gain from relaxing it is real and so is the loss.
A replica does not reconnect after the source returns.
Why: Retries exhausted, or the source's binary logs were purged while it was down.
Fix:
START REPLICAto retry. If the logs are gone, re-seed - see the replication track.InnoDB reports corruption on startup.
Why: Hardware, or a filesystem that did not honour flushes.
Fix:Restore from backup.
innodb_force_recoveryis for extracting data, not repairing - raise it only as far as needed to dump, then rebuild.