Postgres 13 单个 tablespace 超过 13TB,是否应拆分?
超过 13TB 本身不足以成为拆分 tablespace 的理由
把相同的数据分配到多个 tablespace,不会自动提高查询性能,也不会自动缩短备份时间。
如果需要将部分数据迁到独立磁盘或更快的存储,或者现有卷已经无法扩容,新增 tablespace 就有明确用途。PostgreSQL 13 官方文档也指出,在同一个逻辑文件系统内创建多个 tablespace,通常没有多少意义。这条说明可以作为团队讨论的依据。官方文档:Tablespaces
13TB 的 tablespace 中,数据分散在多个文件里
PostgreSQL 的 tablespace 用来指定数据库对象在文件系统中的存放位置。表和索引各有自己的存储文件;在默认构建配置下,表或索引超过 1GB 后,还会分成多个文件段。因此,tablespace 中存放了 13TB 数据,并不意味着 PostgreSQL 在读写一个 13TB 的文件。官方文档:Database File Layout
tablespace 总大小和单个关系的大小也需要区分。PostgreSQL 13 文档列出的单个关系大小上限,在默认 BLCKSZ = 8192 字节时为 32TB。这个限制不能直接套用到整个 tablespace。官方文档:PostgreSQL Limits
评估容量时,应检查剩余空间、增长速度和底层存储限制。13TB 本身不是必须拆分的门槛。
性能是否改善,取决于数据最终放在哪里
| 调整方式 | 对性能的预期 |
|---|---|
| 在同一文件系统的不同目录创建 tablespace | 通常没有直接收益 |
| 放到不同逻辑卷,但仍共享同一存储池和带宽 | 不保证改善,需要确认底层资源是否独立 |
| 将繁忙对象迁到有空余能力的独立存储 | 如果原瓶颈是 I/O 争用,可能有效 |
| 将高频访问的索引迁到更快存储 | 如果相关查询受存储延迟限制,可能有效 |
官方文档给出的使用场景包括:将频繁使用的索引放到快速存储,将很少访问的历史数据放到成本较低的存储。官方文档:Tablespaces 的使用场景
这些收益来自存储能力和对象布局的变化。仅增加 tablespace 数量,不会减少查询需要读取的数据,也无法修复不合适的索引或执行计划。
是否把表和索引分到不同 tablespace,也要看实际情况。先确认它们是否在争用同一组 I/O 资源,以及目标存储能否提供额外能力,再决定是否分开存放。
拆分 tablespace 为什么通常不能缩短备份时间
备份耗时要结合实际使用的工具分析。
使用 pg_basebackup 做物理备份
PostgreSQL 13 的 pg_basebackup 会备份整个数据库集群,包括各个 tablespace。增加 tablespace 后,需要复制的数据总量通常没有减少。
该版本的 pg_basebackup 没有用于并行复制数据文件的 -j 选项。使用 tar 格式时,额外的 tablespace 会分别生成 tar 文件,但这些文件不会因此自动并行传输。官方文档:pg_basebackup
应先检查备份源的读取速度、网络吞吐量、备份目标的写入速度,以及压缩带来的 CPU 开销。解决实际瓶颈后,备份窗口才可能缩短。
此外,tablespace 依赖集群中的其他元数据。仅复制一个 tablespace 目录,不能得到可独立恢复的物理备份。官方文档:Tablespaces 的备份限制
使用 pg_dump 做逻辑备份
pg_dump 的并行导出按表分配任务,与 tablespace 数量无关。PostgreSQL 13 可以使用目录格式启用并行导出:
pg_dump -h dbhost -U backup_user -d appdb \
-Fd -j 4 -f /backup/appdb_dump
这里的 4 只是示例,需要根据生产负载和备份存储能力调整。并行导出会增加服务器负载,并且需要 njobs + 1 个数据库连接。如果绝大部分数据集中在一张大表中,-j 4 也不会自动把这张表的数据拆成四份同时导出。官方文档:pg_dump 的并行选项
pg_dump 只备份一个数据库,角色等集群级对象需要另外处理。因此,上述命令还不能作为完整的集群备份方案。
建议先做哪些检查
1. 确认数据规模和实际路径
先查询 tablespace 的位置和大小:
SELECT
spcname,
pg_tablespace_location(oid) AS location,
pg_size_pretty(pg_tablespace_size(oid)) AS size
FROM pg_tablespace
ORDER BY pg_tablespace_size(oid) DESC;
内置 tablespace 的位置可能显示为空,这不代表其中没有数据。对需要排查的大表,再分别查看表数据和索引的占用:
SELECT
pg_size_pretty(pg_table_size('public.large_table'::regclass))
AS table_size,
pg_size_pretty(pg_indexes_size('public.large_table'::regclass))
AS indexes_size,
pg_size_pretty(pg_total_relation_size('public.large_table'::regclass))
AS total_size;
将 public.large_table 替换为实际表名。pg_table_size 包含 TOAST 等附属存储,但不包含索引;pg_total_relation_size 包含索引和 TOAST。调用部分大小函数需要相应权限。官方文档:数据库对象大小函数
随后请存储管理员确认这些路径对应的文件系统、逻辑卷和物理存储池。路径或挂载点不同,不一定代表它们有独立的 I/O 能力。
2. 分别测量业务和备份瓶颈
在业务高峰和备份窗口记录以下信息:
- 存储读写吞吐量、延迟和队列情况。
- 慢查询的执行计划,以及主要读取哪些表和索引。
- 备份时源端、网络、目标端的吞吐量和 CPU 使用情况。
- 当前备份耗时,以及实际恢复需要多长时间。
如果业务受 CPU 或查询计划限制,迁移 tablespace 通常帮助不大;如果备份目标的写入带宽已经用满,增加源端 tablespace 也解决不了问题。
3. 明确目标后,先做小范围迁移验证
例如,已经确认某个热点索引受原存储延迟影响,并且准备好了更快的存储,就可以先迁移这个索引。在相近负载下比较迁移前后的查询延迟和存储指标,再决定是否扩大范围。
如果主要目标是缩短备份窗口,应单独评估备份并行度、压缩设置,以及支持增量或差异备份的方案,同时验证恢复流程。
确实需要拆分时,如何迁移
假设新存储已挂载到 /mnt/pgfast,空目录 /mnt/pgfast/ts_hot 也已准备好,且目录属于运行 PostgreSQL 的操作系统用户。由数据库超级用户创建 tablespace:
CREATE TABLESPACE ts_hot
LOCATION '/mnt/pgfast/ts_hot';
CREATE TABLESPACE 必须单独执行,不能放在事务块内。路径必须是绝对路径,目录必须事先存在。官方文档:CREATE TABLESPACE
然后按对象迁移,以下名称均为示例:
ALTER TABLE public.orders
SET TABLESPACE ts_hot;
ALTER INDEX public.orders_pkey
SET TABLESPACE ts_hot;
执行前需要注意:
ALTER TABLE ... SET TABLESPACE会实际移动表的数据文件,并取得ACCESS EXCLUSIVE锁。迁移期间,该表的普通读写会被阻塞,大表应安排维护窗口。- 移动表不会自动移动其索引,需要分别处理。
- 对分区表父表执行该命令,不会搬迁已有分区的数据;已有分区应逐个安排迁移。
- 应预留目标空间,评估复制数据带来的额外 I/O、WAL 和复制延迟,并先用有代表性的对象验证迁移耗时。
具体移动行为和锁要求可查阅 PostgreSQL 13 的 ALTER TABLE 文档。
新增 tablespace 后,还要更新备份与恢复的路径安排。使用 pg_basebackup 的 plain 格式时,额外 tablespace 默认沿用源端绝对路径;恢复到不同目录布局时,需要规划 --tablespace-mapping,并实际演练恢复。官方文档:pg_basebackup 路径映射
对于当前这个 13TB 数据库,建议先收集存储拓扑、业务 I/O 指标和备份吞吐量,再确定需要迁移哪些对象、迁到哪组存储,以及要解决什么瓶颈,以便验证拆分方案是否有效。
备注:内容仅供参考。
