如何确定导致 Amazon RDS for PostgreSQL 上出现“设备上无剩余空间”或“DiskFull”错误的原因?

上次更新时间:2019 年 4 月 16 日

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

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

如何解决 DiskFull 错误?如何防止存储已满问题?

简短描述

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

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

解决方法

使用 Amazon CloudWatch 通过 FreeStorageSpace 指标监控您的数据库存储空间。通过为可用存储空间设置 Amazon CloudWatch 警报,您可以在空间开始减少时收到通知。如果您收到了警报,可以查看此前的存储问题可能原因。我们还建议您监控 log_temp_files 中的日志以了解创建临时文件的时间和方式。如果您的数据库实例消耗的存储量仍超过预期,请检查以下各项:

  • 数据库日志文件的大小
  • 存在临时文件
  • 事务日志磁盘使用量不断增加
  • 跨区域只读副本
  • 膨胀或不当地删除死行
  • 存在孤立文件

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

默认情况下,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_files temp_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。复制槽将文件一直保留到这些文件被使用者从外部使用,例如,通过 pg_recvlogical;提取、转换和加载 (ETL) 任务;或 AWS DMS。

如果您将 rds.logical_replication 参数值设置为 1,则 AWS DMS 设置 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 and v11

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
----------------------------------------------------------------+--------------------+--------
xc36ufspjql35djp_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

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

您可以通过执行与以下内容类似的命令删除该查询:

psql=> select pg_drop_replication_slot('xc36ufspjql35djp_00013322_907c1e0a_9f8b_4c13_89ea_ef0ea1cf143d');

跨区域只读副本

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

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

postgres=# select * from pg_replication_slots;

除了副本滞后 CloudWatch 指标和事件,您可以使用源实例上的 pg_stat_replication 视图来检查相关复制的统计信息。有关详细信息,请参阅关于 pg_stat_replication 的 PostgreSQL 文档。

膨胀或不当地删除死行(元组)

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

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

VACUUM 操作可以回收被死元组占用的存储。最佳做法是对频繁更新的表执行定期 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.    记下结果。差异应非常明显。

如果文件占用的数据库大小与通过对象求和检索到的数据库大小之间存在明显的差异,则可能有孤立文件正在占用存储空间。


这篇文章对您有帮助吗?

我们可以改进什么?


需要更多帮助吗?