View a markdown version of this page

Aurora MySQL 的二进制日志复制滞后故障排除 - Amazon Aurora

Aurora MySQL 的二进制日志复制滞后故障排除

本节针对将 Aurora MySQL 配置用于二进制日志副本的情况,提供有关二进制日志复制滞后的指导。本节涵盖了复制架构、并行复制、依赖项跟踪、监控和配置最佳实践。

二进制日志复制可在兼容 MySQL 的数据库之间复制数据。本节介绍的二进制日志复制为异步复制:源在提交事务之前,不会等待副本确认已应用更改。本节的内容不适用于半同步复制。在半同步复制中,源在提交前,会等待至少一个副本确认收到事务。例如,Amazon RDS for MySQL 的多可用区数据库集群使用半同步复制,其内容不在本节讨论范围之内。副本通过 I/O 线程与源保持持久连接,以持续流式传输二进制日志事件。本节适用于所有以 Aurora MySQL 为副本的二进制日志复制拓扑。源可以是另一个 Aurora MySQL 集群、Amazon RDS for MySQL、本地 MySQL 或 Amazon EC2 上的 MySQL。本节同样适用于将 Aurora MySQL 作为源复制到任何 MySQL 兼容目标的情况。

注意

对于跨区域复制使用案例,可以考虑使用 使用 Amazon Aurora Global Database 作为基于二进制日志的复制的替代方案。Aurora 全球数据库使用专用基础设施进行复制,与跨区域二进制日志复制相比,延迟更低,所需的运维管理也更少。

遇到了滞后高峰? 跳过背景资料,从 确定复制滞后瓶颈 开始,确定瓶颈是 I/O 线程还是 SQL 线程,然后点击对应场景的链接:I/O 线程滞后故障排除 或 SQL 线程滞后故障排除。

MySQL 复制架构

MySQL 复制通过专用线程实施:

  • 二进制日志转储线程(源):在副本连接时创建。将二进制日志内容发送到副本。在 SHOW PROCESSLIST 中显示为“Binlog Dump”线程。

  • 复制 I/O 线程(副本):连接到源并请求二进制日志更新。系统将它们写入副本的中继日志。无论多线程复制(MTR)配置如何,每个复制通道始终只有一个线程。

  • 复制 SQL 线程(副本):读取中继日志并应用事务。应用器始终包含一个协调器线程(从中继日志中读取事务),以及用于应用事务的 N 个工作线程,其中 N 是 replica_parallel_workers 的值。使用 replica_parallel_workers=1 时,单个工作线程按顺序应用事务。使用 replica_parallel_workers >= 2 时,协调器将独立事务分配给多个工作线程来并行应用。

    注意

    从 MySQL 8.0.30 起,设置 replica_parallel_workers=0 已被弃用,并可能在未来的 MySQL 版本中移除。请使用 replica_parallel_workers=1,改为单线程应用。

复制流程如下:

  1. 源执行 DML、DCL 或 DDL 语句。

  2. 提交时,源将数据写入二进制日志。

  3. 副本上的 I/O 线程提取事件,并将其写入中继日志。

  4. SQL 线程应用来自中继日志的更改(单线程或多线程)。

确定复制滞后瓶颈

复制滞后可能发生在两个方面:I/O 线程或 SQL 线程。第一步是确定哪个组件存在滞后。

注意

SHOW REPLICA STATUS 中的 Seconds_Behind_Source 字段用于测量在源上记录事件与 SQL 线程应用该事件之间的延迟。此指标并非专门用于指示 I/O 线程滞后。要确定 I/O 线程滞后,必须按照以下步骤中的说明比较二进制日志位置。

注意

本节中显示的某些 MySQL 命令和内部字符串(例如 SHOW MASTER STATUS)使用的是旧的术语。本文档自始至终使用首选术语“源”和“副本”。

确定存在滞后的复制线程
  1. 在副本上,运行 SHOW REPLICA STATUS,并将 Source_Log_File/Read_Source_Log_Pos(I/O 线程位置)与源上 SHOW MASTER STATUS 中的 File/Position 进行比较。如果两者差异显著(相差超过 50 MB 或相差超过 1 个二进制日志文件),则说明 I/O 线程存在滞后。

  2. 将 I/O 线程位置与 SQL 线程位置 (Relay_Source_Log_File/Exec_Source_Log_Pos) 进行比较。如果 I/O 线程已追上,但 SQL 线程落后(相差超过 50 MB),则 SQL 线程是瓶颈所在。

使用仅限副本的数据快速进行初步评测

您可以仅使用副本端的数据执行初步评测。运行 SHOW REPLICA STATUS 两次,间隔 1–2 分钟,并观察以下内容:

  • 如果 Read_Source_Log_Pos 在推进,但 Exec_Source_Log_Pos 停滞不前或推进速度慢得多,则 SQL 线程是瓶颈(场景 B)。继续执行SQL 线程滞后故障排除。

  • 如果 Read_Source_Log_Pos 和 Exec_Source_Log_Pos 都停滞不前,而 Seconds_Behind_Source 持续增长,则很可能是 I/O 线程造成了落后。在更改配置之前,请按照前述过程的步骤 1 中所述,使用 SHOW MASTER STATUS 将副本位置与源进行比较,以确认当前状态。

场景 I/O 线程与源 SQL 线程与 I/O 线程 瓶颈
A 远远落后(>50 MB) 接近(<10 MB) I/O 线程
B 接近(<10 MB) 远远落后(>50 MB) SQL 线程
C 远远落后 远远落后 两者(从 I/O 开始)
例解释二进制日志位置

以下示例演示如何比较二进制日志位置以确定瓶颈。

在副本上,SHOW REPLICA STATUS 返回:

Source_Log_File: mysql-bin.000045 Read_Source_Log_Pos: 524288000 Relay_Source_Log_File: mysql-bin.000045 Exec_Source_Log_Pos: 524200000

在源上,SHOW MASTER STATUS 返回:

File: mysql-bin.000045 Position: 1073741824

要确定 I/O 线程滞后,请从源位置减去 I/O 线程位置:

(1073741824 − 524288000)/1048576 = 524 MB:I/O 线程远远落后于源(场景 A)。

要确定 SQL 线程滞后,请从 I/O 线程位置减去 SQL 线程位置:

(524288000 − 524200000)/1048576 = 0.08 MB:SQL 线程跟上了 I/O 线程的进度。

在这种情况下,请将故障排除重点放在 I/O 线程上。有关更多信息,请参阅 I/O 线程滞后故障排除。

复制滞后峰值剖析

一种常见的模式是 Seconds_Behind_Source 突然飙升,然后迅速恢复到接近零的水平。当源上 DML 运行时间长(例如,UPDATE 扫描数百万行,但只修改少数几行),需要几分钟时间来执行时,就会发生这种情况。使用基于行的二进制日志记录时,只有修改过的行才会写入二进制日志。当 SQL 线程选取此事件时,它会根据事务在源上的开始时间来计算滞后。这会产生较大的初始值。但是,由于只需应用很少的几行,副本会很快追上进度。这是意料之中的行为,并不表示存在持续性的复制问题。

I/O 线程滞后故障排除

I/O 线程负责从源提取二进制日志事件并将其写入中继日志。常见原因及解决方案:

原因 如何识别 解决方案
网络带宽限制 检查 CloudWatch NetworkReceiveThroughput/NetworkTransmitThroughput 使用具有更高网络容量的实例;确保源实例与副本具有相似的实例类
网络延迟(跨区域或本地) 源与副本之间的地理距离;往返时间长 对于本地源,请使用低延迟的专用连接
源上的资源限制 CPU 占用率高 (CPUUtilization)、内存压力大 (FreeableMemory) 纵向扩展源实例。每个已连接的副本都会在源上创建一个二进制日志转储线程,如果源上的资源受限,请减少连接的副本数量。
包含 BLOB/TEXT 数据的大型事务 通过 CloudWatch 指标 SumBinaryLogSize 监控二进制日志事件大小 设置 binlog_row_image=noblob;启用二进制日志事务压缩 (binlog_transaction_compression=ON)。首先在非生产环境中进行测试,因为压缩会增加源和副本上的 CPU 利用率。
达到中继日志空间限制 Replica_IO_State 显示“正在等待副本 SQL 线程释放足够的中继日志空间” 这表明根本原因是 SQL 线程,而非 I/O 线程。首先解决 SQL 线程滞后问题。在 Aurora MySQL 中,默认 relay_log_space_limit 约为 953 MiB。当 SQL 线程无法以足够快的速度应用更改时,此消息属于正常操作的一部分。此消息不一定表示存在 I/O 线程性能问题。

针对 I/O 线程滞后的优化策略

  1. 使用至少与源相同的实例类:此方法提供足够的 CPU、内存、I/O 容量和网络带宽。

  2. 确保有足够的网络带宽:使用具有高网络容量的实例。对于本地源,请考虑使用专用带宽以获得更稳定的网络体验。

  3. 检查源端资源,尤其是存在大量副本时:监控源的 CPU、内存、网络和二进制日志 I/O。每个连接的副本都会在源上新增一个二进制日志转储线程,因此提供大量副本的源本身可能成为瓶颈。我们建议在纵向扩展副本之前,先确认源未处于饱和状态。如果源的资源受限,请考虑减少连接的副本数量。

  4. 验证 Aurora 二进制日志 I/O 缓存是否处于活动状态:二进制日志 I/O 缓存可减少源处理二进制日志事件时的磁盘 I/O 开销。在 Aurora MySQL 版本 2.10 及更高版本中,此缓存自动启用,无需配置。如果源是 Aurora 集群,请验证缓存是否在与 SHOW GLOBAL STATUS LIKE 'aurora_binlog_io_cache%' 一起使用。有关更多信息,请参阅 优化二进制日志复制。

  5. 启用二进制日志事务压缩:在参数组中设置 binlog_transaction_compression=ON。降低带宽需求。此设置有以下注意事项:

    • 压缩会增加源和副本上的 CPU 利用率。

    • 请先在非生产环境中进行测试,以便在压缩和资源利用率之间找到最佳平衡。

    • (可选)使用 binlog_transaction_compression_level_zstd 调整压缩级别(默认值:3,范围:1–22)。

  6. 尽量减少对二进制日志的写入:设置 binlog_row_image=noblob,消除二进制日志中的 BLOB/TEXT 数据。如果副本具有引用 BLOB 列的触发器,请勿使用此项。有关更多信息,请参阅 MySQL 网站上的 binlog_row_image。

  7. 在源上实施复制筛选条件:使用 binlog-do-db 或 binlog-ignore-db 排除不必要的数据库。这些静态参数需要重新启动。

    重要

    源端筛选条件会彻底影响写入二进制日志的内容。排除的数据库不会复制到任何下游副本。如果源是自行管理的 MySQL 实例(本地或 Amazon EC2 上),则排除的数据库也无法用于基于二进制日志的时间点恢复。Amazon RDS for MySQL 不支持源端二进制日志筛选(binlog-do-db/binlog-ignore-db 不可配置);请改用副本端的复制筛选条件。Aurora 使用集群卷进行 PITR,因此对于备份用途,不受二进制日志筛选条件的影响。

SQL 线程滞后故障排除

当 I/O 线程已追上源,但 Seconds_Behind_Source 仍在增长时,SQL 线程是瓶颈所在。要量化滞后情况,请对 15 分钟的时间窗口进行监控。将源上的二进制日志生成速率(Position 相对于 SHOW MASTER STATUS 的增量),与副本上的 SQL 线程应用速率(Exec_Source_Log_Pos 增量)进行比较。

常见原因及解决方案:

原因 如何识别 解决方案
单线程复制 SELECT @@global.replica_parallel_workers; 返回 0 或 1 启用多线程复制(MTR)与 WRITESET 依赖项跟踪。如果时钟冲突较多,仅靠 MTR 并不能解决滞后问题。有关调整依赖项跟踪的信息,请参阅 源上的依赖项跟踪;有关识别时钟冲突的信息,请参阅 错误日志多线程副本统计数据。
缺少主键 如果没有主键(或 NOT NULL 的唯一键),在 UPDATE 和 DELETE 操作中,对于每个修改过的行,副本将执行全表扫描。使用以下查询来识别没有主键的表:
SELECT t.table_schema, t.table_name FROM information_schema.tables t LEFT JOIN information_schema.table_constraints tc ON t.table_schema = tc.table_schema AND t.table_name = tc.table_name AND tc.constraint_type = 'PRIMARY KEY' WHERE tc.constraint_type IS NULL AND t.table_type = 'BASE TABLE' AND t.table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') ORDER BY 1;
将主键添加到所有复制的表中。如果无法立即添加明确的主键,请考虑通过将 sql_generate_invisible_primary_key 参数设置为 ON 来启用生成的不可见主键(GIPK)(适用于基于 MySQL 8.0.30 及更高版本的 Aurora MySQL 3 版本)。有关生成的不可见主键的更多信息,请参阅 MySQL 网站上的 Generated Invisible Primary Keys。
源上有大量写入事务 修改多行的事务(例如,批量 UPDATE 或 DELETE)会作为单个单元进行复制,无论 MTR 配置如何,该事务只通过一个工作线程来应用。在源上,使用以下方法识别长时间运行的活跃写入事务:
SELECT trx_id, trx_started, trx_rows_modified, trx_query FROM information_schema.innodb_trx WHERE trx_rows_modified > 10000 ORDER BY trx_started;
在副本上,一个工作线程持续处理 SHOW PROCESSLIST 中的同一语句,而其他工作线程则处于空闲状态。长时间运行的空闲事务或在源上等待锁定的事务本身不会导致复制滞后,只有已提交的写入量才会产生影响,因为事件在提交时才会写入二进制日志。
将批量操作拆分为较小的事务(例如,每次提交几千行),以便副本并行应用这些事务。有关有效示例,请参阅 示例 4:无法并行处理单个大型事务。
DDL 操作 DDL 会阻止其他复制事件 使用在线架构更改工具,或使用蓝绿部署。请在低流量时段安排执行 DDL 操作。有关 pt-online-schema-change 的信息,请参阅 Percona 网站上的 Percona Toolkit。有关 gh-ost 的信息,请参阅 GitHub 网站上的 gh-ost。
并行复制配置不佳 工作线程利用率低;协调器统计数据中存在大量时钟冲突。在多线程副本统计数据错误日志条目中,检查“时钟冲突时的等待时间”字段(请参阅 错误日志多线程副本统计数据)。如果相对于“经过的秒数”而言该值较高,则表示事务频繁地被依赖项阻止。 调整依赖项跟踪(WRITESET)和工作线程数量
分析工作负载引起的资源争用 复杂的 OLAP 查询会与 SQL 线程争夺资源(CPU、内存) 将分析工作负载分离到专用副本
过时的表统计数据 副本上的查询计划性能不佳。使用以下查询来识别统计数据过时(超过 7 天)的表:
SELECT database_name, table_name, MIN(last_update) AS oldest_stat_update, DATEDIFF(NOW(), MIN(last_update)) AS stats_age_in_days FROM mysql.innodb_index_stats WHERE database_name NOT IN ('mysql', 'sys') GROUP BY database_name, table_name HAVING stats_age_in_days > 7 ORDER BY stats_age_in_days DESC;
定期对统计数据过时的表运行 ANALYZE TABLE

使用副本端复制筛选条件减少应用工作负载

Aurora MySQL 版本 3.01.0 及更高版本支持副本端的复制筛选条件。在应用过程中,使用这些筛选条件可跳过不相关的数据库或表,从而减少 SQL 线程的工作负载。副本端的筛选条件由 SQL 线程应用,I/O 线程仍会从源提取所有二进制日志事件,因此这些筛选条件仅减少应用工作,而不减少网络传输。与源端的筛选条件(仅在数据库级别运行)不同,副本端的筛选条件支持使用 replicate-do-table 和 replicate-ignore-table 实现表级粒度。有关更多信息,请参阅 使用 Aurora MySQL 配置复制筛选条件。

多线程复制(MTR)

对于 MTR,副本并行应用独立事务。它无法将单个大型事务拆分为并行的多个部分。并行度受由源写入的依赖项信息限制。

启用 MTR (replica_parallel_workers >= 2) 后,SQL 应用器将拆分为一个协调器线程和多个工作线程。协调器从中继日志中读取事务。它检查源嵌入每个事务中的逻辑时间戳(last_committed 和 sequence_number)。然后,它将独立事务分配给可用的工作线程来并行执行。如果两个事务不共享数据依赖项(即修改的是不同的行),则这两个事务是独立的。源在提交时,使用所配置的依赖项跟踪方法确定这些依赖项,并将其记录在二进制日志中。副本无法将并行度提升至源所允许的范围之外。

MTR 是推荐的默认设置

我们建议在启用 MTR 的情况下运行副本。在 MySQL 8.0.27 及更高版本中,包括基于这些版本的 Aurora MySQL 3 版本,默认启用 MTR (replica_parallel_workers=4)。在早期版本(Aurora MySQL 2.12.1 及更高版本,或基于 MySQL 8.0.27 之前版本的版本)上,请将 replica_parallel_workers 设置为 2 或更高的值来明确启用它。在并行处理不可用时,保持 MTR 启用的开销极小;只要工作负载允许,尽量让副本利用此功能。

在以下情况下,MTR 不会减少滞后:

  • 瓶颈在于 I/O 线程(网络/带宽问题)。

  • 滞后由单个长时间运行的事务引起。

  • 副本的 CPU 已饱和(增加更多线程会使滞后更严重)。

  • 表缺少主键(强制全表扫描,抵消并行处理带来的收益)。

源上的依赖项跟踪

源使用 binlog_transaction_dependency_tracking 参数,确定哪些事务可以并行应用。建议值:WRITESET。

  • COMMIT_ORDER:根据提交时间跟踪依赖项。对于高并发和较大的分组提交,效果最佳。在低并发环境中,并行度受限。

  • WRITESET(推荐):跟踪实际的行级数据依赖项。性能始终至少与 COMMIT_ORDER 相当。对于低并发工作负载,效益显著。要求表具有主键。

    在以下情况下,WRITESET 依赖项跟踪会生成空写入集或部分写入集(从而限制并行度):

    • 没有主键或唯一键的表

    • DDL 语句(CREATE TABLE、ALTER TABLE 等)

    • 修改外键关系中父表的事务

    此外,在轮换二进制日志时或在达到 binlog_transaction_dependency_history_size 限制时,依赖项历史记录会被清除,从而暂时降低并行度。

  • WRITESET_SESSION:与 WRITESET 相同,但增加了一个约束条件:来自同一会话的事务无法并行化。

注意

MySQL 8.4 已移除 binlog_transaction_dependency_tracking,默认使用 WRITESET(不可配置)。早期版本默认为 COMMIT_ORDER。

并行类型 (replica_parallel_type)

replica_parallel_type 参数决定事务如何在工作线程之间的分配。有两种方式:

  • LOGICAL_CLOCK(推荐):使用逻辑时间戳确定哪些事务(即使在同一数据库中)可以并行执行。对于大多数工作负载,这种方法可带来更高的复制吞吐量和更低的延迟。除非您的特定多数据库工作负载无法从中受益,否则请使用它。

  • DATABASE:根据事务所影响的数据库,将事务分配给工作线程。仅当应用程序使用多个数据库且工作负载明确分离、事务极少跨越数据库边界时,才考虑使用此模式。使用 DATABASE 时,不使用源上的 binlog_transaction_dependency_tracking 设置,并行度完全取决于事务的目标数据库。

注意

在 MySQL 8.0.26 及更早版本中,replica_parallel_type 默认为 DATABASE。从 MySQL 8.0.27 开始默认为 LOGICAL_CLOCK。如果您使用的是较早版本,请将此参数明确设置为 LOGICAL_CLOCK,以利用更精细的依赖项跟踪。

MTR 配置

源端参数
参数建议值备注
binlog_transaction_dependency_tracking WRITESET 集群参数组。动态。在 MySQL 8.4 中不需要。
binlog_transaction_dependency_history_size 25000(默认值) 集群参数组。动态。控制源保留多少行哈希用于 WRITESET 依赖项跟踪;当历史记录填满或二进制日志轮换时,这些哈希值将被清除,并行度会暂时下降。在写入密集型源上,如果在“时钟冲突时的等待时间”错误日志统计数据(请参阅 错误日志多线程副本统计数据)中,副本表现出与二进制日志轮换一致的周期性峰值,则可以考虑将写入密集型源上的值翻倍(例如,调整为 50000)。值越大,在源上消耗的内存就越多。
binlog_format ROW 集群参数组。静态(需要重新启动)。
binlog_group_commit_sync_delay 0(默认值) 延迟分组提交的微秒数。增加此值会将更多事务分为一组,从而提升副本上的并行度,代价是源上的提交延迟略有增加。仅在使用 COMMIT_ORDER 依赖项跟踪时有效,无论提交时间如何,WRITESET 均已跟踪实际的行级依赖项。动态。
binlog_group_commit_sync_no_delay_count 0(默认值) 提交前等待的最大事务数。与 binlog_group_commit_sync_delay 配合使用,可在有足够的事务分批时限制延迟。仅与 COMMIT_ORDER 依赖项跟踪相关。动态。
副本端参数
参数建议值备注
replica_parallel_workers 起始值为 vCPU 数量,最多为 vCPU 数量的两倍 实例参数组。动态,但需要重启复制。从 MySQL 8.0.27(及基于该版本的 Aurora MySQL 3 版本)开始,社区默认值为 4;早期版本默认值为 0,该值自 MySQL 8.0.30 起已被弃用。建议起始值为 vCPU 数量,然后监控 CPU 利用率以及“工作线程占用时的等待(计数)”错误日志统计数据(请参阅 错误日志多线程副本统计数据)。仅当“工作线程占用时的等待(计数)”持续偏高且 CPU 利用率保持在 80% 以下时,才考虑增加该值。
replica_parallel_type LOGICAL_CLOCK 集群参数组。动态,但需要重启复制。
replica_pending_jobs_size_max >= max_allowed_packet(在源上) 实例参数组。动态。
replica_preserve_commit_order 开 集群参数组。动态,但需要重启复制。
binlog_format 关闭 集群参数组。静态。Aurora 特有的扩展,用于禁用二进制日志记录。

更改并行工作线程设置后,重启复制:

CALL mysql.rds_stop_replication; CALL mysql.rds_start_replication;

Aurora 特有的复制优化

Aurora MySQL 提供了以下功能来提高二进制日志复制性能。下表总结了每项功能的可用性、默认状态以及启用方法。

功能 版本 默认值 如何启用/验证
内存中继日志 Aurora MySQL 3.10+ 开启(用于满足条件时由 Aurora 管理的复制) 当副本使用单线程复制 (replica_parallel_workers=0)、启用了 GTID 模式和自动定位的多线程复制,或者通过 replica_preserve_commit_order=ON 使用基于文件的复制时,系统自动为 Aurora 管理的复制(蓝绿部署、Aurora 到 Aurora 以及跨区域副本)启用此功能。由动态参数 aurora_in_memory_relaylog(在数据库集群或实例级别)控制:停止复制,在参数组中将其设置为 ON 或 OFF,然后重新启动复制,无需重启实例。在 Aurora Serverless 上不可用。使用 SHOW GLOBAL STATUS LIKE 'Aurora_in_memory_relaylog_status' 验证当前状态。有关更多信息,请参阅 内存中继日志。
并行二级索引更改 Aurora MySQL 3.06+ 关闭 (0) 将 aurora_binlog_replication_sec_index_parallel_workers 设置为所需的线程数。停止复制,设置参数,然后开始复制。无需重启实例。有关更多信息,请参阅 多线程二进制日志复制。
二进制日志 I/O 缓存 Aurora MySQL 2.10+ 和 3.x 开启(自动) 自动启用。向副本传输二进制日志事件时,减少源上的磁盘 I/O。无需配置。有关更多信息,请参阅 优化二进制日志复制。

监控并行复制

使用以下方法,监控复制性能并确定并行应用中的瓶颈:

性能模式

要监控 MTR 的关键表:

  • performance_schema.replication_applier_status_by_worker:显示每个工作线程处理的事务的详细信息,包括应用时间戳和错误。

  • performance_schema.replication_applier_status_by_coordinator:显示有关协调器线程的缓冲活动的信息。

要评估是否在工作线程之间均匀地分配了工作,请使用以下查询:

SELECT a.channel_name, rw1.thread_id AS replica_worker_thread_id, ts1.count_star AS number_of_trx_executed, ROUND((ts1.count_star / sum_count_star) * 100, 2) AS percent_of_trx_executed_by_worker FROM ( SELECT rw.channel_name, SUM(ts.count_star) AS sum_count_star FROM performance_schema.events_transactions_summary_by_thread_by_event_name AS ts JOIN performance_schema.replication_applier_status_by_worker AS rw ON ts.thread_id = rw.thread_id GROUP BY rw.channel_name ) AS a JOIN performance_schema.replication_applier_status_by_worker AS rw1 ON a.channel_name = rw1.channel_name JOIN performance_schema.events_transactions_summary_by_thread_by_event_name AS ts1 ON rw1.thread_id = ts1.thread_id;

如果一两个工作线程处理了大部分事务,则表明事务依赖性较高,限制了并行度。考虑在源上切换到 WRITESET 依赖项跟踪。

如果 I/O 线程和 SQL 线程的位置彼此接近,但 Seconds_Behind_Source 仍在增长,则协调器线程本身可能是瓶颈。检查 performance_schema.replication_applier_status_by_coordinator 确定是否存在缓冲延迟。协调器瓶颈通常表明严重的事务依赖,或单个协调器线程过载,无法以足够快的速度分发事件。

错误日志多线程副本统计数据

使用 log_error_verbosity=3(默认值)时,Aurora MySQL 会定期将多线程副本统计数据写入 MySQL 错误日志。您可以通过以下方式查看这些条目:

  • 在 RDS 控制台的日志和事件部分,查看错误日志。

  • 通过 AWS CLI 下载:aws rds download-db-log-file-portion --db-instance-identifier <instance-id> --log-file-name error/mysql-error-running.log

  • 如果为错误日志启用了 CloudWatch Logs 导出,则在导出的日志组中搜索。

要在错误日志中定位多线程副本统计数据条目,请搜索以下示例中显示的字符串。MySQL 错误日志在此内部字符串中使用旧的术语;本文档自始至终使用首选术语“副本”。

以下是一个错误日志条目示例:

2026-05-10 14:54:28.045308 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 120; events assigned = 7215200; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 1203644200 waited (count) when Workers occupied = 1022462 waited when Workers occupied = 62349203500

要分析的关键字段:

字段说明处理建议
经过的秒数 自上次输出统计数据以来经过的时间,以秒为单位。Aurora MySQL 不会按固定间隔写入统计数据,而是根据已执行的事件数量以及经过的时间来写入统计数据。 用于计算吞吐量:已分配事件数/经过的秒数 = 平均每个间隔的事件数。
已分配事件数 自上次输出以来,协调器线程分配到工作线程的事件数。 监控复制吞吐量随时间变化的情况。
工作线程队列已填满并超过溢出级别 在工作线程中排队的事件数超过溢出级别(最大队列长度 16384 个事件的 90%)。如果为零,则表示没有工作线程以较高容量运行。 表示工作线程无法跟上协调器的处理速度。检查是否存在长时间运行的事务或缺少索引。
因工作线程队列已满而等待 由于工作线程的队列已满(达到 100% 容量),协调器不得不等待的次数。 检查工作线程上是否存在长时间运行的事务、是否缺少索引或是否存在锁争用。
因超出总大小而等待 因达到 replica_pending_jobs_size_max 限制导致协调器等待的次数。如果某个异常大的事件超过此大小,则事务将一直挂起,直到所有工作线程的队列均为空。 增加 replica_pending_jobs_size_max。检查源上是否存在大型事务。
时钟冲突时的等待时间 由于某个事务依赖于另一个尚未提交的事务,协调器等待的纳秒数。此项量化由于依赖项导致事件无法分配的时间。 在源上切换到 WRITESET 依赖项跟踪。确保表具有主键。预计会出现一些时钟冲突等待,重点放在降低这些等待相对于其他等待的比率。
工作线程占用时的等待(计数) 协调器需要分配事务的第一个事件、但所有工作线程队列均非空的次数。协调器将持续休眠,直到某个队列为空。 这表示 replica_parallel_workers 预调配不足。依赖项不是瓶颈,您本可以并行执行更多事件,但缺少可用的工作线程。增加 replica_parallel_workers。
工作线程占用时的等待 协调器在等待空工作线程队列时休眠的总纳秒数。 较高的值证实工作线程容量是瓶颈所在。增加 replica_parallel_workers。

有效示例:诊断并行应用瓶颈

以下示例演示如何使用前述对源的监控来诊断常见的并行应用瓶颈。每个示例均从相同的症状出发:Seconds_Behind_Source 持续稳步增长,且您已确认 SQL 线程是瓶颈(请参阅 确定复制滞后瓶颈);然后使用多线程副本统计数据和性能架构来确定纠正措施。

示例 1:工作线程预调配不足

在 CloudWatch 中,ReplicaLag 指标在 30 分钟内从接近零稳步攀升至约 400 秒。副本上的 CPU 利用率保持在 55% 左右,因此副本没有受到 CPU 限制。

要确认发生滞后的位置,请间隔 60 秒运行 SHOW REPLICA STATUS 两次。Read_Source_Log_Pos 推进了约 1.2 GB,而 Exec_Source_Log_Pos 仅推进了约 300 MB。I/O 线程能够跟上源的进度,但 SQL 线程落后,因此 SQL 线程是瓶颈所在。

使用 replica_parallel_workers=4 启用了 MTR。从错误日志中检索最新的多线程副本统计数据(请参阅 错误日志多线程副本统计数据):

2026-05-10 14:54:28.045308 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 123; events assigned = 9876544; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 8455000 waited (count) when Workers occupied = 2456789 waited when Workers occupied = 110293847000

解释关键字段:

  • 工作线程占用时的等待 = 110293847000 纳秒,即大约 110 秒。在此时间间隔的 123 秒中,协调器将大约 90% 的时间用在等待工作线程变为空闲。

  • 时钟冲突时的等待时间 = 8455000 纳秒,即大约 0.008 秒,可以忽略不计。事务依赖性不是限制因素。

诊断:协调器上已经就绪、可以应用的独立事务的数量,始终超过了可以运行这些事务的工作线程的数量。由于时钟冲突等待可以忽略不计,因此并行度受工作线程数量限制,而非依赖项限制。

操作:将 replica_parallel_workers 增加至副本的 vCPU 数量(例如,在 db.r6g.4xlarge 上为 16),然后重启复制:

CALL mysql.rds_stop_replication; CALL mysql.rds_start_replication;

验证:稍后的统计数据行显示等待几乎消失,且 ReplicaLag 恢复到 5 秒以内:

2026-05-10 15:02:14.882201 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 120; events assigned = 31840552; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 9120000 waited (count) when Workers occupied = 41233 waited when Workers occupied = 5980411000

“工作线程占用时的等待”从大约 110 秒降至大约 6 秒,吞吐量(分配的事件数)增加了两倍以上。在 CPU 利用率接近 80% 时,或等待时间不再减少时,停止增加工作线程的数量。

示例 2:COMMIT_ORDER 依赖项跟踪对低并发工作负载进行串行化

源是一个运行 OLTP 工作负载的 Amazon RDS for MySQL 实例,仅有 4-8 个并发提交的应用程序线程。副本为 db.r6g.4xlarge 实例,replica_parallel_workers=16 且 replica_parallel_type=LOGICAL_CLOCK。尽管有 16 个工作线程,ReplicaLag 仍稳定在 600 秒左右,副本 CPU 利用率仅约 20%,工作线程似乎处于空闲状态。

首先,使用 性能模式 中工作线程分布查询,检查事务在各工作线程间的分配是否均匀。

+--------------+--------------------------+------------------------+-----------------------------------+ | channel_name | replica_worker_thread_id | number_of_trx_executed | percent_of_trx_executed_by_worker | +--------------+--------------------------+------------------------+-----------------------------------+ | | 45 | 1903556 | 96.41 | | | 46 | 23104 | 1.17 | | | 47 | 18995 | 0.96 | | | 48 | 16720 | 0.85 | . . +--------------+--------------------------+------------------------+-----------------------------------+

上述输出显示了 16 个工作线程中的前 4 个。其中一个工作线程上应用了超过 96% 的事务,而其余 15 个工作线程处于几乎空闲的状态(其余每个工作线程的执行占比均低于 0.05%)。应用过程实际上是串行执行的。

接下来,查看错误日志中的多线程副本统计数据,确认这一情况:

2026-05-10 15:22:11.110432 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 120; events assigned = 2014500; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 98712340000 waited (count) when Workers occupied = 50122 waited when Workers occupied = 1209847000
  • 时钟冲突时的等待时间 = 98712340000 纳秒,约占 120 秒中的 99 秒。在该时间间隔中,协调器在约 82% 的时间无法调度下一个事务,原因是该事务依赖于仍在应用中的事务。

  • 工作线程占用时的等待 = 1209847000 纳秒,即大约 1.2 秒。工作线程几乎始终处于空闲状态,因此增加工作线程数量无济于事。

这表明问题在于依赖项,而非容量不足。检查源上的依赖项跟踪方法:

SELECT @@global.binlog_transaction_dependency_tracking;
+-------------------------------------------------+ | @@global.binlog_transaction_dependency_tracking | +-------------------------------------------------+ | COMMIT_ORDER | +-------------------------------------------------+

根本原因:使用 COMMIT_ORDER 时,源根据在相同二进制日志组提交中一起提交的事务,来决定哪些事务可以并行运行。在这种低并发的工作负载中,每次只有少数几个线程同时提交,因此分组提交的规模非常小。因此,对于几乎每个事务,源都使用等于前一个事务的 sequence_number 的 last_committed 值进行标记,从而将其标记为依赖于前一个事务。然后,副本必须挨个应用它们,即使它们修改的是完全不相关的行,这就是为什么 16 个工作线程中有 15 个处于空闲状态。

操作:将源切换为行级依赖项跟踪,这与提交时间无关。将其设置为动态并持久化到集群(或数据库)参数组中:

SET GLOBAL binlog_transaction_dependency_tracking = WRITESET;

WRITESET 计算每个事务修改过的行的哈希值,因此涉及不同行的事务将获得独立的时间戳并且可并行应用,而无论其何时提交。确保所有表都具有主键,因为 WRITESET 会为没有主键的表生成空的写入集(请参阅 SQL 线程滞后故障排除)。在 MySQL 8.4 中,WRITESET 是默认值,此参数已不存在。

验证:更改后,工作线程分布查询显示工作均匀分配给所有工作线程(以下显示 16 个工作线程中的前 4 个;每个工作线程现在处理约 6% 的事务):

+--------------+--------------------------+------------------------+-----------------------------------+ | channel_name | replica_worker_thread_id | number_of_trx_executed | percent_of_trx_executed_by_worker | +--------------+--------------------------+------------------------+-----------------------------------+ | | 45 | 132540 | 6.30 | | | 46 | 125610 | 5.97 | | | 47 | 129774 | 6.17 | | | 48 | 122901 | 5.84 | . . +--------------+--------------------------+------------------------+-----------------------------------+

并且错误日志统计数据显示时钟冲突数量骤降,同时 ReplicaLag 降至接近零:

2026-05-10 15:41:55.204713 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 120; events assigned = 19874100; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 412300000 waited (count) when Workers occupied = 88122 waited when Workers occupied = 7019847000

“时钟冲突时的等待时间”从约 99 秒减少到约 0.4 秒,吞吐量提升了约 10 倍,瓶颈也从依赖项变为工作线程容量,此时可参照示例 1 调整工作线程数量。

示例 3:缺少主键,强制对副本进行全表扫描

ReplicaLag 在夜间批处理作业期间出现峰值,随后恢复正常,该作业运行大型 UPDATE 和 DELETE 语句。峰值期间,副本 CPU 处于中等水平,但有一个工作线程出现卡住的情况。

在副本上,运行 SHOW PROCESSLIST 并检查复制工作线程。某个工作线程在同一条语句上持续处于 Updating 状态,每次长达数秒:

+----+-------------+------+---------+----------+------+ | Id | User | db | Command | State | Time | +----+-------------+------+---------+----------+------+ | 12 | system user | app | Connect | Updating | 38 | +----+-------------+------+---------+----------+------+

使用 SQL 线程滞后故障排除 中的检测查询,检查哪些复制表缺少主键:

+--------------+----------------+ | table_schema | table_name | +--------------+----------------+ | app | events_archive | +--------------+----------------+

根本原因:使用基于行的二进制日志记录时,UPDATE 或 DELETE 事件中的每一行都必须先在副本上定位,然后才能应用。如果表没有主键或 NOT NULL 的唯一键,则副本会对每个受影响的行执行全表扫描。在数百万行的表上,批处理操作会转换为数百万次全表扫描,应用该操作的工作线程将陷入停滞,既阻碍了应用进度,又加剧了延迟。

操作:向表添加主键。如果您无法立即定义明确的主键,请将 sql_generate_invisible_primary_key 参数设置为 ON(适用于基于 MySQL 8.0.30 及更高版本的 Aurora MySQL 3 版本),以启用生成的不可见主键(GIPK),从而使新表自动获得主键(请参阅 SQL 线程滞后故障排除)。

验证:添加主键后,工作线程不再滞留于 Updating 状态,SQL 线程应用速率恢复正常,ReplicaLag 在下一个批处理窗口期间恢复至基线水平。

示例 4:无法并行处理单个大型事务

ReplicaLag 每天在可预测的时间急剧跳升,持续数分钟后回落至接近零。WRITESET 依赖项跟踪已启用、replica_parallel_workers=16 且所有表都有主键,因此前面的示例不适用此情况。

在峰值期间,工作线程分布查询显示一个工作线程处于忙碌状态,其余均处于空闲状态,但与示例 2 不同,错误日志中既未显示高时钟冲突,也未显示工作线程占用等待:

2026-05-10 02:13:40.551922 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel '': seconds elapsed = 95; events assigned = 4120000; worker queues filled over overrun level = 0; waited due a Worker queue full = 0; waited due the total size = 0; waited at clock conflicts = 1530000 waited (count) when Workers occupied = 980 waited when Workers occupied = 210440000

依赖项等待和工作线程占用等待均处于较低水平,但只有一个工作线程处于活动状态。这排除了依赖项瓶颈(示例 2)和工作线程容量瓶颈(示例 1)。

检查 performance_schema.replication_applier_status_by_worker 中忙碌的工作线程,或运行 SHOW PROCESSLIST。在整个峰值期间,有一个工作线程在应用一个事务。在源上,应用作业将一条大型语句(例如,影响数百万行的 DELETE FROM orders WHERE created_at < '2025-01-01')作为单个事务运行。

根本原因:MTR 跨多个独立事务并行处理;它无法将单个事务拆分给多个工作线程。大型事务只能由一个工作线程应用,因此增加工作线程、切换到 WRITESET 或添加主键都无济于事。有关更多信息,请参阅 多线程复制(MTR)。

操作:在源上将较大的操作拆分为较小的批次(例如,每个事务分批删除几千行,批次之间短暂暂停)。较小的事务独立提交,这样副本可以将其分散到多个工作线程中。尽可能在低流量时段安排批量作业。

验证:对作业进行分批处理后,每日 ReplicaLag 峰值趋于平缓,工作线程分布查询显示批次分散到多个工作线程,而非仅由一个工作线程处理。

监控最佳实践

  1. 使用适当的阈值,为 ReplicaLag 指标配置 CloudWatch 警报。

  2. 与 Seconds_Behind_Source 相比,使用检测信号表可更准确地测量延迟。例如,使用 pt-heartbeat(需要单独安装,请参阅 Percona 网站上的 Percona Toolkit)。

  3. 监控源上的 SumBinaryLogSize CloudWatch 指标,以跟踪二进制日志的生成速率。

  4. 为错误日志启用 CloudWatch Logs 日志导出,以保留多线程副本统计数据。

  5. 对源和副本上的 CPU、内存和 I/O 利用率使用增强监控。

  6. 使用 Amazon CloudWatch 数据库洞察识别导致资源争用最严重的等待事件和查询。性能详情将于 2026 年 7 月 31 日终止服务;此后,性能详情控制台将重定向到 CloudWatch 数据库洞察。请选择适合您需求的数据库洞察模式:标准模式保留了核心监控体验和定价,而高级模式则新增了实例集级别的监控、锁定诊断和执行计划捕获功能。有关更多信息,请参阅 在 Amazon Aurora 上使用 Amazon CloudWatch 数据库洞察监控数据库负载。

尽可能降低复制滞后的最佳实践

以下建议总结了用以尽可能降低复制滞后的关键措施。有关每个主题的详细指导,请参阅交叉引用链接。

  1. 确保所有表都有主键:没有主键时,对于 UPDATE 和 DELETE 操作中每个修改过的行,副本将执行全表扫描。有关更多信息,请参阅 SQL 线程滞后故障排除。

  2. 使用 WRITESET 启用多线程复制:并行处理独立事务的 SQL 应用。有关配置详细信息,请参阅 多线程复制(MTR)。

  3. 使用至少与源实例相同的实例类:为复制提供充足的 CPU、内存和网络资源。有关更多信息,请参阅 针对 I/O 线程滞后的优化策略。

  4. 保持较小的事务大小:大型事务会降低并行度并增加历史列表长度。有关更多信息,请参阅 SQL 线程滞后故障排除。

  5. 禁用副本上的二进制日志记录:除非需要下游复制,否则请设置 binlog_format=OFF。有关参数的详细信息,请参阅 MTR 配置。

  6. 避免在副本上执行长时间运行的事务和查询:长时间运行的读取事务会阻止清除历史列表,从而降低复制性能。

  7. 启用基于 GTID 的复制:提供自动位置跟踪并启用 Aurora 内存中继日志(Aurora MySQL 3.10+)。有关更多信息,请参阅 Aurora 特有的复制优化。