一次勒索软件恢复案例把一个平时很少被业务开发者关注的 PostgreSQL 存储细节推到台前:PostgreSQL 通常把每张表、每个索引等 relation 存成独立文件。这个设计让定位、截断、清理、备份某些对象更直观,但当系统目录被破坏、部分文件被加密或删除时,恢复人员可能会发现:数据文件还在,数据库却“不认识”它们了。
这篇文章从工程视角拆开这个问题:为什么 PostgreSQL 的单文件 relation 存储会让 catalog 恢复痛苦,它和 MySQL、Oracle 的暴露面有什么不同,以及你可以怎样提前做恢复演练。
问题不只是“文件丢了”,而是“目录失忆了”
PostgreSQL 的表、索引、TOAST 表等对象在磁盘上对应 relation 文件。文件名通常和 relfilenode 有关,而对象含义则记录在系统目录中,例如:
pg_class:relation 的元信息,包含表、索引、TOAST 表等;pg_database:数据库到目录 OID 的映射;pg_tablespace:表空间映射;- 依赖关系、命名空间、类型信息等还散落在其他系统 catalog 中。
也就是说,磁盘上看到一个文件,并不等于你知道它属于哪张业务表、哪一个索引、哪一个数据库 schema。正常情况下,PostgreSQL 通过 catalog 把逻辑对象和物理文件连起来。一旦 catalog 文件被勒索软件加密、覆盖,或者恢复时不一致,恢复就会从“拷贝文件”变成“考古”。
一个典型痛点是:业务表的数据文件可能仍然存在,但缺少可信的 catalog 映射后,无法安全地把它们重新挂回原数据库。你可能能猜出某些大文件是核心表,却很难保证字段布局、TOAST 关系、索引状态、可见性信息都一致。
和 MySQL、Oracle 的暴露面差异
这里不能简单说谁“更安全”。不同数据库的文件布局、数据字典设计和恢复工具链不同,风险形态也不同。
PostgreSQL 的特点是大量 relation 文件直接散落在数据库目录、表空间目录下。优势是对象边界清楚,很多运维操作和诊断可以落到单个 relation 文件;代价是 catalog 一旦不可用,文件的业务语义恢复难度显著增加。
MySQL,尤其是 InnoDB,常见形态包括共享表空间、独立表空间 .ibd 文件、redo/undo、数据字典等组合。它也有“文件还在但字典不认”的问题,例如独立表空间导入需要元数据匹配。但由于不同版本和配置差异很大,恢复路径通常围绕 InnoDB 字典、表结构、表空间导入工具链展开。
Oracle 更强调数据文件、控制文件、redo、归档日志和数据字典之间的整体恢复链路。企业现场常借助 RMAN、归档日志和控制文件备份完成时间点恢复。它并非没有 catalog/data dictionary 风险,但工具链通常围绕数据库整体一致性来设计。
勒索恢复时,真正的分界不是“单文件还是多文件”这么粗,而是:
- 是否有离线、不可变、可验证的备份;
- catalog 或 data dictionary 是否能和数据文件保持一致;
- 是否有 WAL/redo/归档日志支撑时间点恢复;
- 团队是否演练过“只剩部分文件”的坏场景。
可以这样实践:看懂 PostgreSQL relation 文件映射
下面这个小实验可以在本地测试库运行,用来理解 PostgreSQL 如何把表映射到物理文件。不要在生产库直接执行实验命令;如果要观察生产库,只运行只读查询。
准备一个临时数据库:
createdb relfile_demo
psql relfile_demo <<'SQL'
CREATE TABLE orders_demo (
id bigserial PRIMARY KEY,
customer_id bigint NOT NULL,
amount numeric(12,2) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO orders_demo (customer_id, amount)
SELECT g, (random() * 1000)::numeric(12,2)
FROM generate_series(1, 10000) AS g;
CREATE INDEX idx_orders_demo_customer_id ON orders_demo(customer_id);
SQL
查看逻辑对象到物理文件的映射:
psql relfile_demo <<'SQL'
SELECT
c.oid,
n.nspname AS schema_name,
c.relname,
c.relkind,
c.relfilenode,
pg_relation_filepath(c.oid) AS file_path,
pg_size_pretty(pg_relation_size(c.oid)) AS size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname IN ('orders_demo', 'orders_demo_pkey', 'idx_orders_demo_customer_id')
ORDER BY c.relname;
SQL
你会看到类似 base/数据库OID/relfilenode 的路径。继续在 shell 里对照实际文件:
DATA_DIR=$(psql -At relfile_demo -c "SHOW data_directory;")
psql -At relfile_demo -c "SELECT pg_relation_filepath('orders_demo'::regclass);" | while read -r path; do
echo "Data directory: $DATA_DIR"
echo "Relation file: $DATA_DIR/$path"
ls -lh "$DATA_DIR/$path"*
done
注意几个细节:
- 大 relation 会分段,可能出现
.1、.2这样的文件; - 可见性映射、空闲空间映射可能以
_vm、_fsm结尾; - TOAST 表会有自己的 relation 文件;
VACUUM FULL、CLUSTER、某些重写操作可能改变relfilenode。
这就是勒索恢复时痛苦的来源之一:你看到的不只是一个表文件,而是一组需要 catalog 正确解释的物理碎片。
恢复策略:不要把希望押在“手工拼文件”上
如果 PostgreSQL 集群被勒索软件破坏,优先级通常应该是保护现场,而不是立刻启动数据库反复尝试。
可以采用这样的处置顺序:
# 1. 立刻停止实例,避免进一步写入扩大损坏面
sudo systemctl stop postgresql
# 2. 对数据目录做只读快照或块级拷贝;下面只是示意,设备名需要按现场修改
sudo rsync -aHAX --numeric-ids /var/lib/postgresql/ /mnt/forensic-copy/postgresql/
# 3. 记录关键环境信息
postgres --version || true
pg_controldata /var/lib/postgresql/*/main 2>/tmp/pg_controldata.txt || true
# 4. 在隔离环境中分析副本,不要直接操作原始数据目录
更重要的是事前准备。对 PostgreSQL 来说,推荐至少具备下面几类能力:
- 物理基础备份:例如
pg_basebackup或成熟备份工具; - WAL 归档:支撑时间点恢复,避免只能恢复到上一次全量备份;
- 离线或不可变备份:备份服务器不要和数据库服务器共享同一套可写凭据;
- 定期恢复演练:备份能恢复,才叫备份;
- catalog 一致性意识:不要只备份表文件,也不要以为拷贝
base/目录就等于可恢复。
一个最小化的物理备份示例:
# 在备份机或隔离目录执行;按你的环境修改主机、用户和目录
export PGPASSWORD='change-me'
pg_basebackup \
-h 10.0.0.12 \
-U repl \
-D /backup/postgres/base_$(date +%F) \
-Fp \
-Xs \
-P \
-R
同时在 PostgreSQL 配置中启用 WAL 归档,示例配置如下,路径和命令需要按环境调整:
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /backup/postgres/wal/%f && cp %p /backup/postgres/wal/%f'
这段配置的重点不是照抄路径,而是确保 WAL 被持续、安全地复制到数据库主机之外的位置。更成熟的环境应使用专门备份工具、对象存储不可变策略、权限隔离和告警。
落地检查清单
如果你维护 PostgreSQL,建议把下面几项加入灾备检查:
- 能否在不接触生产库的情况下,从最近一次备份恢复出完整实例?
- 是否能恢复到某个指定时间点,而不只是恢复到昨晚?
- 备份账号是否无法删除历史备份?
- WAL 归档失败是否会触发告警?
- 是否记录了 PostgreSQL 大版本、扩展版本、表空间路径、关键配置?
- 是否演练过 catalog 损坏、表空间缺失、部分 relation 文件损坏这些非理想场景?
PostgreSQL 的单 relation 文件设计本身不是缺陷,它是一个明确的工程取舍。但勒索软件恢复会放大这个取舍的阴影:文件还在不等于数据库可恢复,catalog 一致性和 WAL 链路同样关键。真正可靠的方案不是事后手工拼接文件,而是事前构建可验证、可隔离、可演练的恢复体系。