_%20How%20MySQL%2026.7%20Fixes%20Replication%20Lag%20(1).avif)
_%20How%20MySQL%2026.7%20Fixes%20Replication%20Lag%20(1).avif)
Replication lag remains one of the most persistent bottlenecks in high-throughput MySQL environments. As write workloads surge on a primary server, the replica must continuously read, schedule, apply, and commit those transactions. When a replica cannot process incoming transactions as fast as the source generates them, replication lag accumulates, leading to stale reads, delayed analytical reporting, and compromised readiness during automated failover scenarios.
MySQL 26.7 introduces the Change Stream Applier (CSA), a next-generation implementation of the replica SQL applier designed to fundamentally change how worker threads process binary log events. Enabled via APPLIER_VERSION = 2, CSA shifts replication scheduling from traditional thread-per-transaction lock-in to an asynchronous, worker-pool architecture.
Below, we examine the mechanics of CSA, walk through a reproduction benchmark against the legacy Multi-Threaded Applier (MTA), and review the configuration parameters required to run it in production.
What Is the Change Stream Applier (CSA)?
In traditional Multi-Threaded Appliers (APPLIER_VERSION = 1), a single worker thread remains bound to a transaction throughout its entire lifecycle:
Read⟶Apply⟶Wait⟶Commit
Workers often sit idle while waiting for preceding transactions to commit, holding thread resources captive.
CSA fundamentally re-architects this model using three core concepts:
- Channel-Specific Worker Pools: Instead of rigid worker-to-transaction mappings, worker sessions are managed in a dynamic pool per replication channel.
- Dependency-Driven Parallelism: Uses last_committed and sequence_number GTID metadata to schedule non-conflicting transactions concurrently.
- Phase Separation (APPLY vs. COMMIT): When commit-order preservation is enabled, CSA splits the transaction lifecycle into separate phases. A worker completing the APPLY phase can detach and return to the pool while the transaction waits for its turn to COMMIT.
Traditional MTA Worker (APPLIER_VERSION = 1):
[ Worker Thread 1 ] ----> [ APPLY Phase ] ----> [ WAITING FOR COMMIT ORDER (Thread blocked, idle)] ----> [ COMMIT Phase ]
CSA Worker Pool (APPLIER_VERSION = 2):
[ Worker Thread 1 ] ----> [ APPLY Phase ] ----> Detaches & returns to pool!(Available for next transaction)
[ Transaction State ] ------------------------> [ Waiting in queue ] -------------> [ Committed by pool ]The 4-Layer CSA Pipeline
Unlike MTA, which ties a single SQL coordinator thread directly to a rigid set of execution threads, CSA is built as a decoupled 4-layer engine:
- Storage / Transport Layer: Reads relay log events closer to their execution time and immediately releases memory upon event consumption.
- Dependency Adapter: Extracts last_committed and sequence_number GTID metadata from binlogs using the same logical clock mechanism as MTA, but schedules work asynchronously.
- Channel-Specific Scheduler: Schedules tasks to worker sessions independently per channel based on dynamic dependency checks.
- Worker Session Pool: A pool of 1 to 1024 worker threads that claim tasks dynamically.
Configuration & Prerequisites
To run CSA, the replication topology must satisfy row-based logging and GTID consistency constraints:
my.cnf Settings
gtid_mode = ON
enforce_gtid_consistency = ON
replica_preserve_commit_order = ONChannel Prerequisites
Before configuring CSA, the target channel must meet these requirements:
- SOURCE_AUTO_POSITION = 1
- GTID_ONLY = 1
- REQUIRE_ROW_FORMAT = 1
- SOURCE_DELAY = 0
Core CSA Options
Enabling CSA on a Channel
STOP REPLICA FOR CHANNEL 'channel_name';
CHANGE REPLICATION SOURCE TO
APPLIER_VERSION = 2,
APPLIER_WORKER_COUNT = 6
FOR CHANNEL 'channel_name';
START REPLICA FOR CHANNEL 'channel_name';Benchmark Setup: MTA vs. CSA Under Contention
To observe how both appliers handle write contention and serialization blocks, consider a simple table with four concurrent transactions running on the primary database:
CREATE DATABASE IF NOT EXISTS csa_test;
USE csa_test;
CREATE TABLE test_orders (
id INT PRIMARY KEY,
status VARCHAR(50),
amount INT
);
root@warehouse-b2b-db-playground-none-9216947:/tmp# cat t1.sql
USE csa_test;
START TRANSACTION;
SELECT SLEEP(5);
UPDATE test_orders SET status = 'T1' WHERE id = 1;
COMMIT;
root@warehouse-b2b-db-playground-none-9216947:/tmp# cat t2.sql
USE csa_test;
START TRANSACTION;
SELECT SLEEP(10);
UPDATE test_orders SET status = 'T2' WHERE id = 2;
COMMIT;
root@warehouse-b2b-db-playground-none-9216947:/tmp# cat t3.sql
USE csa_test;
START TRANSACTION;
SELECT SLEEP(5);
UPDATE test_orders SET status = 'T3' WHERE id = 1;
COMMIT;
root@warehouse-b2b-db-playground-none-9216947:/tmp# cat t4.sql
USE csa_test;
START TRANSACTION;
SELECT SLEEP(5);
UPDATE test_orders SET status = 'T4' WHERE id = 3;
COMMIT;- T1 ( id = 1): Heavy update batch modifying ~500,010 rows (SET status = 'T1').
- T2 ( id = 2): Light independent update modifying 5 rows (SET status = 'T2').
- T3 (id = 1): Heavy dependent update modifying ~500,010 rows (SET status = 'T3').
- T4 ( id = 3): Light independent update modifying 5 rows (SET status = 'T4').
Dependency Graph:
T1 (id = 1)
|
v
T3 (id = 1) [Dependent on T1]
T2 (id = 2) [Independent]
T4 (id = 3) [Independent]Benchmark Log Analysis
Part 1: Legacy Multi-Threaded Applier (APPLIER_VERSION = 1)
Examining the replica under MTA reveals classic thread starvation and coordinator serialization.
1. Worker Starvation
Even though 6 worker threads are allocated, inspecting the worker status table shows every thread blocked:
+-----------+-----------+---------------+------+---------------------------------------+
| WORKER_ID | THREAD_ID | SERVICE_STATE | GTID | Worker_State |
+-----------+-----------+---------------+------+---------------------------------------+
| 1 | 58 | ON | | Waiting for an event from Coordinator |
| 2 | 59 | ON | | Waiting for an event from Coordinator |
| 3 | 60 | ON | | Waiting for an event from Coordinator |
| 4 | 61 | ON | | Waiting for an event from Coordinator |
| 5 | 62 | ON | | Waiting for an event from Coordinator |
| 6 | 63 | ON | | Waiting for an event from Coordinator |
+-----------+-----------+---------------+------+---------------------------------------+Because the centralized coordinator cannot safely dispatch dependent transactions out of order, it stops assigning new tasks altogether. Independent rows sit in an unapplied state:
+------+--------+
| id | status |
+------+--------+
| 1 | New |
| 2 | New |
| 3 | New |
+------+--------+2. Processlist Blocking
Under sustained execution, SHOW PROCESSLIST highlights the thread-lock issue:
| 9 | system user | csa_test | Query | 14 | Applying batch of row changes (update) | NULL |
| 12 | system user | NULL | Query | 4 | Waiting for preceding transaction to commit | NULL |
| 13 | system user | NULL | Connect | 72 | Waiting for an event from Coordinator | NULL |- Worker 1 (Thread 9): Applying batch of row changes (update) — Actively processing row updates.
- Worker 2 (Thread 12): Waiting for preceding transaction to commit — This thread is associated with a transaction that cannot complete its commit order. Because MTA ties physical threads directly to transactions, Thread 12 remains locked in this wait state instead of taking another ready task off the queue.
3. Single-Thread Pinning
Looking at transaction history metrics confirms that tasks were serialized onto a single thread:
Txn | GTID | WORKER_ID (THREAD_ID) | Duration
+----+---------------------------------------------+-----------------------+----------+
| T1 | 00000000-0000-0000-0000-000000000000:778804 | Worker 1 (44) | 0.0000s |
| T2 | 3f359584-9df0-11f1-9e86-02010a323a76:778806 | Worker 1 (44) | 0.0029s |
| T3 | 3f359584-9df0-11f1-9e86-02010a323a76:778807 | Worker 1 (44) | 5.5076s |
| T4 | 3f359584-9df0-11f1-9e86-02010a323a76:778808 | Worker 1 (44) | 4.4446s |
+----+---------------------------------------------+-----------------------+----------+
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+
| GTID | THREAD_ID | EVENT_ID | start_sec | end_sec | duration_sec | STATE |
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+
| 3f359584-9df0-11f1-9e86-02010a323a76:778808 | 44 | 15 | 49.9310 | 54.3756 | 4.4446 | COMMITTED |
| 3f359584-9df0-11f1-9e86-02010a323a76:778807 | 44 | 13 | 44.4231 | 49.9307 | 5.5076 | COMMITTED |
| 3f359584-9df0-11f1-9e86-02010a323a76:778806 | 44 | 11 | 37.4776 | 37.4805 | 0.0029 | COMMITTED |
| 00000000-0000-0000-0000-000000000000:778804 | 44 | 9 | 0.9970 | 0.9971 | 0.0000 | COMMITTED |
| 00000000-0000-0000-0000-000000000000:778802 | 44 | 7 | 0.9627 | 0.9969 | 0.0342 | COMMITTED |
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+Key MTA Failure: All primary tasks (T1 through T4) were forced onto Worker 1 (Thread 44) serially. Transaction :778807 ran for 5.5076 seconds, and only after it hit its COMMITTED boundary tick did the next transaction (:778808) begin execution on the exact same thread for another 4.4446 seconds.
Part 2: Change Stream Applier Behavior (APPLIER_VERSION = 2)
Running the exact same four-transaction workload under CSA produces an entirely different execution pattern.
1. Worker Pool Status
+-----------+-----------+---------------+---------------------------------------------+----------------------------------------+
| WORKER_ID | THREAD_ID | SERVICE_STATE | GTID | Worker_State |
+-----------+-----------+---------------+---------------------------------------------+----------------------------------------+
| 1 | 42 | ON | 3f359584-9df0-11f1-9e86-02010a323a76:778815 | Applying batch of row changes (update) |
| 2 | 43 | ON | | Thread pool worker: wait for task. |
| 3 | 44 | ON | | Thread pool worker: wait for task. |
| 4 | 45 | ON | | Thread pool worker: wait for task. |
| 5 | 46 | ON | | Thread pool worker: wait for task. |
| 6 | 47 | ON | 3f359584-9df0-11f1-9e86-02010a323a76:778816 | Thread pool worker: wait for task. |
+-----------+-----------+---------------+---------------------------------------------+----------------------------------------+- CSA Worker 1 (Thread 42): Actively applying a transaction (performing row updates).
- Dynamic Pool Worker Availability: Unassigned workers remain in Thread pool worker: wait for task., managed dynamically to pull ready work off the channel queue.
2. The Defining State: Session Detachment
During execution, SHOW PROCESSLIST records:
73 system user | | Query | 11 | Session detached from worker thread.
74 system user | csa_test | Query | 11 | Applying batch of row changes (update)
75 system user | | Query | 6 | Waiting for preceding transaction to commitKey CSA Innovation: "Session detached from worker thread."
This state confirms that the session is no longer occupying the physical worker thread for that phase of the transaction's lifecycle. The session completed its APPLY work and detached, allowing the physical worker thread to return to the pool immediately to process other unblocked transactions.
3. Concurrent Execution Traces
Checking the trace logs shows work balanced across multiple worker threads simultaneously:
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+
| GTID | THREAD_ID | EVENT_ID | start_sec | end_sec | duration_sec | STATE |
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+
| 3f359584-9df0-11f1-9e86-02010a323a76:778815 | 42 | 2 | 55.7403 | 61.1282 | 5.3879 | COMMITTED |
| 3f359584-9df0-11f1-9e86-02010a323a76:778813 | 44 | 2 | 43.2463 | 43.2498 | 0.0035 | COMMITTED |
| 3f359584-9df0-11f1-9e86-02010a323a76:778814 | 46 | 2 | 50.0296 | 55.7400 | 5.7104 | COMMITTED |
| 3f359584-9df0-11f1-9e86-02010a323a76:778816 | 47 | 2 | 55.2758 | 61.1282 | 5.8524 | COMMITTED |
+---------------------------------------------+-----------+----------+-----------+---------+--------------+-----------+Key CSA Advantage: Work execution was balanced concurrently across Threads 42, 44, 46, and 47. Thread 46 executed from 50.0296 to 55.7400, while Thread 47 ran concurrently right in the middle of that window (55.2758 to 61.1282).
Visualizing the Difference
Under MTA, a single thread executes all dependent and independent tasks sequentially, stalling replica read consistency for several seconds. Under CSA, worker threads apply independent transactions immediately, detaching to let secondary tasks catch up within milliseconds.
Architectural Comparison Matrix
Beyond thread pooling, CSA introduces critical operational upgrades documented in Oracle Worklogs:
Operational Upgrades in CSA
Beyond thread scheduling, CSA introduces three notable stability improvements:
1. Bounded Event Memory Management
Under MTA, high backlog catch-up frequently causes the coordinator to buffer massive numbers of binlog events into worker queues, increasing the risk of replica Out-of-Memory (OOM) crashes.
CSA establishes a strict cap on memory usage:
Effective Limit = max(replica_max_allowed_packet, APPLIER_EVENT_MEMORY_LIMIT)Events load on demand, are applied, and release their memory buffers immediately. This keeps memory predictable even when catching up across gigabytes of relay logs.
2. Subscriber-Driven Relay Log Purging
In MTA, relay log files are cleared strictly sequentially by the coordinator thread. CSA implements a subscriber pattern: once all events within a relay log segment have been applied by the worker pool, that relay log file is immediately marked for purge, keeping replica disk consumption lower.
3. Explicit Stop Orchestration
When running STOP REPLICA on a heavily lagged MTA replica, threads often hang while waiting for earlier transactions to reach commit points. CSA decouples session execution from worker threads, so stop signals are handled immediately. Workers clean up active buffers and stop in milliseconds rather than minutes.
Monitoring CSA with Performance Schema
MySQL provides native Performance Schema tables to inspect CSA configuration and worker thread execution:
1. Verify Applier Configuration
SELECT
CHANNEL_NAME,
APPLIER_VERSION,
APPLIER_WORKER_COUNT,
APPLIER_EVENT_MEMORY_LIMIT
FROM performance_schema.replication_applier_configuration;2. Monitor Live Worker Sessions
SELECT
CHANNEL_NAME,
WORKER_ID,
THREAD_ID,
SERVICE_STATE,
APPLYING_TRANSACTION,
LAST_APPLIED_TRANSACTION,
LAST_ERROR_NUMBER,
LAST_ERROR_MESSAGE
FROM performance_schema.replication_applier_status_by_worker;Summary
The Change Stream Applier in MySQL 26.7 addresses the structural bottlenecks that have limited the Multi-Threaded Applier since MySQL 5.7 and 8.0. By separating transaction application from commit-order preservation and switching to an asynchronous worker-pool model, CSA keeps worker threads utilized, prevents thread starvation, and shortens catch-up time on write-heavy replicas.
If your workloads frequently suffer from replication lag during bulk updates or back-office batch jobs, testing APPLIER_VERSION = 2 on your MySQL 26.7 staging clusters is a worthwhile operational evaluation.
Struggling with replication lag, thread contention, or high-availability failover issues in your MySQL environments? The database engineers at Mydbops help enterprises design high-throughput replication architectures, audit MySQL performance bottlenecks, and execute seamless database upgrades.
Talk to a MySQL replication specialist today to assess your database architecture.

_%20How%20MySQL%2026.7%20Fixes%20Replication%20Lag.avif)
.avif)

.avif)
