

# 在 Aurora PostgreSQL 中解决可识别的真空拦截器
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Resolving_Identifiableblockers"></a>

自动真空会执行积极真空操作，并将事务 ID 的龄期降低到低于由 RDS 实例的 `autovacuum_freeze_max_age` 参数指定的阈值。您可以使用 Amazon CloudWatch 指标 `MaximumUsedTransactionIDs` 跟踪此龄期。

要查找 Amazon RDS 实例的 `autovacuum_freeze_max_age` 设置（默认为 2 亿个事务 ID），您可以使用以下查询：

```
SELECT
    TO_CHAR(setting::bigint, 'FM9,999,999,999') autovacuum_freeze_max_age
FROM
    pg_settings
WHERE
    name = 'autovacuum_freeze_max_age';
```

请注意，`postgres_get_av_diag()` 仅在龄期超过 Amazon RDS 的 5 亿个事务 ID 的[自适应自动真空](Appendix.PostgreSQL.CommonDBATasks.Autovacuum.md#Appendix.PostgreSQL.CommonDBATasks.Autovacuum.AdaptiveAutoVacuuming)阈值时检查积极真空拦截器。要使 `postgres_get_av_diag()` 检测拦截器，拦截器必须至少有 5 亿个事务。

`postgres_get_av_diag()` 函数可识别以下类型的拦截器：

**Topics**
+ [活动语句](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Active_statement)
+ [事务中空闲](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Idle_in_transaction)
+ [准备好的事务](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Prepared_transaction)
+ [逻辑复制槽](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Logical_replication_slot)
+ [读取器实例](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Reader_instances)
+ [临时表](#Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Temporary_tables)

## 活动语句
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Active_statement"></a>

在 PostgreSQL 中，活动语句是数据库当前正在执行的 SQL 语句。这包括查询、事务或任何正在进行的操作。通过 `pg_stat_activity` 进行监控时，状态列指示具有相应 PID 的进程处于活动状态。

`postgres_get_av_diag()` 函数在识别系活动语句的语句时会显示类似于以下内容的输出。

```
blocker               | Active statement
database              | my_database
blocker_identifier    | SELECT pg_sleep(20000);
wait_event            | Timeout:PgSleep
autovacuum_lagging_by | 568,600,871
suggestion            | Connect to database "my_database", review carefully and you may consider terminating the process using suggested_action. For more information, see Working with PostgreSQL autovacuum in the Amazon RDS User Guide.
suggested_action      | {"SELECT pg_terminate_backend (29621);"}
```

**建议采取的措施**

按照 `suggestion` 列中的指导，用户可以连接到存在活动语句的数据库，并按照 `suggested_action` 列中的指定，建议仔细查看终止会话的选项。如果终止是安全的，则可以使用 `pg_terminate_backend()` 函数终止会话。此操作可由管理员（如 RDS 主账户）或具有所需 `pg_terminate_backend()` 权限的用户执行。

**警告**  
已终止的会话将撤消 (`ROLLBACK`) 其所做的更改。根据您的要求，您可能需要重新运行相应语句。但是，建议只有在自动真空过程完成其积极真空操作之后才这样做。

## 事务中空闲
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Idle_in_transaction"></a>

事务中空闲语句是指已打开显式事务（例如通过发出 `BEGIN` 语句）、执行了一些工作，并且现在正在等待客户端传递更多工作或通过发出 `COMMIT`、`ROLLBACK` 或 `END`（这将导致隐式 `COMMIT`）来发出事务结束信号的任何会话。

`postgres_get_av_diag()` 函数在将 `idle in transaction` 语句识别为拦截器时，显示类似于以下内容的输出。

```
blocker               | idle in transaction
database              | my_database
blocker_identifier    | INSERT INTO tt SELECT * FROM tt;
wait_event            | Client:ClientRead
autovacuum_lagging_by | 1,237,201,759
suggestion            | Connect to database "my_database", review carefully and you may consider terminating the process using suggested_action. For more information, see Working with PostgreSQL autovacuum in the Amazon RDS User Guide.
suggested_action      | {"SELECT pg_terminate_backend (28438);"}
```

**建议采取的措施**

如 `suggestion` 列中所示，您可以连接到存在事务中空闲会话的数据库，并使用 `pg_terminate_backend()` 函数终止会话。该用户可以是您的管理员（RDS 主账户）用户或具有 `pg_terminate_backend()` 权限的用户。

**警告**  
已终止的会话将撤消 (`ROLLBACK`) 其所做的更改。根据您的要求，您可能需要重新运行相应语句。但是，建议只有在自动真空过程完成其积极真空操作之后才这样做。

## 准备好的事务
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Prepared_transaction"></a>

PostgreSQL 允许属于称为[准备好的事务](https://www.postgresql.org/docs/current/sql-prepare-transaction.html)的两阶段提交策略一部分的事务。通过将 `max_prepared_transactions` 参数设置为非零值即可启用这些功能。准备好的事务旨在确保事务的持久性，即使在数据库崩溃、重启或客户端断开连接后仍可用。与定期事务一样，它们会被分配一个事务 ID，并且可能会影响自动真空。如果保持已准备状态，则自动真空将无法执行冻结，这样可能会导致事务 ID 重叠。

当事务被无限期地准备而没有被事务管理器解决时，它们将成为孤立的已准备事务。解决此问题的唯一方法是分别使用 `COMMIT PREPARED` 或 `ROLLBACK PREPARED` 命令提交或回滚事务。

**注意**  
请注意，在准备事务期间进行的备份在还原后仍将包含该事务。有关如何查找和关闭此类事务的信息，请参阅以下信息。

`postgres_get_av_diag()` 函数在识别出系已准备事务的拦截器时显示以下输出。

```
blocker               | Prepared transaction
database              | my_database
blocker_identifier    | myptx
wait_event            | Not applicable
autovacuum_lagging_by | 1,805,802,632
suggestion            | Connect to database "my_database" and consider either COMMIT or ROLLBACK the prepared transaction using suggested_action. For more information, see Working with PostgreSQL autovacuum in the Amazon RDS User Guide.
suggested_action      | {"COMMIT PREPARED 'myptx';",[OR],"ROLLBACK PREPARED 'myptx';"}
```

**建议采取的措施**

如建议列中所述，连接到已准备事务所在的数据库。根据 `suggested_action` 列，仔细查看是执行 `COMMIT` 还是 `ROLLBACK`，并执行适当的操作。

为了总体上监控已准备事务，PostgreSQL 提供了一个名为 `pg_prepared_xacts` 的目录视图。您可以使用以下查询来查找已准备事务。

```
SELECT
    gid,
    prepared,
    owner,
    database,
    transaction AS oldest_xmin
FROM
    pg_prepared_xacts
ORDER BY
    age(transaction) DESC;
```

## 逻辑复制槽
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Logical_replication_slot"></a>

复制槽的用途是保存未使用的更改，直到它们被复制到目标服务器。有关更多信息，请参阅 PostgreSQL 的 [Logical replication](https://www.postgresql.org/docs/current/logical-replication.html)。

有两种类型的逻辑复制槽。

**非活动逻辑复制槽**

复制终止后，无法删除未使用的事务日志，且复制槽变为非活动状态。尽管订阅用户当前未使用非活动逻辑复制槽，但它仍保留在服务器上，从而保留 WAL 文件并防止删除旧的事务日志。这样会增加磁盘使用量，特别是阻止自动真空清理内部目录表，因为系统必须保护 LSN 信息不被覆盖。如果不加以解决，可能会导致目录臃肿、性能下降和重叠真空风险增加，从而可能导致事务停机。

**活动但速度较慢的逻辑复制槽**

有时，由于逻辑复制的性能下降，目录中无效元组的删除会延迟。这种复制延迟会减慢 `catalog_xmin` 的更新速度，并可能导致目录臃肿和重叠真空。

`postgres_get_av_diag()` 函数在找到逻辑复制槽作为拦截器时，会显示类似于以下内容的输出。

```
blocker               | Logical replication slot
database              | my_database
blocker_identifier    | slot1
wait_event            | Not applicable
autovacuum_lagging_by | 1,940,103,068
suggestion            | Ensure replication is active and resolve any lag for the slot if active. If inactive, consider dropping it using the command in suggested_action. For more information, see Working with PostgreSQL autovacuum in the Amazon RDS User Guide.
suggested_action      | {"SELECT pg_drop_replication_slot('slot1') FROM pg_replication_slots WHERE active = 'f';"}
```

**建议采取的措施**

要解决此问题，请检查复制配置中是否存在可能终止应用进程的目标架构或数据问题。最常见的原因如下：
+ 缺少列
+ 数据类型不兼容
+ 数据不匹配
+ 缺少表

如果该问题与基础设施问题有关：
+ 网络问题 – [如何解决 Amazon RDS 数据库处于不兼容网络状态的问题？](https://repost.aws/knowledge-center/rds-incompatible-network)。
+ 数据库或数据库实例不可用的原因如下：
  + 副本实例用尽存储空间 – 有关添加存储的信息，请查看 [Amazon RDS DB 实例存储空间不足](https://repost.aws/knowledge-center/rds-out-of-storage)。
  + 不兼容参数 – 有关如何能够解决该问题的更多信息，请查看[如何修复一直处于不兼容参数状态的 Amazon RDS 数据库实例问题？](https://repost.aws/knowledge-center/rds-incompatible-parameters)。

如果您的实例位于 AWS 网络之外或 AWS EC2 上，请咨询您的管理员，了解如何解决可用性或基础设施相关问题。

**删除非活动槽**

**警告**  
注意：在删除复制槽之前，请仔细确保其没有正在进行的复制、处于非活动状态且处于不可恢复状态。过早删除槽可能会中断复制或导致数据丢失。

确认不再需要复制槽后，将其删除以允许自动真空继续运行。条件 `active = 'f'` 可确保仅删除非活动槽。

```
SELECT pg_drop_replication_slot('slot1') WHERE active ='f'
```

## 读取器实例
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Reader_instances"></a>

在启用 hot\$1standby\$1feedback 设置之后，对于读取器实例上正在运行的查询仍然需要的死行，该设置会阻止写入器实例上的 autovacuum 删除这些死行。此行为是必需的，因为在读取器实例（同样适用于 Aurora Global Database 中的读取器实例）上运行的查询要求这些行在写入器实例上保持可用，从而防止出现查询冲突和查询取消。

**注意**  
`hot_standby_feedback` 在 Aurora PostgreSQL 中默认启用且不可修改。

`postgres_get_av_diag()` 函数在找到以物理复制槽作为拦截器的只读副本时，会显示类似于以下内容的输出。

```
blocker               | Oldest query running on aurora reader
database              | Not applicable
blocker_identifier    | my-aurora-reader-2
wait_event            | Not applicable
autovacuum_lagging_by | 540,122,859
suggestion            | Run the following query on the reader "my-aurora-reader-2" to find the long running query:                                                                                                 
                      | SELECT * FROM pg_catalog.pg_stat_activity WHERE backend_xmin::text::bigint = 523476310;                                                                                                    
                      | Review carefully and you may consider terminating the query on reader using suggested_action.                                                      
suggested_action      | {"SELECT pg_terminate_backend(pid) FROM pg_catalog.pg_stat_activity WHERE backend_xmin::text::bigint = 523476310;","                                                                       
                      | [OR]                                                                                                                                                                                    
                      | ","Delete the reader if not needed"}
```

按照 `suggested_action` 列中的建议，仔细查看用于解除阻止自动真空的这些选项。
+ **终止查询** – 按照建议列中的指导，您可以连接到只读副本，如 suggested\$1action 列中指定，建议仔细查看终止会话的选项。如果终止被视为是安全的，则可以使用 `pg_terminate_backend()` 函数终止会话。此操作可由管理员（如 RDS 主账户）或具有所需 pg\$1terminate\$1backend() 权限的用户执行。

  您可以在只读副本上运行以下 SQL 命令来终止用于阻止主数据库上的真空清理旧行的查询。在函数的输出中报告 `backend_xmin` 的值：

  ```
  SELECT
      pg_terminate_backend(pid)
  FROM
      pg_catalog.pg_stat_activity
  WHERE
      backend_xmin::text::bigint = backend_xmin;
  ```
+ **如果不需要读取器实例，请将其删除** – 如果不再需要读取器实例，则可以将其删除。这将消除相关的复制开销，并允许主数据库在不受实例阻碍的情况下回收事务日志。

## 临时表
<a name="Appendix.PostgreSQL.CommonDBATasks.Autovacuum_Monitoring.Temporary_tables"></a>

使用 `TEMPORARY` 关键字创建的[临时表](https://www.postgresql.org/docs/current/sql-createtable.html)驻留在临时架构中，例如 pg\$1temp\$1xxx，并且只能由创建它们的会话访问。会话结束时会删除临时表。但是，这些表对于 PostgreSQL 的自动真空流程是不可见的，必须由创建它们的会话手动清理。尝试从另一个会话中清空临时表是无效的。

在特殊情况下，存在一个临时表，但没有活动会话拥有该表。如果所属会话由于致命崩溃、网络问题或类似事件而意外结束，则可能无法清理临时表，从而会将其留作为“孤立”表。当 PostgreSQL 自动真空进程检测到孤立的临时表时，它会记录以下消息：

```
LOG: autovacuum: found orphan temp table \"%s\".\"%s\" in database \"%s\"
```

`postgres_get_av_diag()` 函数在将临时表识别为拦截器时，会显示类似于以下内容的输出。要使该函数正确显示与临时表相关的输出，需要在存在这些表的同一数据库中执行该函数。

```
blocker               | Temporary table
database              | my_database
blocker_identifier    | pg_temp_14.ttemp
wait_event            | Not applicable
autovacuum_lagging_by | 1,805,802,632
suggestion            | Connect to database "my_database". Review carefully, you may consider dropping temporary table using command in suggested_action. For more information, see Working with PostgreSQL autovacuum in the Amazon RDS User Guide.
suggested_action      | {"DROP TABLE ttemp;"}
```

**建议采取的措施**

按照输出的 `suggestion` 列中提供的说明识别并删除阻止自动真空运行的临时表。使用以下命令删除由 `postgres_get_av_diag()` 报告的临时表。根据 `postgres_get_av_diag()` 函数提供的输出替换表名称。

```
DROP TABLE my_temp_schema.my_temp_table;
```

以下查询可用于识别临时表：

```
SELECT
    oid,
    relname,
    relnamespace::regnamespace,
    age(relfrozenxid)
FROM
    pg_class
WHERE
relpersistence = 't'
ORDER BY
    age(relfrozenxid) DESC;
```