承上启下:上篇我们聊了什么
上篇我们从一次让我差点删库跑路的事故讲起,聊了表空间的前世今生、auto_createtblspcdir参数的基本用法、GUC机制、底层实现逻辑,还有实战场景和踩坑记录。
说实话,写完上篇之后我发现,表空间这个话题远比我最初想的要复杂。它不只是"创建目录、自动创建目录"这么简单,背后涉及存储规划、性能调优、安全管控、运维规范等一系列问题。
今天这篇下篇,我们就把这些延伸话题一一展开聊聊。
表空间管理最佳实践:让你的存储稳如老狗
做了这么多年运维,我总结了一套表空间管理的最佳实践,不敢说完美,但确实帮我避开了大部分坑。
磁盘规划策略:未雨绸缪才是王道
做存储规划的时候,很多人犯的毛病是"先用起来再说"。结果等到磁盘满了、业务炸了,才开始手忙脚乱地扩容。
我建议在做数据库部署规划的时候,就要把表空间的磁盘布局想清楚。
第一步,确认有几块磁盘可用。
现在服务器一般都有多块盘,比如系统盘SSD 500G,数据盘1TB SSD,数据盘4TB SATA。不同性能的盘要分配不同的用途。
第二步,明确哪些数据要分离存储。
一般来说,我会把数据分成这么几类:
- 系统表空间:存放系统表、索引、视图这些元数据,访问频率高,性能要求高,放SSD
- 业务表空间:存放业务数据,根据热度继续细分
- 索引表空间:如果索引量很大,可以单独建表空间放索引,和基表分离,提升查询并发
- 临时表空间:排序、哈希、临时结果集用的,性能要求也高,放SSD
- 归档表空间:历史数据、归档日志,访问频率低,放大容量SATA
-- 创建多个独立的表空间
CREATE TABLESPACE tbs_system LOCATION '/ssd1/kingbase/system';
CREATE TABLESPACE tbs_app_hot LOCATION '/ssd2/kingbase/app_hot';
CREATE TABLESPACE tbs_app_cold LOCATION '/sata1/kingbase/app_cold';
CREATE TABLESPACE tbs_index LOCATION '/ssd3/kingbase/index';
CREATE TABLESPACE tbs_temp LOCATION '/ssd4/kingbase/temp';
第三步,一个表空间对应一个独立的磁盘分区。
这是重点!不要多个表空间共用一个分区。为什么?因为如果共用的话,你没法做真正的IO隔离,也没法单独给某个表空间设置磁盘配额。将来想扩容或者迁移某一个业务,会非常麻烦。
第四步,预留足够的余量。
生产环境我一般建议预留30%的磁盘空间,不要把磁盘用到80%、90%才想起来扩容。磁盘空间不足会导致数据库性能急剧下降,严重的甚至会宕机。
监控要提前做好,设置告警阈值,比如磁盘使用率达到70%就告警,给扩容留足时间窗口。
目录权限配置:安全底线不能破
KES对表空间目录的权限有严格的要求,不了解这些规则就容易踩坑。
核心规则只有三条,但每条都很重要:
规则一:属主必须是KES运行用户。
通常情况下,KES的运行用户是kingbase:kingbase。你创建的表空间目录,属主必须是这个用户。
mkdir -p /data/kingbase/tbs_app
chown kingbase:kingbase /data/kingbase/tbs_app
chmod 700 /data/kingbase/tbs_app
规则二:权限最好是700。
700意味着只有属主有读写执行权限,组用户和其他用户没有任何权限。KES会检查权限,755或者777会被认为不安全,直接拒绝访问。
我知道有些人觉得700太严格了,但这是KES的安全策略,为了防止数据被其他用户非法读取。生产环境,安全大于便利。
规则三:父目录也必须有写权限。
如果你的表空间目录是/data/kingbase/tbs/biz1,要自动创建biz1这个子目录,那么/data/kingbase/tbs这个父目录也必须给kingbase用户写权限。
很多自动创建失败的案例,根因都在这——父目录权限不够。
# 确保父目录有正确的属主和权限
chown -R kingbase:kingbase /data/kingbase
chmod 755 /data/kingbase
路径命名规范:统一约定便于维护
一个规范的命名约定,能让你的表空间管理事半功倍。
我的命名习惯是这样的:/data/tbs_业务名_用途
CREATE TABLESPACE tbs_erp_data LOCATION '/data/tbs_erp_data';
CREATE TABLESPACE tbs_erp_index LOCATION '/data/tbs_erp_index';
CREATE TABLESPACE tbs_crm_archive LOCATION '/data/tbs_crm_archive';
这样命名有什么好处?
第一,从路径名就能看出这个表空间是给哪个业务用的。
第二,同一个业务的表空间和索引分开了,方便做性能分析和容量规划。
第三,统一的前缀便于写自动化脚本、做批量处理。
还有一些禁用规则:
- 不要用中文路径,有些字符集问题会导致识别失败
- 不要用空格、特殊字符,路径只允许字母、数字、下划线、短横线
- 不要用太长的路径名,某些操作系统有路径长度限制
表空间性能调优:让数据库飞起来
表空间不只是存储容器,更是性能优化的关键抓手。用好了,性能提升很明显。
I/O负载均衡:别让单块磁盘累死
数据库的I/O是最容易成为瓶颈的地方。如果所有表空间都放在同一块磁盘上,所有读写请求都排队等这一块盘,那性能肯定好不了。
正确的做法是把热点表分散到不同的磁盘,让I/O请求分散开。
怎么判断哪些是热点表?我一般这么操作:
-- 查看哪些表空间占用空间最大
SELECT spcname, pg_size_pretty(pg_tablespace_size(oid)) AS size
FROM pg_tablespace
ORDER BY pg_tablespace_size(oid) DESC;
-- 查看哪些表访问最频繁(需要开启统计信息收集)
SELECT schemaname, tablename, seq_scan, idx_scan
FROM pg_stat_user_tables
ORDER BY (seq_scan + idx_scan) DESC
LIMIT 20;
然后根据分析结果,把大表、热点表放到独立的高速盘表空间。
-- 把大表迁移到新的表空间
ALTER TABLE big_table SET TABLESPACE tbs_fast;
ALTER INDEX big_table_pkey SET TABLESPACE tbs_fast;
物理存储分离:表和索引别放一起
很多人建表的时候不注意,索引默认和表放在同一个表空间。这样的话,读写索引的时候会和读写表的数据竞争磁盘I/O。
更好的做法是把索引单独放到一个表空间,最好是另一块磁盘。
-- 创建一个索引表空间
CREATE TABLESPACE tbs_index LOCATION '/ssd2/kingbase/index';
-- 创建表的时候指定表空间
CREATE TABLE orders (
id bigserial PRIMARY KEY,
customer_id integer,
order_date timestamp,
amount numeric(10,2)
) TABLESPACE tbs_data;
-- 创建索引的时候指定到独立表空间
CREATE INDEX idx_orders_customer ON orders(customer_id) TABLESPACE tbs_index;
CREATE INDEX idx_orders_date ON orders(order_date) TABLESPACE tbs_index;
这样做的好处是:查询的时候表数据和索引数据的读取可以并行,不互相争抢带宽。
热点数据隔离:热数据用好盘
这是最常见的性能优化手段。
按照数据的访问频率,把数据分成热、温、冷三层:
- 热数据:最近7天的数据,访问最频繁,放SSD
- 温数据:最近30天的数据,偶尔访问,放普通SSD或SAS盘
- 冷数据:超过30天的历史数据,很少访问,放SATA或者直接归档
分区表结合表空间使用,效果最好:
-- 创建一个按时间分区的订单表
CREATE TABLE orders (
id bigserial,
order_date date,
amount numeric(10,2)
) PARTITION BY RANGE (order_date);
-- 为最近的数据创建热表空间分区
CREATE TABLE orders_recent PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')
TABLESPACE tbs_hot;
-- 为历史数据创建冷表空间分区
CREATE TABLE orders_history PARTITION OF orders
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01')
TABLESPACE tbs_cold;
我们公司用这个方法,存储成本降了60%,为什么?因为大部分查询都是查最近的数据,而最近的数据只占整体数据量的很小一部分,放SSD就够了。历史数据放到便宜的大容量盘上,性能没影响,成本省了一大截。
临时表空间优化:让排序不再卡顿
临时表空间用于排序、哈希聚合、创建临时表这些操作。如果临时表空间性能差,大查询排序的时候会很慢。
建议把临时表空间放到最快的SSD上:
-- 创建高性能临时表空间
CREATE TABLESPACE tbs_temp LOCATION '/ssd_fast/kingbase/temp';
-- 设置为默认临时表空间
SET temp_tablespaces = 'tbs_temp';
GUC参数治理:让配置管理更规范
上篇我们讲了auto_createtblspcdir是GUC参数的一种,这篇我们把GUC参数治理这件事系统地讲一讲。
GUC参数的分类与优先级
KES的GUC参数大概有几百个,涉及内存、连接、查询优化、存储、日志、安全等方方面面。怎么管理好这些参数,是个技术活。
先说参数分类。按功能分,大致可以分为这几类:
- 连接相关:max_connections、shared_buffers等
- 内存相关:work_mem、maintenance_work_mem等
- 查询优化相关:enable_hashjoin、enable_seqscan等
- 日志相关:log_directory、log_filename等
- 存储相关:max_wal_size、checkpoint_timeout等
- 安全相关:ssl、password_encryption等
按生效方式分,前面我们讲过,有Internal、Postmaster、Sighup、Backend、Session五个级别。
参数还有个优先级的概念,从高到低依次是:
- 会话级SET命令(最高优先级)
- ALTER DATABASE SET
- ALTER ROLE SET
- kingbase.auto.conf
- kingbase.conf
- 编译时的默认值(最低优先级)
这意味着同样是设置auto_createtblspcdir参数,如果你在会话里用SET命令改了,那它的优先级最高,会覆盖其他所有地方的设置。
配置文件管理的最佳实践
生产环境的参数配置,我建议遵循这几个原则:
原则一:关键参数统一写入配置文件。
不要图省事在会话里SET一下了事,所有的参数调整都要记录在配置文件里,并且纳入版本管理。
# kingbase.conf中的相关配置
auto_createtblspcdir = off
temp_tablespaces = '/ssd_fast/kingbase/temp'
work_mem = '256MB'
原则二:用ALTER SYSTEM做持久化修改。
如果需要在运行时修改参数,优先用ALTER SYSTEM命令,这样会写入kingbase.auto.conf,不会被手动编辑kingbase.conf覆盖,也便于审计。
ALTER SYSTEM SET auto_createtblspcdir = off;
SELECT pg_reload_conf();
原则三:改参数前先在测试环境验证。
这个不用多说了,生产环境无小事,任何参数修改都要先在测试环境跑一遍回归测试。
原则四:建立参数变更记录。
谁在什么时候改了什么参数,为什么要改,改之前是什么值,改之后是什么效果,这些都要记录下来。将来出了问题要回溯,这个记录就是救命稻草。
-- 查看参数历史变更(如果启用了审计日志)
SELECT * FROM sys审计日志 WHERE action LIKE '%SET%';
auto_createtblspcdir的具体配置建议
针对auto_createtblspcdir这个参数,我的建议是:
开发测试环境:保持默认开启(on)。
开发测试环境追求的是便捷和效率,目录自动创建能省不少事。开发人员可以一条SQL完成表空间创建,不用关心目录那些事。
-- kingbase.conf
auto_createtblspcdir = on
生产环境:建议关闭(off)。
生产环境讲究的是可控、可追溯。手动创建目录虽然多了一步,但能让运维人员对存储布局有完全的控制权,心里更踏实。
-- kingbase.conf
auto_createtblspcdir = off
-- 或者用ALTER SYSTEM
ALTER SYSTEM SET auto_createtblspcdir = off;
SELECT pg_reload_conf();
而且关闭之后,如果有人误操作创建了不存在的目录,会直接报错,能更早发现问题。
超级用户权限管理:权限不能滥用
表空间创建、删除这些操作,只能由超级用户执行。这是KES的安全设计,普通用户没有这个权限。
-- 只有超级用户能创建表空间
CREATE TABLESPACE tbs_test LOCATION '/data/test';
-- 普通用户尝试创建会报错
-- ERROR: must be superuser to create tablespace
但是权限大意味着风险也大。生产环境里,超级用户权限不能随便给,更不能滥用。
我的建议是:
第一,超级用户账号要严格管控。
数据库里超级用户越少越好。一般保留2-3个就够了,比如一个给DBA日常维护用,一个给应用连接池用。其他的应用系统账号,不要给superuser权限。
-- 查看所有超级用户
SELECT rolname FROM pg_roles WHERE rolsuper = true;
-- 创建应用用户时不要给superuser
CREATE USER app_user WITH PASSWORD 'xxx';
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
-- 只给必要的表权限,不要给superuser
第二,区分管理角色和应用角色。
不要让一个账号既做管理又跑业务。管理账号用来做表空间创建、用户管理、备份恢复这些操作;应用账号只能读写数据,不能做DDL操作。
-- 创建管理角色
CREATE ROLE dba_role CREATEROLE CREATEDB;
-- 创建应用角色
CREATE ROLE app_role BYPASSRLS;
-- 赋予最小必要权限
GRANT dba_role TO admin_user;
GRANT app_role TO app_user;
第三,启用审计日志,记录所有敏感操作。
谁在什么时候创建了表空间,删除了表空间,修改了什么配置,这些都要记录下来。KES提供了审计插件sysaudit,启用之后所有操作都有日志。
-- 查看审计日志
SELECT * FROM sys_audit_log
WHERE action_statement LIKE '%TABLESPACE%'
ORDER BY recorded_time DESC;
第四,定期审查权限配置。
权限不是给完就完了,要定期检查。看看有没有账号长期不用但权限还在,有没有权限被滥用的情况。
生产环境表空间规划实战
说了这么多理论,不如来看一个实际的例子。
假设我们要为一个ERP系统做表空间规划,这个系统有以下几个模块:
- 基础数据模块:客户、供应商、物料等,访问频繁
- 交易模块:订单、收发货、发票等,数据量大,访问也频繁
- 财务报表模块:各种统计报表,历史数据多,访问相对少
- 文件附件模块:各种上传的文件,大对象,访问很少
我的规划是这样的:
-- 创建目录(假设每块盘已挂载好)
-- SSD1: /ssd1/kingbase
-- SSD2: /ssd2/kingbase
-- SATA: /sata1/kingbase
-- 系统表空间(默认)
-- 使用默认的pg_default
-- 基础数据表空间 - 放SSD1,热数据
CREATE TABLESPACE tbs_erp_base LOCATION '/ssd1/kingbase/erp_base';
-- 交易数据表空间 - 放SSD2,热数据
CREATE TABLESPACE tbs_erp_trade LOCATION '/ssd2/kingbase/erp_trade';
-- 交易索引表空间 - 放SSD1,和基表分开
CREATE TABLESPACE tbs_erp_trade_idx LOCATION '/ssd1/kingbase/erp_trade_idx';
-- 历史交易表空间 - 放SATA,冷数据
CREATE TABLESPACE tbs_erp_history LOCATION '/sata1/kingbase/erp_history';
-- 附件表空间 - 放SATA,大对象
CREATE TABLESPACE tbs_erp_attachment LOCATION '/sata1/kingbase/erp_attachment';
-- 临时表空间 - 放SSD1,大排序用
CREATE TABLESPACE tbs_erp_temp LOCATION '/ssd1/kingbase/erp_temp';
SET temp_tablespaces = 'tbs_erp_temp';
然后在创建表的时候指定表空间:
-- 基础数据表
CREATE TABLE m_customer (
id serial PRIMARY KEY,
name varchar(100),
code varchar(50) UNIQUE
) TABLESPACE tbs_erp_base;
CREATE INDEX idx_customer_code ON m_customer(code) TABLESPACE tbs_erp_base;
-- 订单表(当前月份放热表空间)
CREATE TABLE f_orders (
id bigserial,
order_no varchar(50),
customer_id integer REFERENCES m_customer(id),
order_date date,
amount numeric(12,2)
) PARTITION BY RANGE (order_date);
-- 为最近分区指定热表空间
CREATE TABLE f_orders_2024_q4 PARTITION OF f_orders
FOR VALUES FROM ('2024-10-01') TO ('2025-01-01')
TABLESPACE tbs_erp_trade;
-- 历史订单放冷表空间
CREATE TABLE f_orders_2023 PARTITION OF f_orders
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01')
TABLESPACE tbs_erp_history;
-- 附件表
CREATE TABLE f_attachments (
id serial,
file_name varchar(255),
file_content oid
) TABLESPACE tbs_erp_attachment;
这个规划的好处:
- 热数据和冷数据物理分离,IO互不干扰
- 表和索引分离,同一查询可以并行读写
- 不同业务模块的数据放不同表空间,方便容量分析和扩容
- 分区表结合表空间,实现自动的数据分层
踩坑与避坑指南:老司机血泪经验
最后再给你们总结一些避坑指南,都是我这些年踩过的坑。
避坑一:创建完表空间一定要验证
很多新手以为CREATE TABLESPACE执行成功就完事了,这是不对的。创建完之后一定要验证:
-- 1. 检查表空间是否创建成功
SELECT * FROM pg_tablespace WHERE spcname = 'my_tbs';
-- 2. 检查目录是否真的存在
\! ls -la /data/my_tbs
-- 3. 在表空间里创建一个测试表,插入一条数据
CREATE TABLE test_in_tbs (id int) TABLESPACE my_tbs;
INSERT INTO test_in_tbs VALUES (1);
SELECT * FROM test_in_tbs;
-- 4. 确认没问题后删除测试表
DROP TABLE test_in_tbs;
这套流程跑下来,才算真正验证成功。
避坑二:删表空间一定要清目录
-- 删表空间
DROP TABLESPACE my_old_tbs;
-- 重要!手动清目录
\! rm -rf /data/my_old_tbs
切记这两步缺一不可。
避坑三:主备环境要同步操作
主备切换是常有的事,表空间目录必须在所有节点都准备好。
# 在主库节点
mkdir -p /data/my_tbs
chown kingbase:kingbase /data/my_tbs
chmod 700 /data/my_tbs
# 在备库节点(同样操作)
mkdir -p /data/my_tbs
chown kingbase:kingbase /data/my_tbs
chmod 700 /data/my_tbs
避坑四:不要把表空间放网络盘
NFS之类的网络文件系统延迟高,稳定性差,不适合放表空间。曾经有团队把表空间放到NFS上,结果网络抖动导致数据库间歇性不可用,查了半天原因才发现是NFS的问题。
避坑五:定期检查表空间使用率
-- 查看所有表空间的使用情况
SELECT
spcname,
pg_size_pretty(pg_tablespace_size(oid)) AS total_size,
pg_size_pretty(
pg_tablespace_size(oid) -
pg_tablespace_free_space(oid)::bigint
) AS used_size,
pg_tablespace_free_space(oid)::bigint AS free_bytes
FROM pg_tablespace
ORDER BY pg_tablespace_size(oid) DESC;
超过80%就要告警,提前准备扩容方案。
避坑六:熟悉参数默认值版本差异
V8和V9默认不一样,你的运维手册、部署脚本要根据实际版本调整。
-- 先查当前版本
SHOW server_version;
-- 再查参数值
SHOW auto_createtblspcdir;
总结与展望
好啦,两篇文章终于写完了,我们来回顾一下都聊了些什么。
上篇我们从一次事故讲起,聊了表空间的前世今生、auto_createtblspcdir参数的基本用法、GUC机制、底层实现逻辑和实战场景。
下篇我们聊了表空间管理的最佳实践、存储规划策略、性能调优技巧、GUC参数治理、超级用户权限管理、生产环境规划实战和避坑指南。
总结一下重点:
- auto_createtblspcdir参数控制是否自动创建目录,V9默认开启,V8默认关闭
- 自动创建功能虽然方便,但生产环境建议关闭,保持可控
- 表空间是性能优化的关键,合理规划能大幅提升数据库性能
- 权限、目录、安全是表空间管理的核心,任何时候都不能放松
- 运维规范要建立,监控告警要到位
展望一下未来,随着国产数据库的持续发展,表空间管理这块肯定还会有更多实用的特性出来。比如更智能的存储分层、更细粒度的配额控制、更灵活的在线迁移等等。
但不管技术怎么演进,规范运维、可控管理这个核心理念是不会变的。希望这两篇文章能帮到你们,让你们在用KES的时候少踩坑、不踩坑。
如果有问题,欢迎去金仓官网逛逛,那里有更详细的文档和社区支持。