如何排查 AWS DMS 任务失败并显示错误消息“错误:由于语句超时取消语句”的问题?

上次更新时间:2019 年 9 月 30 日

我使用 AWS Database Migration Service (AWS DMS) 在我的本地 PostgreSQL 数据库中来回迁移数据。AWS DMS 任务正常运行一段时间,然后任务失败,出现错误。如何排查并解决这些错误?

简短描述

如果 PostgreSQL 数据库是您的迁移任务来源,则 AWS DMS 会在完整加载期间从表中获取数据。然后,AWS DMS 会从预写日志 (WAL) 中读取数据,该日志在更改数据捕获 (CDC) 期间由复制槽保留。

如果 PostgreSQL 数据库是您的迁移任务目标,则 AWS DMS 会从来源中获取数据并在复制实例中创建 CSV 文件。然后,AWS DMS 执行 COPY 命令以在完整加载期间将这些记录插入到目标中。然而,在 CDC 期间,AWS DMS 在事务应用模式下从源 WAL 日志中执行准确的 DML 语句。对于批量应用模式,AWS DMS 还会在 CDC 期间创建 CSV 文件并执行 COPY 命令以将净更改插入目标中。

当 AWS DMS 执行命令以从来源获取数据或将数据放入目标中时,AWS DMS 使用 60 秒的默认超时设置。如果源或目标的负载太大或者表格中有锁定项,则 AWS DMS 无法在 60 秒内完成这些命令的执行。因此,任务失败,并显示错误“由于语句超时取消语句”,且您会在日志中看到以下条目之一:

消息
]E:  RetCode: SQL_ERROR  SqlState: 57014 NativeError: 1 消息:错误:由于语句超时取消语句;

要排查并解决这些错误,请执行以下步骤:

  • 查明命令执行时间长的原因。
  • 增加超时值并检查槽创建超时值。
  • 排查槽创建问题。

解决方法

查明命令执行时间长的原因

要查找超时期间未能执行的命令,请查看 AWS DMS 任务日志任务的表统计部分。如果参数 log_min_error_statement 被设置为 ERROR 或较低的严重程度,您还可以在 PostgreSQL 错误日志文件中查找此信息。识别失败的命令后,您可以找到失败的表名称。从 PostgreSQL 错误日志中查看下面的示例错误消息:

ERROR: canceling statement due to statement timeout 
STATEMENT: <The statement executed>"

要查找相关表上的锁定项,请在源或目标中执行以下命令(具体取决于错误显示的位置):

SELECT blocked_locks.pid     AS blocked_pid,
         blocked_activity.usename  AS blocked_user,
         blocking_locks.pid     AS blocking_pid,
         blocking_activity.usename AS blocking_user, 
         blocked_activity.query    AS blocked_statement,
         blocking_activity.query   AS current_statement_in_blocking_process
   FROM  pg_catalog.pg_locks         blocked_locks 
    JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
    JOIN pg_catalog.pg_locks         blocking_locks 
        ON blocking_locks.locktype = blocked_locks.locktype 
        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
        AND blocking_locks.pid != blocked_locks.pid 
    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
   WHERE NOT blocked_locks.GRANTED;

如果您发现任何被阻止的 PID,请通过运行以下命令停止或“消除”被阻止的 PID:

SELECT pg_terminate_backend(blocking_pid); 

由于死行或“元组”可以提高 SELECT 时间,通过运行以下命令检查源表中有无大量死行:

select * from pg_stat_user_tables where relname= 'table_name';

检查失败的目标表中是否有主键或唯一索引。如果没有主键或唯一索引,将导致在执行任何 UPDATE 语句时都会进行全表扫描,这需要花费很长时间。

提高超时值

AWS DMS 在源和目标终端节点中均使用 executeTimeout 额外连接属性。executeTimeout 的默认值为 60 秒,因此,如果查询执行时间超过 60 秒,AWS DMS 会超时。

如果 Source_Unload Source_Capture 中出现错误,则在源中设置 executeTimeout 的超时值。如果 Target_LoadTarget_Apply 中出现错误,则在目标中设置 executeTimeout 的超时值。遵照下述步骤提高超时值设置:

1.    打开 AWS DMS 控制台

2.    从导航窗格中选择终端节点

3.    选择 PostgreSQL 终端节点。

4.    选择操作,然后选择修改

5.    展开特定于终端节点的设置部分。

6.    在额外连接属性的字段中,输入以下值:

executeTimeout=3600;

7.    选择保存

8.    从终端节点窗格中,选择您的 PostgreSQL 终端节点名称。

9.    从连接部分中,终端节点的状态将从测试更改为成功

您可以提高 PostgreSQL 数据库实例中的 statement_timeout 参数(以毫秒为单位)。默认值为 0,这将禁用任何查询的超时。您还可以提高 lock_timeout 参数。默认值为 0,这将禁用锁定的超时。

排查槽创建问题

如果您在 PostgreSQL 数据库中创建复制槽时发生超时,您将看到类似于以下内容的日志条目:

消息
]E:  wal_slot_create(...) - 无法创建插槽 'xxxxxxxxxxxxxxxx_00016391_c4a70947_84c9_4a55_8d54_ff63f2f69a52' (on execute(...) phase) [1020101]  (postgres_endpoint_wal_utils.c:3215)

如果您的复制实例运行 3.1.3 或更低版本,将对此命令应用 60 秒的默认超时设置,从而覆盖 executeTimeout 的值。要解决此问题,请使用此命令的默认超时在其中为 600 秒的版本 3.1.4。您可以通过在任务设置部分中配置 TransactionConsistencyTimeout 参数来提高此超时。

如果数据库用户表中存在任何活动锁定,PostgreSQL 将无法创建复制插槽。通过执行以下命令检查有无锁定:

select * from pg_locks;

然后,为了测试错误是否已解决,请运行以下命令以在源 PostgreSQL 数据库中手动创建复制槽:

select  xlog_position FROM pg_create_logical_replication_slot('<Slot name as per
    the task log>', 'test_decoding');

如果该命令仍然无法创建槽,则您可能需要使用 PostgreSQL DBA 来确定瓶颈和配置数据库。如果命令成功,则删除您刚刚创建作为测试的插槽:

select pg_drop_replication_slot(‘<slot name>');

最后,重新开始迁移任务。