更新时间:2024-11-01 GMT+08:00
分享

磁盘使用率高问题定位及处理方法

指标异常说明

生产数据库的磁盘要有一定的冗余,一旦磁盘使用率过高要及时处理,防止出现磁盘满导致数据库损坏等问题。

数据库提供了多项体现磁盘使用的监控指标,建议重点关注以下指标:

  • 磁盘利用率: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日志、临时文件。当磁盘使用率增长较快不符合预期时,可以按照以下思路进行排查:

图1 排查思路

排查及解决方法

查询数据库、表、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日志占用的磁盘空间会增加,建议通过磁盘扩容保证一定的磁盘冗余。

  • 查看数据文件大小否异常并进行处理
    • 查询磁盘占用前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插件,详见支持的插件列表

      插件使用参考:https://www.postgresql.org/docs/15/pgstattuple.html

    • 清理表数据
      • 如果发现是表膨胀,可以选择在维护时间窗内对表的磁盘占用整理。

        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倍现有表大小的空闲空间。

      • 另外,如果需要保留的数据相对较少,也可以新建一张表转移需要保留的数据,参考步骤:
        1. 保存原表的结构、索引等信息。
        2. 创建新表。
        3. 向新表插入数据。
        4. 检查新表的数据是否符合预期,符合则进行下一步,否则检查前面操作是否有异常。
        5. 删除原表。
        6. 将新表重命名、创建索引等。

      vacuum full(会锁表)会对表及其索引进行重建,重建期间还会生成WAL日志,需要预留足够的磁盘空间(假设重建后的表大小为1GB,索引为0.5GB,建议预留2.5GB以上的磁盘空间)。

      vacuum介绍:https://www.postgresql.org/docs/current/routine-vacuuming.html

  • 磁盘使用率达到97%以上,实例会进入只读状态,此时无法通过droptruncate进行清理,解决方法如下:
    • 扩容磁盘空间,确保磁盘空间足够。如果原有规格的磁盘已是最大,请先升级规格

      磁盘扩容后,若空间利用率低于87%,则会只读状态会自动解除,之后删除无用数据。注意:云盘实例可以设置存储空间自动扩容,在实例存储空间达到阈值时,会触发自动扩容,避免实例磁盘打满进入只读。

    • 如果不想进行扩容,只能联系客服解除只读,再删除无用数据。注意:解除只读前请停止业务,避免继续写入。如果解开只读,数据继续写入,会导致磁盘再次爆满,实例异常。
  • 查看临时文件大小是否异常并进行处理

    如果总的磁盘占用减去数据文件和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,减少临时文件的产生,另一方面需要在维护时间窗内对数据库进行重启,重启数据库可以清除所有的临时文件。

相关文档