磁盘使用率高问题定位及处理方法
指标异常说明
生产数据库的磁盘要有一定的冗余,一旦磁盘使用率过高要及时处理,防止出现磁盘满导致数据库损坏等问题。
数据库提供了多项体现磁盘使用的监控指标,建议重点关注以下指标:
- 磁盘利用率:rds039_disk_util
- 磁盘总大小:rds047_disk_total_size
- 磁盘使用量:rds048_disk_used_size
- 事务日志(WAL日志)使用量:rds040_transaction_logs_usage
- 最滞后副本滞后量(因复制槽积压的WAL日志):rds045_oldest_replication_slot_lag
问题排查思路
RDS for PostgreSQL数据库中占用磁盘空间最多的可能是:数据文件(表/索引等)、WAL日志、临时文件。当磁盘使用率增长较快不符合预期时,可以按照以下思路进行排查:
排查及解决方法
查询数据库、表、WAL日志等大小的SQL会占用较多的磁盘IO,请在业务低峰期运行。
- 查看WAL日志大小是否异常并进行处理
- 查看wal日志大小
可以通过rds040_transaction_logs_usage监控指标或者以下SQL查看WAL日志大小,如果发现WAL非常多,可以通过后续步骤依次排查。
select round(sum(size)/1024/1024/1024,2) "GB" from pg_ls_waldir();
RDS for PostgreSQL 12之后的版本才有pg_ls_waldir() 函数。
需要root用户执行pg_ls_waldir函数。
- 查看WAL日志保留相关参数
- 对于RDS for PostgreSQL 12及以下版本,查看“wal_keep_segments”参数(单位MB)的值;对于12以上版本,查看“wal_keep_size”参数(单位为MB)。
- WAL日志保留参数的值不宜太大,一般设置要小于磁盘总空间的10%;也不宜太小,一般要大于4GB,否则容易导致主库将备库需要的wal日志清理,进而导致备库异常。
- 查看复制槽状态,及延迟未清理的日志大小
复制槽会阻塞WAL的回收,如果发现非活动的复制槽或者不需要的复制槽,可以根据需要进行删除。
查询slot状态、WAL日志滞后量的SQL:
select slot_name, active, pg_size_pretty(pg_wal_lsn_diff(b, a.restart_lsn)) as slot_latency from pg_replication_slots as a, pg_current_wal_lsn() as b;
删除slot命令的SQL:
select pg_drop_replication_slot('slot_name');
- 查看写业务繁忙程度
可以通过rds044_transaction_logs_generations指标查看写业务繁忙程度,该指标表示平均每秒生成的事务日志(WAL日志)大小。
如果该指标较大,说明写业务较多,数据库内核会自动预留更多的WAL日志以便回收使用,WAL日志占用的磁盘空间会增加,建议通过磁盘扩容保证一定的磁盘冗余。
- 查看wal日志大小
- 查看数据文件大小否异常并进行处理
- 查询磁盘占用前10的数据库
select datname, pg_database_size(oid)/1024/1024 as dbsize_mb from pg_database order by dbsize_mb desc limit 10;
- 查看磁盘占用前10的对象(表/索引)
可以通过pg_class的“relpages”字段估算表或者索引的大小,SQL如下:
select relname, relpages*8/1024 as tablesize_mb from pg_class order by tablesize_mb desc limit 10;
如果要获取表或者索引的精确大小,需要通过以下函数获取:
表1 函数说明 名称
返回类型
描述
pg_relation_size(relation regclass, fork text)
bigint
指定表或索引的指定分叉('main'、'fsm'、'vm'或'init')使用的磁盘空间。
pg_relation_size(relation regclass)
bigint
pg_relation_size(..., 'main')的简写。
pg_table_size(regclass)
bigint
被指定表使用的磁盘空间,排除索引(但包括 TOAST、空闲空间映射和可见性映射)。
pg_total_relation_size(regclass)
bigint
指定表所用的总磁盘空间,包括所有的索引和TOAST数据。
- 查看表是否发生了膨胀
一旦确认了占用磁盘较多的表后,可以通过pgstattuple插件分析表是否发生了膨胀,插件可以通过如下方式安装:
create control_extension('create', 'pgstattuple'); select * from pgstattuple('table_name');
部分内核版本不支持pgstattuple插件,详见支持的插件列表。
- 清理表数据
- 如果发现是表膨胀,可以选择在维护时间窗内对表的磁盘占用整理。
vacuum full会锁表,请确保操作期间没有DML等操作。
vacuum full table_name;
- 如果发现不需要的表或数据,可以通过truncate table或是drop table清理掉不需要的数据。
truncate table table_name;
- 通过执行delete操作不会释放磁盘空间,反而因生产大量wal日志加剧磁盘空间消耗。磁盘满时禁止通过delete来释放磁盘空间。
由于PostgreSQL的MVCC机制,delete操作不会释放磁盘空间(被delete的数据被标记为不可见,空间不释放),需要结合vauum full(会锁表)才能真正释放空间。vacuum full操作自身也会消耗空间,并且会锁表,影响业务,请于业务低峰期执行,并至少预留2倍现有表大小的空闲空间。
- 另外,如果需要保留的数据相对较少,也可以新建一张表转移需要保留的数据,参考步骤:
- 保存原表的结构、索引等信息。
- 创建新表。
- 向新表插入数据。
- 检查新表的数据是否符合预期,符合则进行下一步,否则检查前面操作是否有异常。
- 删除原表。
- 将新表重命名、创建索引等。
vacuum full(会锁表)会对表及其索引进行重建,重建期间还会生成WAL日志,需要预留足够的磁盘空间(假设重建后的表大小为1GB,索引为0.5GB,建议预留2.5GB以上的磁盘空间)。
vacuum介绍:https://www.postgresql.org/docs/current/routine-vacuuming.html
- 如果发现是表膨胀,可以选择在维护时间窗内对表的磁盘占用整理。
- 查询磁盘占用前10的数据库
- 磁盘使用率达到97%以上,实例会进入只读状态,此时无法通过drop、truncate进行清理,解决方法如下:
- 查看临时文件大小是否异常并进行处理
如果总的磁盘占用减去数据文件和WAL日志还有较大的剩余,那么可能是临时文件占用较多的磁盘空间。查看临时文件大小的SQL如下:
select round(sum(size)/1024/1024/1024,2) "GB" from pg_ls_tmpdir();
- RDS for PostgreSQL 12之后的版本才有pg_ls_waldir() 函数。
- 需要root用户执行pg_ls_waldir函数。
- 当临时文件非常多时,该SQL执行会非常缓慢。
一般来说,临时文件会在复杂SQL执行完成后释放,但如果生过OOM等异常,可能会导致临时文件不能正常释放。当发现临时文件非常多时,一方面需要分析并优化慢SQL,减少临时文件的产生,另一方面需要在维护时间窗内对数据库进行重启,重启数据库可以清除所有的临时文件。