At a glance
Replication Lag (Seconds_Behind_Source) is how far, in seconds, a replica is behind the source it copies from. Zero means the replica is caught up; a rising number means writes on the source are not yet visible on the replica. Lag is the number that decides whether your read replicas are safe to read from and whether a failover would lose data. A replica 30 seconds behind serves stale catalogue, stale inventory, and stale order state, which is how customers see “out of stock” on something they just bought, or an order that “does not exist yet”.
Calculation
The card surfacesSeconds_Behind_Source directly from each replica’s status, then reports the maximum across the topology so a single lagging node cannot hide behind healthy ones.
Seconds_Behind_Source actually measures. It is the difference between the timestamp of the event the SQL (applier) thread is currently executing and the replica’s clock. This has two well-known quirks the engine accounts for:
- It only reflects the applier thread. If the IO (receiver) thread has stopped but the applier is still chewing through its relay log, the replica can briefly report a small, falling number while in reality it has stopped receiving new data. That is why this card is always read with Replication Thread Health.
NULLis not zero. When a replication thread is stopped,Seconds_Behind_SourcereportsNULL, not a large number. The engine treatsNULLas “lag unknown / threads not running” and escalates rather than rendering it as caught up.
Worked example
A platform team runs one MySQL 8.0 source and two read replicas:replica-a serves the catalogue API, replica-b serves analytics. Snapshot taken on 22 Apr 26 at 09:40 BST after a bulk price-update job.
The headline reports 47s (the worst replica) with a red border, and a Nerve Centre alert fires. The team works the problem:
- Both threads are running, so this is throughput, not breakage. Replication Thread Health is green. The replica is receiving and applying, just not fast enough to keep up with the source’s write rate.
- The trigger is the bulk price-update job. A single large transaction updating 400,000 catalogue rows landed on the source in seconds, but
replica-aapplies it serially and is now behind.replica-blags less because it is on faster storage. - The customer-visible risk is the catalogue. Because
replica-abacks the catalogue API, shoppers may see the pre-update prices for up to 47 seconds. The team temporarily routes catalogue reads to the source until lag clears, then routes them back.
replica_parallel_workers) so future bulk jobs apply in parallel rather than serially.
Three takeaways:
- Lag is a data-correctness signal, not just a performance one. A lagging replica serves stale rows. If you read inventory or pricing from replicas, lag directly causes wrong answers to customers.
- A stable high number and a
NULLare different emergencies. Stable high lag is a throughput shortfall you can engineer around (parallel workers, faster storage, smaller transactions).NULLmeans a thread stopped, which is the Replication Thread Health emergency. - Big transactions are the usual cause. A single huge
UPDATEorDELETEserialises on the replica even when the source applied it quickly. Batch large DML into smaller chunks to keep lag flat.
Sibling cards
Reconciling against the source
Where to look in MySQL’s own tooling:Why our number may legitimately differ:SHOW REPLICA STATUSon each replica is the canonical source. Read these fields together:Performance Schema for multi-threaded detail:performance_schema.replication_applier_status_by_workerandreplication_connection_status. GTID delta for a clock-independent measure: compareRetrieved_Gtid_Set(received) againstExecuted_Gtid_Set(applied); the gap is transactions still to apply, immune to clock skew. Managed-service consoles: Amazon RDS exposesReplicaLagin CloudWatch; Aurora exposesAuroraReplicaLagin milliseconds; both should track this card closely.
Cross-connector reconciliation: pair with the ecommerce inventory cards. If MySQL Inventory Rows vs Ecom Inventory Count shows drift exactly while lag is high, your storefront is reading a stale replica, which is a routing problem you can fix by pinning inventory reads to the source.
Known limitations / FAQs
The card reads zero but I am sure the replica is behind. Why? The most common trap: the source is idle.Seconds_Behind_Source is computed from the timestamp of the event currently being applied, so when no new writes arrive there is nothing to time and it reports 0 even if the IO thread is wedged. Always read this card with Replication Thread Health; if a thread is not running, the 0 is a lie.
The card shows an alert but no number. What is happening?
That is NULL, which means a replication thread is stopped. NULL is worse than a large number: a lagging replica is still catching up, but a stopped one will never catch up until you restart the thread. Jump to thread health and the replication-broken alert immediately.
Why does the headline show the worst replica instead of an average?
Because an average hides the failure. If one replica is caught up and one is 200 seconds behind, the average (100s) describes neither. The replica you happen to route a read to is what the customer experiences, so the safe headline is the worst case.
My lag is high but stable, not growing. Is that an emergency?
Less urgent than growing lag, but still a correctness problem. Stable lag means the replica is keeping pace with new writes but never closing the existing gap, usually because it started behind after a restore or a big transaction. It will not self-heal during steady load; you need a quiet window, smaller transactions, or parallel apply to drain it.
Can I change the 10-second alert threshold?
Yes, per profile in the Alert Rules tab. An analytics replica can tolerate minutes of lag; a replica serving live inventory or session state should be tighter, for example 2 to 3 seconds. Set it to just above your normal busy-hour lag.
Why does a single big UPDATE cause a lag spike?
A large transaction commits atomically on the source in one go, but the replica applies it as one unit too, and (without parallel workers) serially behind everything else in the relay log. A 400,000-row update that took 3 seconds on the source can take far longer to clear on a replica, spiking lag. Chunk large DML into smaller batches and enable replica_parallel_workers to mitigate.
Does GTID replication change how I read this card?
The card still surfaces Seconds_Behind_Source, but with GTIDs you have a better backup measure: the gap between Retrieved_Gtid_Set and Executed_Gtid_Set counts transactions, not seconds, and is immune to clock skew. When in doubt about the seconds figure, the GTID gap is the ground truth for “how many transactions behind”.