记一次价值100美元的Supabase磁盘满告警的问题定位与修复

216 阅读4分钟

今天在使用Supabase过程中,遇到磁盘空间告警并影响项目正常运行的情况。当时冒了一身冷汗,产品经理就在旁边。幸好supabase上放的主要是异步数据(内部使用和清洗的数据),没有造成线上不可用(对用户无影响),但由于每日依赖这批数据,我需要在24H内修复它。历时一天我终于定位并且修复了问题。

本文结合实际案例,系统梳理了从告警现象、问题排查到修复手段的全过程,重点涵盖了索引清理、触发器优化、空间回收及Postgres版本升级等关键环节,供大家参考(以下内容均已脱敏)


一、告警现象与问题描述

Supabase免费项目默认配备8GB的GP3磁盘空间,超过后会自动扩容1.5倍,但超出免费额度部分会按小时计费。告警通常表现为:

  • 控制台提示“Projects exceeding quota”,磁盘使用超过8GB免费额度。
  • 项目进入只读模式,写操作受限。
  • 计费页面显示持续的磁盘使用量(usage)和费用。
  • 无法直接缩减磁盘大小,提示需升级Postgres版本触发“right-size”操作。

二、问题排查与定位

1. 查看磁盘使用情况

通过SQL查询了解数据库及各表大小:

SELECT pg_size_pretty(pg_database_size(current_database())) AS db_size;

SELECT relname AS table_name,
       pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;

2. 检查索引使用情况,排查冗余索引

查询表的所有索引:

SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'your_table_name';

查找未使用的索引(idx_scan=0):

SELECT psui.indexrelid::regclass AS index_name,
       psui.idx_scan AS index_scans
FROM pg_stat_user_indexes psui
JOIN pg_index pi ON psui.indexrelid = pi.indexrelid
WHERE psui.idx_scan = 0 AND pi.indisunique = false;

确认冗余索引后,删除无用索引:

DROP INDEX IF EXISTS redundant_index_name;

3. 检查触发器,删除无用触发器

查询触发器:

SELECT tgname FROM pg_trigger WHERE tgrelid = 'your_table_name'::regclass;

删除无用触发器:

DROP TRIGGER IF EXISTS trigger_name ON your_table_name;

4. 检查表膨胀及死元组情况

使用pgstattuple扩展查看表膨胀:

CREATE EXTENSION IF NOT EXISTS pgstattuple;

SELECT * FROM pgstattuple('your_table_name');

三、修复手段与具体语句

1. 删除冗余索引和触发器,减少空间和性能消耗

示例:

DROP INDEX IF EXISTS idx_job_positions_url_isclose_id_desc;
DROP TRIGGER IF EXISTS handle_updated_at ON your_table_name;

2. 执行VACUUM FULL彻底回收空间

VACUUM FULL your_table_name;

注意:VACUUM FULL会锁表,执行时间较长,建议在业务低峰期执行。

3. 重建索引(如索引膨胀严重)

REINDEX TABLE your_table_name;

4. 升级Postgres版本触发磁盘“right-size”

  • 删除不兼容扩展(如pgjwt):

    sql
    DROP EXTENSION IF EXISTS pgjwt;
    
  • 在Supabase控制台项目设置中找到Postgres版本升级入口,执行升级操作。

  • 升级过程中,系统会根据当前数据库大小自动调整磁盘容量,释放WAL碎片,减少磁盘浪费。

5. 设置更长的statement_timeout避免长操作超时

连接数据库后执行(注意在supabase的dashboard后台不生效,需要本地使用python来运行):

SET statement_timeout = '120min';

然后执行VACUUM FULL等长时间操作。


四、WAL日志空间管理

  • WAL(预写日志)用于保证数据一致性和崩溃恢复,是数据库核心机制。
  • WAL日志写入频繁,可能占用较大磁盘空间。
  • 通过合理配置检查点(checkpoint)、归档策略和监控复制延迟,控制WAL空间增长。
  • 不要手动删除WAL文件,避免数据库崩溃。

五、总结与建议

关键步骤作用与说明
删除未使用的索引减少磁盘占用和写入开销
删除无用触发器减少额外的性能和空间消耗
执行VACUUM FULL彻底回收死元组空间,释放磁盘
重建索引(REINDEX清理索引膨胀,提升查询性能
升级Postgres版本触发磁盘“right-size”,缩减磁盘碎片和WAL空间
设置长超时参数避免长操作被超时中断
监控数据库大小和使用情况及时发现和预防磁盘空间问题

通过以上步骤,成功解决了Supabase磁盘满告警问题,恢复了项目的正常读写和性能,避免了额外计费和限制。


参考链接


通过这次问题定位与修复,深刻体会到数据库维护的重要性,合理管理索引和空间,结合平台特性,才能保障项目稳定高效运行。

目前看到的情况是,在100万数据量左右情况下supabase会出现蛮奇怪的现象。一开始用supabase用得爽,网络传输成本以及与大数据计算(如火山云的Mapreduce、阿里云ODPS【dataworks】的集成还是有蛮多不方便。

计划后面还是需要排期将系统迁移到阿里云或者火山引擎的postgresql。