GBase 8c
其他
文章
精选

GBase 8c 表空间规划和对象迁移

发表于2026-04-02 10:18:35156次浏览4个评论

GBase 8c 表空间规划和对象迁移

我最近看 GBase 8c 资料时,越来越强烈的一个感觉是:很多现场不是不会建表空间,而是把表空间用得太晚、太散、太随意。

真正落到现场时,最常见的现象通常不是“不会执行 CREATE TABLESPACE”,而是业务已经跑了一段时间,默认表空间所在磁盘开始吃紧,热点表、归档表、临时排序、历史索引全堆在一起,最后才想到把对象往别的路径挪。这个时候事情就不再是“加一个目录”这么简单了,而是变成了对象盘点、变更窗口、I/O 冲击、回退方案一起上。

我自己理解下来,GBase 8c 里的表空间更像是存储布局的控制点。它能解决“对象落到哪里”的问题,但解决不了“规划混乱”本身。要是前期没想清楚,后面迁移对象时不仅麻烦,还容易把影响面低估。

我实际排查时先看的,不是建不建表空间

很多人一上来就问,默认表空间满了,要不要赶紧新建一个。 但我最近整理下来觉得,先别急着建,先把下面几件事想清楚:

  1. 这次是容量问题,还是冷热数据混放问题。
  2. 需要移动的是表、索引,还是临时文件压力。
  3. 这批对象是后续持续新增,还是只需要一次性搬迁。
  4. 当前环境是不是适合自定义表空间,而不是继续沿用默认表空间。

先把这四个问题分开,后面的动作才不会乱。

现场现象 我更倾向于先判断什么 常见误区
默认路径空间告急 是单个大对象膨胀,还是很多对象混放 直接新建表空间,但不盘点对象
查询高峰时 I/O 抖动明显 是热点索引和归档数据抢盘,还是排序临时文件压力 只搬表,不搬索引
新系统即将上线 是不是应该在建库建表阶段就定好落盘位置 等上线后再慢慢挪
云化或标准化部署环境 是否真的适合使用自定义表空间 默认把“多表空间”当最佳实践

从落地角度看,表空间最怕的不是没建,而是只建不用规则、只迁不做分层

我对 GBase 8c 表空间边界的理解

GBase 8c 的数据最终由 DN 节点落盘,表空间本质上是文件系统里的目录,用来承载数据库对象的数据文件。系统自带的两个表空间是 pg_defaultpg_global。我自己更关注的是这两个内置表空间的边界:

  • pg_default:默认承载非共享系统表、用户表、用户表索引、临时表、临时表索引、内部临时表。
  • pg_global:承载共享系统表空间,不是业务对象随便放的地方。

我最近看资料时发现,很多人把“表空间”理解成逻辑库的分组,但实际上它更偏物理存储布局。数据库、模式、表、索引是逻辑对象;表空间解决的是这些对象最终落在哪个目录、哪类介质上。

对象类型 我一般怎么理解它和表空间的关系 现场建议
数据库 可以关联默认表空间,但不是拿来替代对象级规划的 库级默认只做兜底,不做精细治理
表数据只能属于一个表空间 大表、历史表、归档表适合单独规划
索引 索引是独立对象,别默认认为会和表一起迁 热点索引要单独盘点
临时对象/临时文件 和会话行为、排序/哈希压力强相关 需要单独观察,不要混着看
系统共享对象 pg_global 不建议碰业务规划

这一点我特别想强调:迁表不等于迁完了。 很多现场真正占 I/O 的,可能反而是索引和临时文件。

哪些场景适合自定义表空间,哪些场景我反而会克制一点

GBase 8c 文档里对表空间的定位很清楚,核心还是控制磁盘布局、隔离不同对象的存储位置。 但我自己更倾向于把它用在下面几种场景:

场景 我更推荐的做法 原因
热点交易表和历史归档表混在一个路径 分开规划业务表空间 容量和 I/O 行为差异太大
热点索引需要更稳定的介质 索引与表分开规划 真实瓶颈常常先出现在索引读写
原路径已经接近容量上限 给后续新增对象切到新表空间 比全量搬迁更稳
临时排序和业务对象争抢磁盘 评估单独的临时表空间策略 能减少高峰期互相影响

但也不是所有环境都值得自定义表空间。 我最近整理资料时注意到,在一些标准化云场景里,文档本身就不建议用户随意使用自定义表空间。原因也不复杂:底层存储往往已经标准化,额外拆表空间不一定带来收益,反而容易增加运维复杂度。 所以我个人更倾向于这个判断顺序:

  • 能用默认表空间解决的,不急着拆。
  • 必须做物理隔离的,再做自定义表空间。
  • 一旦做了,就把对象范围、命名规范、迁移顺序一次想清楚。

我更愿意采用的落地步骤

1)先准备目录,而不是先写 SQL

表空间对应的是目录,目录条件不满足,后面 SQL 再漂亮也没用。 我自己一般会先在数据库节点把路径准备好,再进入数据库层动作。

#!/bin/bash

for p in /data1/gbase/ts_hot \
         /data2/gbase/ts_hist \
         /data3/gbase/ts_temp
do
  mkdir -p "$p"
  chown -R gbase:gbase "$p"
  chmod 700 "$p"
done

# 做变更前,先确认目录为空
for p in /data1/gbase/ts_hot /data2/gbase/ts_hist /data3/gbase/ts_temp
do
  echo "== $p =="
  ls -la "$p"
done

这里我一般会特别检查三件事:

检查项 为什么要先看 我自己的判断方式
目录是否已存在且为空 表空间创建依赖已有空目录 ls -la,别直接上 SQL
目录属主属组是否正确 避免后面出现权限问题 统一给数据库运行用户
路径是否为稳定存储 表空间丢失会直接影响实例工作 不放移动介质、不放临时挂载点

2)创建表空间时,先按用途命名

我个人不太喜欢 space1space2 这种命名。 真到了半年后排查现场,谁都记不住哪个是热表、哪个是历史、哪个是临时。

CREATE TABLESPACE ts_hot  LOCATION '/data1/gbase/ts_hot';
CREATE TABLESPACE ts_hist LOCATION '/data2/gbase/ts_hist';
CREATE TABLESPACE ts_temp LOCATION '/data3/gbase/ts_temp';

-- 按需授权给业务角色
GRANT CREATE ON TABLESPACE ts_hot  TO app_rw;
GRANT CREATE ON TABLESPACE ts_hist TO app_rw;

命名我一般会直接带语义:

  • ts_hot:高频交易对象
  • ts_hist:历史归档对象
  • ts_temp:临时对象或临时文件策略验证
  • ts_idx_hot:热点索引单独落盘时再建

3)新增对象尽量在创建阶段就落对地方

我自己更倾向于把“对象落盘位置”前移到建表脚本,而不是等上线后再改。 尤其是大表和大索引,后迁移的代价通常更高。

CREATE TABLE acct.bill_detail (
    bill_id        bigint primary key,
    acct_no        varchar(32) not null,
    trade_date     date not null,
    trade_amount   numeric(18,2) not null,
    status         varchar(16) not null,
    created_time   timestamp not null default current_timestamp
) TABLESPACE ts_hot;

CREATE INDEX idx_bill_detail_01
ON acct.bill_detail(trade_date, acct_no)
TABLESPACE ts_hot;

如果某一批对象在一个会话里统一创建,我一般会显式设置默认表空间,避免脚本里漏写:

SET default_tablespace = ts_hist;

CREATE TABLE acct.bill_detail_2024m12 (
    like acct.bill_detail including defaults
);

CREATE INDEX idx_bill_detail_2024m12_01
ON acct.bill_detail_2024m12(trade_date, acct_no);

而临时对象相关的策略,我更愿意单独验证,不和业务表迁移混在一个窗口里:

SET temp_tablespaces = 'ts_temp';

CREATE TEMP TABLE tmp_bill_check AS
SELECT *
FROM acct.bill_detail
WHERE trade_date >= date '2024-12-01';

真正开始迁移前,我一定会先做对象盘点

很多人做对象迁移时,最大的问题不是不会写 ALTER,而是不知道自己到底要迁什么

我一般先把目标模式里的表和索引盘出来,至少先看清楚当前对象落在哪个表空间。

SELECT
    n.nspname AS schema_name,
    c.relname AS object_name,
    c.relkind AS object_type,
    COALESCE(t.spcname, 'pg_default') AS tablespace_name
FROM pg_class c
JOIN pg_namespace n
  ON n.oid = c.relnamespace
LEFT JOIN pg_tablespace t
  ON t.oid = c.reltablespace
WHERE n.nspname = 'acct'
  AND c.relkind IN ('r', 'i')
ORDER BY n.nspname, c.relkind, c.relname;

如果只是快速查看现有表空间,我一般会直接用这两种方式:

SELECT spcname FROM pg_tablespace;
\db

上面这个顺序我自己很少省。 因为只要一跳过盘点,后面就很容易出现两个问题:

  1. 表挪走了,索引还在原路径。
  2. 当前对象其实不大,真正大的对象根本没被纳入本次变更。

我更喜欢“先生成 SQL,再分批执行”

在现场里,最怕的是手敲几十条 ALTER TABLE ... SET TABLESPACE。 我自己的习惯是先生成,再复核,再分批跑。

迁移一批历史表

SELECT format(
    'ALTER TABLE %I.%I SET TABLESPACE ts_hist;',
    schemaname,
    tablename
)
FROM pg_tables
WHERE schemaname = 'acct'
  AND tablename LIKE 'bill_detail_2024%';

迁移对应索引

SELECT format(
    'ALTER INDEX %I.%I SET TABLESPACE ts_hist;',
    schemaname,
    indexname
)
FROM pg_indexes
WHERE schemaname = 'acct'
  AND tablename LIKE 'bill_detail_2024%';

我个人更倾向于分三步走:

步骤 我会怎么做 这样做的原因
第一步 先生成迁移 SQL 清单 防止漏对象、漏索引
第二步 先跑小批量对象验证窗口 先看锁等待、I/O 波动、耗时
第三步 再分批处理大对象 把风险拆小,便于回退

这里还有一个我实际排查时经常遇到的坑: 表迁完以后,不代表业务路径就一定改善。 如果热点访问主要落在索引,或者高峰期压力来自临时排序,那单纯挪表,效果通常不会特别明显。

这些坑我自己会提前写进变更单里

坑一:只看表,不看索引

这是最常见的。业务反馈“已经迁表了,磁盘还是忙”,最后一查,大索引还在老路径上。

坑二:把表空间当容量回收工具

表空间能重新布局对象,但它不是空间回收工具。 如果问题根源是历史垃圾、对象膨胀、无序增长,只迁路径不治理对象,后面还会再满。

坑三:高峰期直接迁大对象

我自己一般不会在业务高峰做大对象迁移。 即使 SQL 看起来简单,底层也涉及真实数据文件移动,这种动作最好还是留给明确的变更窗口。

坑四:路径规划没有命名规则

没有命名规范的表空间,时间一长就会变成“知道有、没人敢动”的状态。 后面无论巡检、扩容还是迁移,成本都会升高。

坑五:忘了删除前提是表空间必须为空

这类问题通常出现在回滚或者试验环境清理时。 表空间里还有对象,DROP TABLESPACE 就不会让你轻松过去。

常见坑 现场表现 我更建议的做法
只迁表不迁索引 迁移后 I/O 改善不明显 表和索引分开盘点、分开执行
把表空间当扩容补丁 过一段时间又满 同时做对象分层和容量治理
高峰期迁大对象 业务抖动、窗口不可控 小批量预演后再正式执行
路径命名混乱 后续无人敢维护 建立统一命名和用途说明
清理不彻底 删除表空间失败 先确认对象完全迁出

我自己更认可的一套思路

如果把这件事再压缩一下,我现在会把 GBase 8c 表空间治理理解成三句话:

第一,表空间解决的是物理落盘,不是逻辑建模。 第二,新对象尽量创建时就放对位置,别把所有问题留到后迁移。 第三,迁移动作一定是对象盘点、表索引分治、分批执行,而不是一条命令糊过去。

我最近整理下来觉得,GBase 8c 的表空间能力并不难,难的是把它用在真正合适的地方。 该用的时候,要把目录、对象、会话参数、执行窗口一起想清楚;不该用的时候,也别为了“看起来更专业”硬拆出一堆表空间。 真正能让现场更稳的,往往不是多建几个目录,而是从一开始就把对象怎么落、后面怎么迁、出了问题怎么退,提前想明白。

参考资料

[1] GBase 8c 文档介绍
https://www.gbase.cn/docs/gbase-8c/%E6%AC%A2%E8%BF%8E/

[2] 基本概念 | GBASE南大通用
https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5

[3] 数据库使用 | GBASE南大通用
https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8

[4] gsql | GBASE南大通用
https://www.gbase.cn/docs/gbase-8c/04%20%E5%B7%A5%E5%85%B7%E5%8F%82%E8%80%83/01%20%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%B7%A5%E5%85%B7/gsql

[5] GBase 8c 教程(十五)表空间
https://www.gbase.cn/community/post/1821

评论

登录后才可以发表评论
用户头像
柒柒天晴发表于 5个月前
111
用户头像
GBase用户28017发表于 5个月前
优秀
GBase用户47954发表于 5个月前
感谢作者的精彩分享!
流泪猫猫头发表于 3个月前
学习了。