Skip to content

PostgreSQL 磁盘空间不足问题

磁盘空间不足现象

1. 系统层面

  • 操作系统告警:磁盘使用率超过阈值
  • 命令行输出:使用df -h查看磁盘使用率达到100%
  • 系统日志:包含磁盘空间不足的错误信息

2. 数据库层面

  • 连接失败:无法建立新连接
  • 写入失败:无法插入或更新数据
  • 错误信息
    • FATAL: could not write to file "pg_xlog/xlogtemp.1234": No space left on device
    • ERROR: could not extend file "base/12345/67890": No space left on device
    • WARNING: could not write statistics file "pg_stat_tmp/global.stat": No space left on device

磁盘空间分析

1. 查看磁盘使用率

bash
# 查看所有磁盘分区的使用率
df -h

# 查看特定目录的磁盘使用情况
du -sh /var/lib/postgresql/*

# 查看PostgreSQL数据目录的详细使用情况
du -sh /var/lib/postgresql/15/main/*

# 按照大小排序,查看前10个最大的文件或目录
du -a /var/lib/postgresql/15/main | sort -n -r | head -n 10

2. 分析PostgreSQL数据目录结构

bash
# PostgreSQL数据目录结构
# 主要目录说明:
# - base/:用户数据库文件
# - pg_xact/:事务提交日志
# - pg_wal/:预写式日志(WAL)
# - pg_stat_tmp/:临时统计数据
# - pg_logical/:逻辑复制相关文件
# - pg_replslot/:复制槽文件
# - pg_tblspc/:表空间链接

3. 查看数据库大小

sql
-- 查看所有数据库的大小
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

-- 查看特定数据库的大小
SELECT pg_size_pretty(pg_database_size('mydb'));

-- 查看数据库中表的大小(包括索引)
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC;

-- 查看数据库中索引的大小
SELECT schemaname, indexname, tablename, pg_size_pretty(pg_relation_size(schemaname || '.' || indexname)) AS size
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY pg_relation_size(schemaname || '.' || indexname) DESC;

4. 查看WAL日志大小

bash
# 查看WAL日志目录大小
du -sh /var/lib/postgresql/15/main/pg_wal

# 查看WAL日志文件数量
ls -la /var/lib/postgresql/15/main/pg_wal | grep -v "\." | wc -l
sql
-- 查看WAL保留策略
SHOW wal_keep_size;
SHOW max_wal_size;
SHOW min_wal_size;

-- 查看复制槽状态(可能导致WAL堆积)
SELECT slot_name, slot_type, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS wal_diff
FROM pg_replication_slots;

磁盘空间不足解决措施

1. 紧急释放空间

清理临时文件

bash
# 清理PostgreSQL临时统计文件
sudo rm -f /var/lib/postgresql/15/main/pg_stat_tmp/*

# 清理操作系统临时文件
sudo rm -f /tmp/*

清理WAL日志(仅在紧急情况下)

bash
# 注意:此操作非常危险,可能导致数据丢失,仅在无法启动数据库时使用
# 确保已备份重要数据
# 清理旧的WAL日志文件
sudo rm -f /var/lib/postgresql/15/main/pg_wal/00000001000000000000000*

清理无用的复制槽

sql
-- 删除不活跃的复制槽
SELECT pg_drop_replication_slot('inactive_slot');

2. 长期解决方案

扩展磁盘空间

  • 添加新的磁盘分区
  • 扩展现有分区
  • 迁移到更大的存储设备

优化数据库存储

sql
-- 清理表中的死元组
VACUUM ANALYZE table_name;

-- 彻底清理表(需要排它锁)
VACUUM FULL ANALYZE table_name;

-- 重建索引(释放空间)
REINDEX TABLE table_name;

-- 删除无用的表
DROP TABLE unused_table;

-- 删除无用的索引
DROP INDEX unused_index;

-- 清理大表中的历史数据
DELETE FROM large_table WHERE created_at < '2023-01-01';
VACUUM ANALYZE large_table;

配置自动清理

sql
-- 调整自动清理参数
ALTER SYSTEM SET autovacuum = 'on';
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = '0.05'; -- 5%的行变更触发清理
ALTER SYSTEM SET autovacuum_analyze_scale_factor = '0.02'; -- 2%的行变更触发分析
ALTER SYSTEM SET autovacuum_max_workers = '3'; -- 最大清理工作线程数
ALTER SYSTEM SET maintenance_work_mem = '256MB'; -- 清理时使用的内存

-- 重新加载配置
SELECT pg_reload_conf();

配置WAL日志保留策略

sql
-- 调整WAL日志大小限制
ALTER SYSTEM SET max_wal_size = '1GB';
ALTER SYSTEM SET min_wal_size = '512MB';

-- 调整WAL保留时间(如果使用归档)
-- 注意:这取决于归档设置和备份策略

-- 重新加载配置
SELECT pg_reload_conf();

磁盘空间监控与告警

1. 系统层面监控

bash
# 使用iostat监控磁盘I/O和使用率
iostat -x 1

# 使用du定期检查目录大小
# 可以编写脚本定期检查并告警

2. 数据库层面监控

sql
-- 监控数据库大小变化
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size,
       pg_size_pretty(pg_database_size(datname) - pg_size_pretty(pg_database_size(datname) FILTER (WHERE datname = datname AND now() - pg_postmaster_start_time() < interval '1 day'))) AS growth
FROM pg_database;

-- 监控表大小变化
CREATE OR REPLACE FUNCTION monitor_table_growth()
RETURNS VOID AS $$
BEGIN
  -- 插入表大小信息到监控表
  INSERT INTO table_growth_monitor (table_name, size, monitoring_time)
  SELECT schemaname || '.' || tablename, pg_total_relation_size(schemaname || '.' || tablename), now()
  FROM pg_tables
  WHERE schemaname = 'public';
END;
$$ LANGUAGE plpgsql;

-- 每天执行一次监控
SELECT cron.schedule('0 0 * * *', 'SELECT monitor_table_growth();');

3. 告警配置

Prometheus + Grafana监控

  • 监控指标

    • node_filesystem_avail_bytes:可用磁盘空间
    • node_filesystem_size_bytes:总磁盘空间
    • node_filesystem_free_bytes:空闲磁盘空间
    • node_filesystem_files_free:可用文件数
  • 告警规则

    • 磁盘使用率超过90%时告警
    • 可用空间小于10GB时告警
    • WAL目录使用率超过80%时告警

Zabbix监控

  • 监控项

    • 磁盘使用率
    • 可用磁盘空间
    • PostgreSQL数据目录大小
    • WAL日志目录大小
  • 触发器

    • 磁盘使用率 > 90%
    • 可用磁盘空间 < 10GB
    • WAL目录大小 > max_wal_size * 2

磁盘空间管理最佳实践

1. 预防措施

  • 定期监控:设置磁盘空间监控和告警
  • 合理规划:根据业务增长预测,规划足够的磁盘空间
  • 自动清理:配置自动清理策略,定期清理无用数据
  • 分区表:对大表使用分区,便于管理和清理历史数据
  • 压缩数据:对不常访问的数据进行压缩

2. 备份与恢复

  • 定期备份:确保有可靠的备份策略
  • 测试恢复:定期测试备份恢复,确保数据可恢复
  • 增量备份:结合全量备份和增量备份,减少备份空间占用

3. 存储优化

  • 使用表空间:将不同类型的数据存储在不同的磁盘上
  • 优化WAL策略:根据业务需求调整WAL保留策略
  • 使用压缩备份:使用pg_basebackup的压缩选项
  • 清理无用对象:定期清理无用的表、索引和扩展

常见问题(FAQ)

Q1:PostgreSQL磁盘空间不足的常见原因有哪些?

A1:PostgreSQL磁盘空间不足的常见原因包括:

  • 数据量增长过快
  • WAL日志堆积(复制槽未清理、归档失败)
  • 临时文件过多
  • 自动清理不及时,导致死元组堆积
  • 索引膨胀
  • 表空间配置不当

Q2:如何查看PostgreSQL数据目录的使用情况?

A2:可以使用以下命令查看:

bash
du -sh /var/lib/postgresql/15/main/*
du -a /var/lib/postgresql/15/main | sort -n -r | head -n 10

Q3:如何安全地清理PostgreSQL磁盘空间?

A3:安全清理PostgreSQL磁盘空间的方法:

  • 先分析空间使用情况,找出占用空间最大的文件或目录
  • 使用VACUUM和VACUUM FULL清理死元组
  • 重建膨胀的索引
  • 清理无用的表和索引
  • 调整WAL保留策略
  • 清理不活跃的复制槽

Q4:如何防止PostgreSQL磁盘空间不足?

A4:防止PostgreSQL磁盘空间不足的措施:

  • 设置磁盘空间监控和告警
  • 配置自动清理策略
  • 定期清理历史数据
  • 使用分区表管理大表
  • 合理规划存储容量
  • 定期备份和清理备份文件

Q5:WAL日志堆积的原因是什么?如何解决?

A5:WAL日志堆积的常见原因:

  • 复制槽不活跃或配置错误
  • 归档失败
  • max_wal_size设置过大

解决方法:

  • 删除不活跃的复制槽
  • 检查归档配置,确保归档成功
  • 调整max_wal_size参数
  • 手动清理旧的WAL日志(仅在紧急情况下)