async vs. semi-sync in MySQL
MySQL replication determines how many transactions can be lost in a failure and how stale the data an application reads can become. Understanding the difference between asynchronous and semi-synchronous replication, binlog processing and replication lag lets you tune a topology for actual consistency requirements instead of relying on defaults.
Table of Contents
- 1. What MySQL replication actually solves
- 2. How asynchronous replication works internally
- 3. Semi-synchronous replication: ACK before commit
- 4. Replication lag: causes and measurement
- 5. Enabling GTID-based replication
- 6. Setting up a replica step by step
- 7. Multi-threaded replication and parallelization
- 8. Monitoring with SHOW REPLICA STATUS
- 9. Async vs. semi-sync compared
- 10. Summary
- 11. FAQ
1. What MySQL replication actually solves
MySQL replication copies data changes from a source to one or more replicas and solves several independent problems at once: resilience through a warm copy of the data, load distribution through additional read servers, and geographic proximity through replicas in other regions. Without replication, every read and write sits on a single server whose failure takes down the whole application and whose capacity caps the total read throughput available.
The decisive design factor in MySQL replication is how a commit on the source relates to the transfer to the replica. If the transfer happens completely independently of the commit, that is asynchronous replication. If the source waits for a confirmation before treating the commit as complete, that is semi-synchronous replication. This distinction directly determines how many transactions can be lost in the worst case if the source fails immediately after a commit.
2. How asynchronous replication works internally
In classic asynchronous MySQL replication, the source first writes every data change to its binary log, the binlog. A commit on the source is considered complete once the transaction is persisted in the InnoDB redo log and the binlog, regardless of whether any replica has received the change yet. On the replica, an IO thread continuously reads the source's binlog stream and writes the events into a local relay log. A separate SQL thread, or several worker threads when parallelization is enabled, reads the relay log and applies the changes to its own tables.
This decoupling of the IO step from the apply step is why asynchronous replication scales so well: the source is not slowed down by a lagging replica, and network latency to a replica has no direct impact on commit latency on the source. The price is a potential data loss during a failover. If the source fails immediately after a commit, before the replica's IO thread has even read the corresponding binlog event, that transaction exists only on the failed source and is lost on failover to the replica.
For most read traffic and non-critical reporting workloads this risk is acceptable, because asynchronous MySQL replication produces markedly lower write-side latency than any synchronous variant. For financial transactions, order completions, or other operations where a lost commit causes direct business damage, asynchronous replication alone is often not enough.
# my.cnf on the source: minimal configuration for asynchronous replication
[mysqld]
server-id = 1
log_bin = /var/log/mysql/binlog
binlog_format = ROW
gtid_mode = ON
enforce_gtid_consistency = ON
sync_binlog = 1
# my.cnf on the replica: read-only, independent server-id
[mysqld]
server-id = 2
relay_log = /var/log/mysql/relaylog
read_only = ON
super_read_only = ON
gtid_mode = ON
enforce_gtid_consistency = ON
# Check IO and SQL thread lag independently after START REPLICA
SHOW STATUS LIKE 'Slave_running';
SELECT service_state, remaining_delay
FROM performance_schema.replication_applier_status_by_worker;
3. Semi-synchronous replication: ACK before commit
Semi-synchronous MySQL replication closes exactly this gap without incurring the full latency of a synchronous multi-master solution. With the semi-sync plugin enabled, after writing to the binlog the source waits for confirmation from at least one replica that the binlog event has arrived in its relay log, before reporting the commit as successful to the client. An important nuance: the replica only needs to have received and persisted the event, not necessarily already applied it to its tables. That distinguishes semi-synchronous replication from true synchronous replication, where the application of the change would be awaited as well.
In practice, semi-synchronous replication is enabled through the rpl_semi_sync_source and rpl_semi_sync_replica plugins, named rpl_semi_sync_master and rpl_semi_sync_slave in older versions. The parameter rpl_semi_sync_source_timeout defines how long the source waits for confirmation before automatically falling back to asynchronous mode. This fallback is essential for availability: without it, an outage of all replicas would also block the source, since no commit could ever be confirmed.
-- Semi-synchronous replication: install plugins on source and replica
-- Run on the source server
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;
SET GLOBAL rpl_semi_sync_source_timeout = 10000; -- milliseconds before fallback to async
-- Run on every replica
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;
-- Restart the replication IO thread so the new setting takes effect
STOP REPLICA IO_THREAD;
START REPLICA IO_THREAD;
-- Verify semi-sync status on the source
SHOW STATUS LIKE 'Rpl_semi_sync_source_status';
SHOW STATUS LIKE 'Rpl_semi_sync_source_clients';
In practice, many setups combine semi-synchronous MySQL replication with at least one geographically remote replica, so that a regional outage of the source does not also take down the only confirmation source. The timeout should be low enough to avoid blocking write traffic on the source during a genuine replica outage, but high enough to avoid interpreting normal network jitter as a failure.
4. Replication lag: causes and measurement
Replication lag describes the time delay between a commit on the source and the same change becoming visible on the replica. With asynchronous MySQL replication, some lag is always present because applying changes happens downstream. It becomes a problem when the lag grows uncontrolled, for instance because the replica hardware is weaker than the source, because individual transactions touch very large amounts of data, or because the SQL thread cannot keep up with the source's write volume on a heavily written table without parallelization.
The classic indicator Seconds_Behind_Source, called Seconds_Behind_Master in older versions, from SHOW REPLICA STATUS is a useful but incomplete metric. It measures the difference between the timestamp of the most recently processed event and the replica's current system time, but says nothing about how many events the IO thread has already received but the SQL thread has not yet applied. More precise is comparing the GTID sets between source and replica, or evaluating performance_schema.replication_applier_status_by_worker, which shows the processing state per worker thread.
Replication lag has direct consequences for applications that distribute reads across replicas: a user who reads immediately after writing can see stale data if the read is routed to a replica with noticeable lag. That is why lag monitoring belongs in every setup that uses replicas productively for read traffic, not just as a pure backup copy.
5. Enabling GTID-based replication
Global Transaction Identifiers, or GTID, replace traditional position-based replication using binlog filename and offset with unique, cross-server transaction IDs. Every transaction receives a GTID on commit following the pattern server_uuid:transaction_number, unique across every server in the replication topology. The big advantage over position-based MySQL replication: a failover to another replica no longer requires manually locating the correct binlog position, because every server can determine from the already-executed GTID set which transaction it needs to continue replicating from.
GTID is enabled via gtid_mode = ON and enforce_gtid_consistency = ON in the server configuration. The second parameter forbids statements that cannot be handled safely with GTID-based replication, such as mixing non-transactional and transactional changes in a single statement. After migrating to GTID, switching a replica no longer requires the error-prone manual reading of File and Position from SHOW MASTER STATUS; instead CHANGE REPLICATION SOURCE TO ... SOURCE_AUTO_POSITION = 1 is sufficient.
6. Setting up a replica step by step
Setting up a new replica follows a fixed sequence: first, a dedicated replication user with the REPLICATION SLAVE privilege is created on the source, then a consistent backup of the source is taken, for example with mysqldump --single-transaction --source-data=2 or with Percona XtraBackup for larger databases without locking. After restoring the backup on the new server, replication is configured with CHANGE REPLICATION SOURCE TO and started with START REPLICA.
-- On the source: create a dedicated replication user
CREATE USER 'repl'@'10.0.%' IDENTIFIED BY 'strong-password-here';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.%';
FLUSH PRIVILEGES;
-- On the source: enable binary logging and GTID in my.cnf
-- [mysqld]
-- server-id = 1
-- log_bin = /var/log/mysql/binlog
-- binlog_format = ROW
-- gtid_mode = ON
-- enforce_gtid_consistency = ON
-- On the new replica: point it at the source using GTID auto-positioning
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = 'db-primary.internal',
SOURCE_USER = 'repl',
SOURCE_PASSWORD = 'strong-password-here',
SOURCE_AUTO_POSITION = 1,
SOURCE_SSL = 1;
START REPLICA;
SHOW REPLICA STATUS\G
After starting, check Replica_IO_Running and Replica_SQL_Running, both must show Yes for MySQL replication to be fully functional. A common mistake in practice is an inadequately consistent backup window: if the backup is not taken as a consistent snapshot, for example without --single-transaction, the new replica ends up with inconsistencies that only surface on the first diverging query.
7. Multi-threaded replication and parallelization
In older MySQL versions, a single SQL thread applied all changes sequentially, which regularly led to growing lag on write-heavy sources with many concurrent transactions. Multi-threaded replication, or MTS, parallelizes the apply step across several worker threads, controlled via replica_parallel_workers. The parallelization strategy replica_parallel_type = LOGICAL_CLOCK uses information from the source's group commit logic to apply transactions that were committed simultaneously there in parallel on the replica as well, without compromising consistency.
Parallelization brings the biggest gains for MySQL replication on workloads with many independent tables or schemas. For workloads that predominantly touch a single, heavily used table, the parallelization gain is limited, because many transactions must be serialized anyway to avoid conflicts. In those cases, reducing transaction size on the source or switching the binlog format to ROW, which allows more granular parallelization than STATEMENT, helps more.
-- Enable multi-threaded replication with 8 parallel worker threads
SET GLOBAL replica_parallel_workers = 8;
SET GLOBAL replica_parallel_type = 'LOGICAL_CLOCK';
SET GLOBAL replica_preserve_commit_order = ON;
STOP REPLICA;
START REPLICA;
-- Inspect per-worker progress and detect a lagging worker thread
SELECT worker_id, service_state, last_applied_transaction,
apply_time_period
FROM performance_schema.replication_applier_status_by_worker
ORDER BY worker_id;
8. Monitoring with SHOW REPLICA STATUS
Despite modern Performance Schema tables, SHOW REPLICA STATUS\G remains the first diagnostic tool for MySQL replication. Important fields are Replica_IO_State, describing the current state of the IO thread, Last_IO_Error and Last_SQL_Error for error messages, and Retrieved_Gtid_Set and Executed_Gtid_Set, whose comparison shows how many received transactions have not yet been applied.
-- Typical (shortened) output of SHOW REPLICA STATUS on a healthy replica
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: db-primary.internal
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Last_IO_Error:
Last_SQL_Error:
Retrieved_Gtid_Set: a1b2c3d4-0000-0000-0000-000000000001:1-98421
Executed_Gtid_Set: a1b2c3d4-0000-0000-0000-000000000001:1-98421
-- Compare GTID sets programmatically to detect a stuck applier
SELECT GTID_SUBTRACT(
@@GLOBAL.gtid_executed,
(SELECT VARIABLE_VALUE FROM performance_schema.global_variables
WHERE VARIABLE_NAME = 'gtid_executed')
) AS pending_transactions;
For ongoing monitoring, an occasional manual glance at SHOW REPLICA STATUS is not enough. Production setups export Seconds_Behind_Source, the semi-sync confirmation status, and the number of active replica connections regularly into a monitoring system like Prometheus via the MySQL Exporter, so that lag spikes and dropped replication channels trigger alerts automatically instead of first surfacing as a customer complaint.
9. Async vs. semi-sync compared
The choice between asynchronous and semi-synchronous MySQL replication is a trade-off between write latency and data safety during a failover. The table below summarizes the key differences relevant to this decision in practice.
| Criterion | Asynchronous replication | Semi-synchronous replication |
|---|---|---|
| Commit latency | Minimal, no wait for replicas | Increased by a network round trip to the fastest replica |
| Data loss risk on failover | Possible, the latest transactions may be missing | Significantly reduced if at least one ACK was received |
| Behavior on replica outage | Source keeps running unaffected | Falls back to async after timeout, then behaves as on the left |
| Configuration effort | Low, default MySQL behavior | Plugin installation and timeout tuning required |
| Typical use case | Reporting replicas, geographically remote copies | Financial data, order systems, critical OLTP workloads |
In practice, many operators run a hybrid model: one or two local replicas run semi-synchronously to minimize data loss within the local data center, while geographically remote replicas are attached purely asynchronously so interconnect latency does not affect commit latency. This combination uses the strengths of both modes of MySQL replication without fully accepting either mode's weaknesses.
Mironsoft
MySQL replication, high availability and database architecture
Replication that actually holds up when it matters?
We analyze existing MySQL replication topologies, configure semi-synchronous protection where it counts, and set up replication lag monitoring before it becomes a problem.
Replication audit
Analysis of existing topologies for data loss risk and lag behavior
GTID migration
Moving from position-based to GTID-based replication
Monitoring setup
Integrating lag alerts and semi-sync status into Prometheus and Grafana
10. Summary
Choosing between asynchronous and semi-synchronous MySQL replication is not a pure configuration question, it is a deliberate decision about acceptable data loss during a failover versus additional commit latency. Asynchronous replication is the right default for reporting replicas and geographically remote copies, semi-synchronous replication belongs wherever a lost commit causes real damage. GTID substantially simplifies failover and replica setup compared to classic position-based replication.
Replication lag is not a static value, it must be monitored continuously, especially when replicas are used for read traffic in the application. Multi-threaded replication reduces lag significantly for suitable workloads, but it does not replace clean monitoring through SHOW REPLICA STATUS and Performance Schema tables. Anyone who masters these fundamentals of MySQL replication can design topologies deliberately around the application's actual consistency requirements instead of relying on defaults.
MySQL Replication: The Essentials at a Glance
Asynchronous
Minimal commit latency, but potential data loss on failover immediately after a commit.
Semi-synchronous
Waits for an ACK from at least one replica before commit, with fallback to async after timeout.
GTID
Unique transaction IDs simplify failover and replica setup with SOURCE_AUTO_POSITION.
Monitoring
Export Seconds_Behind_Source, GTID sets and semi-sync status to Prometheus regularly.