我为什么会在 Amazon RDS for PostgreSQL 上收到“设备上无剩余空间”或“DiskFull”错误?

上次更新日期:2022 年 6 月 23 日

我有一个小型的 Amazon Relational Database Service (Amazon RDS) for PostgreSQL 数据库。该实例的可用存储空间逐渐减少,我收到以下错误:

"错误消息:PG::DiskFull:错误:无法扩展文件"base/16394/5139755":设备上无剩余空间。提示:检查可用磁盘空间。"

我想解决 DiskFull 错误并防止存储问题。

简短描述

Amazon RDS 数据库实例存储用于以下各项:

  • PostgreSQL 事务创建的临时表或文件
  • 数据文件
  • 预写日志(WAL 日志)
  • 复制槽
  • 保留时间过长的数据库日志(错误文件)
  • 支持 RDS 数据库实例的一致状态的其他数据库或 Linux 文件

解决方法

1.    通过 FreeStorageSpace 指标使用 Amazon CloudWatch 监控您的数据库存储空间。为可用存储空间设置 Amazon CloudWatch 告警时,您将会在空间开始减少时收到通知。如果您收到了告警,请查看此前提到的存储问题的原因。

2.    如果您的数据库实例消耗的存储量仍超过预期,请检查以下各项:

  • 数据库日志文件的大小
  • 存在临时文件
  • 事务日志磁盘使用量不断增加
  • 复制槽:
    • 只有当跨区域只读副本或同区域只读副本在 PostgreSQL 14.1 及更高版本上运行时,才会创建物理复制槽
    • 逻辑复制槽是针对副本或订阅者创建的
  • 膨胀或不当地删除死行
  • 存在孤立文件

3.    当您的工作负载可预测时,请为您的实例启用存储自动扩展。启用存储自动扩展之后,当 Amazon RDS 检测到您的可用数据库空间不足时,您的存储将自动扩展。当以下因素适用时,Amazon RDS 会开始对启用自动扩展的数据库实例进行存储修改:

  • 可用空间小于已分配存储空间的 10%。
  • 低存储条件持续至少五分钟。
  • 自上次修改存储空间或实例的存储优化已完成至少 6 个小时(以较长者为准)。

可以通过设置最大存储阈值来设置自动扩展数据库实例的限制。有关更多信息,请参阅使用 Amazon RDS 存储自动扩展功能自动管理容量

检查数据库日志文件的大小

默认情况下,Amazon RDS for PostgreSQL 错误日志文件的保留时间值为 4320 分钟(三天)。大型日志文件可以因更高的工作负载或过多的日志记录而使用更多空间。您可以使用与您的数据库实例关联的数据库参数组中的 rds.log_retention_period 参数更改系统日志的保留期限。例如,如果您将该值设置为 1440,则日志将保留一天。有关详细信息,请参阅 PostgreSQL 数据库日志文件

此外,您还可以更改数据库参数组中的错误报告和日志记录参数,以减少过多的日志记录。这反过来会减小日志文件的大小。有关更多信息,请参阅错误报告和日志记录

检查临时文件

临时文件是指每个后端或会话连接存储的文件。这些文件用作资源池。通过运行以下类似的命令来查看临时文件统计信息:

psql=> SELECT datname, temp_files AS "Temporary files",temp_bytes AS "Size of temporary files" FROM pg_stat_database ;

重要提示:视图 pg_stat_database 中的 temp_filestemp_bytes 列收集聚合中的统计信息(累积值)。这是设计使然,因为这些计数器只会在服务器启动时通过恢复重置。也就是说,计数器会在立即关闭、服务器崩溃或时间点故障恢复 (PITR) 之后重置。因此,最佳实践是监控这些文件的数量和大小的增长,而不是仅查看输出。

为排序、哈希或临时查询结果创建临时文件。要跟踪临时表或文件的创建,在自定义参数组中设置 log_temp_files。此参数控制临时文件名称和大小的日志记录活动。如果您将 log_temp_files 值设置为 0,则将记录所有临时文件信息。如果您将参数设置为正值,则只记录等于或大于指定千字节数的文件。默认设置为 -1,表示禁用临时文件的日志记录。

您还可以对查询使用 EXPLAIN ANALYZE 以查看磁盘排序。在查看日志输出时,可以看到您的查询所创建的临时文件的大小。有关详细信息,请参阅关于监控数据库活动的 PostgreSQL 文档。

检查事务日志磁盘使用量的不断增加

CloudWatch 的 TransactionLogsDiskUsage 指标代表事务 WAL 所用的磁盘空间。以下情况可能会导致事务日志磁盘使用量增加:

  • 高数据库负荷(生成额外的 WAL 的写入和更新)
  • 流式传输支付副本卡滞(副本位于相同区域中)或只读副本处于存储已满状态
  • 复制槽

复制槽可作为 AWS Database Migration Service (AWS DMS) 的逻辑解码功能的一部分创建。对于逻辑复制,槽参数 rds.logical_replication 设置为 1。复制槽会保留 WAL 文件,直到这些文件被使用者从外部使用。例如,它们可能被 pg_recvlogical;提取、转换和加载 (ETL) 任务或 AWS DMS 使用。

如果您将 rds.logical_replication 参数值设置为 1,则 AWS RDS 设置 wal_levelmax_wal_sendersmax_replication_slotsmax_connections 参数。更改这些参数可能会增加 WAL 生成。最好的做法是仅当您使用逻辑槽时设置 rds.logical_replication 参数。如果此参数设置为 1 且存在逻辑复制槽,但是该复制槽保留的 WAL 文件没有使用者,则事务日志磁盘使用量可能会增加。这也会导致可用存储空间持续减少。

运行此查询以确认复制槽的状态和大小:

PostgreSQL v9:

psql=> SELECT slot_name, pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_location(),restart_lsn)) AS 
replicationSlotLag, active FROM pg_replication_slots ;

PostgreSQL v10 及更高版本:

psql=> SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),restart_lsn)) AS replicationSlotLag, 
active FROM pg_replication_slots ;

在确定目前使用的复制槽(有效状态为 False)后,可以通过运行以下查询删除该复制槽:

psql=> SELECT pg_drop_replication_slot('Your_slotname_name');

注意:如果 AWS DMS 任务是使用者且不再需要,则可以删除该任务并手动删除复制槽。

示例输出:

slot_name                                                      | replicationslotlag | active
---------------------------------------------------------------+--------------------+--------
xc36ujql35djp_00013322_907c1e0a_9f8b_4c13_89ea_ef0ea1cf143d    | 129 GB             | f
7pajuy7htthd7sqn_00013322_a27bcebf_7d0f_4124_b336_92d0fb9f5130 | 704 MB             | t
zp2tkfo4ejw3dtlw_00013322_03e77862_689d_41c5_99ba_021c8a3f851a | 624 MB             | t

在本示例中,槽名称 xc36ujql35djp_00013322_907c1e0a_9f8b_4c13_89ea_ef0ea1cf143d 的有效状态为 False。因此,该槽未被主动使用,且该槽对 129 GB 的事务文件大小有贡献。

通过运行以下命令来删除查询:

psql=> SELECT pg_drop_replication_slot('xc36ujql35djp_00013322_907c1e0a_9f8b_4c13_89ea_ef0ea1cf143d');

检查跨区域只读副本的状态

使用跨区域只读副本时,在主实例上创建一个物理复制槽。如果跨区域只读副本失败,则主数据库实例上的存储空间可能会受影响。这是因为 WAL 文件未复制到只读副本。您可以使用 CloudWatch 指标“最旧复制槽滞后”和“事务日志磁盘使用量”来确定最滞后副本滞后了多少。此外,您还可以看到用于 WAL 数据的存储量。

要检查跨区域只读副本的状态,请使用查询 pg_replication_slots。有关详细信息,请参阅 pg_replication_slots 的 PostgreSQL 文档。如果有效状态为 false,则该槽目前未用于复制。

psql=> SELECT * FROM pg_replication_slots;

此外,您可以使用源实例上的查看 pg_stat_replication 来检查相关复制的统计信息。有关详细信息,请参阅关于 pg_stat_replication 的 PostgreSQL 文档。

检查膨胀或不当删除的死行(元组)

在正常 PostgreSQL 操作中,通过 UPDATE 删除或设置为过时的元组不会从其表中删除。对于多版本并发控制 (MVCC) 实施,在执行 DELETE 操作时,不会立即从数据文件中删除行,而是通过在标头中设置 xmax field 将该行标记为已删除。更新也会先将行标记为待删除,然后执行插入操作。这样允许并发操作,使事务之间的锁定情况最少。因此,不同行版本都作为 MVCC 进程的组成部分。

如果死行不清理,它们可能保留在数据文件中,但仍对任何事务都不可见,这会影响磁盘空间。如果表中有很多 DELETEUPDATE 操作,则死元组可能会占用大量磁盘空间,此情况在 PostgreSQL 中有时称为“膨胀”。

VACUUM 操作可以释放死元组占用的存储空间以便重复使用,但这不会向文件系统释放可用存储空间。运行 VACUUM FULL 会向文件系统释放存储空间。但请注意,在 VACUUM FULL 运行期间,表上会保留专享访问锁定。此方法还需要额外的磁盘空间,因为它会写入表的新副本,并且在操作完成之前不会释放旧副本。最佳做法是仅在必须从表中回收大量空间时才使用此方法。另一个最佳做法是对频繁更新的表执行定期 vacuum 或 autovacuum 操作。有关详细信息,请参阅关于 VACUUM 的 PostgreSQL 文档。

要检查死元组的估计数量,请使用 pg_stat_all_tables 视图。有关详细信息,请参阅关于 pg_stat_all_tables 视图的 PostgreSQL 文档。在本示例中,有 1999952 个死元组 (n_dead_tup):

psql => SELECT * FROM pg_stat_all_tables WHERE relname='test';

-[ RECORD 1 ]-------+------------------------------
relid               | 16395
schemaname          | public
relname             | test
seq_scan            | 3
seq_tup_read        | 5280041
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 2000000
n_tup_upd           | 0
n_tup_del           | 3639911
n_tup_hot_upd       | 0
n_live_tup          | 1635941
n_dead_tup          | 1999952
n_mod_since_analyze | 3999952
last_vacuum         | 
last_autovacuum     | 2018-08-16 04:49:52.399546+00
last_analyze        | 2018-08-09 09:44:56.208889+00
last_autoanalyze    | 2018-08-16 04:50:22.581935+00
vacuum_count        | 0
autovacuum_count    | 1
analyze_count       | 1
autoanalyze_count   | 1


psql => VACUUM TEST;

检查孤立文件

当文件存在于数据库目录中却没有对象指向这些文件时,可能会出现孤立文件。如果实例的存储空间不足或者引擎在 ALTER TABLEVACUUM FULLCLUSTER 之类的操作中崩溃,则可能会发生此情况。要检查有无孤立文件,请执行下面的步骤:

1.    在每个数据库中登录 PostgreSQL。

2.    执行以下查询以评估已使用的大小和实际大小。

# Size of the database occupied by files
psql=> SELECT pg_size_pretty(pg_database_size('DATABASE_NAME')); 

# Size of database retrieved by summing the objects (real size)
psql=> SELECT pg_size_pretty(SUM(pg_relation_size(oid))) FROM pg_class;

3.    记下结果。如果差异很大,则孤立文件可能正在占用存储空间。


这篇文章对您有帮助吗?


您是否需要账单或技术支持?