引言
上次帮一个做政务系统的朋友排查了一个生产故障,折腾了快大半天,最后问题出在一个绝大多数人都不会注意的小参数auto_createtblspcdir上。翻遍了全网能找到的资料,不是官方文档一句话带过,就是复制粘贴的基础用法,没人说清楚这个参数底层逻辑是什么,会踩什么坑,结合这次故障,我把KingbaseES(下文简称KES)表空间管理、目录权限控制、GUC参数配置这些相关内容整理了一遍,不管你是刚接触KES的新手DBA,还是正在做迁移的老运维,相信都能有收获。
一、为什么KES的表空间管理值得我们花时间深挖?
我一直觉得,存储管理是数据库稳定的根,表空间就是存储管理的核心抓手。尤其是现在信创落地进入深水区,绝大多数企业都在做国产服务器+国产数据库的替换,新服务器基本都是多挂载磁盘,不同性能的存储介质混插:核心业务用SSD,冷数据用大容量SATA盘,日志存储用廉价机械盘,要实现物理隔离、IO负载均衡,全靠表空间来做。
KES作为目前国内市场占有率最高的自主企业级关系型数据库,底层基于PostgreSQL演进,但是在表空间这块做了不少自研的改造,新增了很多适配企业级需求和Oracle兼容的特性,auto_createtblspcdir就是其中一个非常实用但容易踩坑的参数。我接触过不下十个DBA,刚用KES的时候都在这个参数上栽过跟头:有的是创建表空间不报错,插数据的时候才报错找不到目录;有的是自动创建出来的目录权限不对,直接导致库起不来;还有的是删了表空间忘记清物理目录,半年后磁盘满了才发现问题。
今天我就从auto_createtblspcdir这个小参数切入,把KES表空间机制、目录权限控制、存储空间规划、GUC参数治理这些内容全部讲透,从底层实现到生产最佳实践,全是干货。

二、核心基础:auto_createtblspcdir到底是什么?底层设计逻辑讲透
很多人第一次看到这个参数都是在postgresql.conf的注释里,官方解释只有一句话:是否自动创建表空间对应的物理目录,看起来简单,实际上背后的设计逻辑、使用场景都有讲究,我们一步步说。
2.1 这个参数为什么会存在?设计初衷是什么
KES最核心的一个优势就是Oracle兼容性好,很多从Oracle迁移过来的DBA都知道,Oracle创建表空间的时候,只需要你指定数据文件的路径,不需要提前手动建好目录,Oracle会自动帮你创建不存在的目录和文件。
而原生PostgreSQL的逻辑是:你创建表空间的时候,必须提前手动把物理目录建好,还要把权限给对,否则CREATE TABLESPACE直接报错,根本不让你继续。这个差异对于从Oracle迁到KES的DBA来说非常不友好,很多人刚上手都会在这里卡住,为了兼容Oracle的使用习惯,降低迁移门槛,KES专门加了auto_createtblspcdir这个GUC参数。
简单来说,这个参数就是用来控制:当你执行CREATE TABLESPACE的时候,如果指定的LOCATION物理目录不存在,KES要不要自动帮你创建这个目录。参数是bool类型,只有两个值:on和off。
这里有一个很多人不知道的点:不同KES大版本的默认值不一样,我测过目前主流的两个版本:
- KES V8:默认值是
off,也就是保持原生PostgreSQL的逻辑,要求你提前手动建目录 - KES V9:默认值改成了
on,默认开启自动创建,兼容Oracle的使用习惯
我接触过好几个新手,就是因为没注意这个默认版本差异,照着旧文档操作,结果踩了坑,所以大家拿到新环境第一件事,先执行命令看一下当前参数值:
sql
show auto_createtblspcdir;
不要想当然用默认值。
2.2 基于GUC机制的参数配置与生效逻辑
既然说到参数,我们这里延伸讲一下KES的GUC(Grand Unified Configuration)参数管理机制,这是KES所有参数配置的核心基础,很多人对GUC一知半解,改参数改错了还不知道为什么。
KES的GUC机制是从PostgreSQL延续过来的,核心作用就是统一管理所有系统参数,所有参数都可以通过postgresql.conf配置,也支持动态修改,KES把所有参数分成了5个级别,不同级别的参数生效规则不一样:
| 参数级别 | 生效规则 | 举例 |
|---|---|---|
| Internal级 | 内核编译时写死,不能修改 | block_size |
| Postmaster级 | 只有重启数据库实例才能生效 | max_connections |
| Sighup级 | 修改配置文件后执行pg_reload_conf()就能动态生效,不需要重启 |
auto_createtblspcdir |
| Backend级 | 现有连接不生效,新建立的连接自动加载新值 | search_path |
| Session级 | 当前会话直接可以修改,只对当前会话生效 | nls_date_format |
划重点:auto_createtblspcdir属于Sighup级参数,不需要重启数据库就能生效,我自己做的测试记录给大家参考:
sql
-- 初始状态,V8默认off
show auto_createtblspcdir;
auto_createtblspcdir
-----------------------
off
(1 row)
-- 修改postgresql.conf,把参数改成on,保存退出
-- 执行重载配置
select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
-- 再次查看,已经生效,不需要重启
show auto_createtblspcdir;
auto_createtblspcdir
-----------------------
on
(1 row)
这个特性非常方便,比如你生产环境要开这个参数,直接改配置重载就行,不用停库,对业务没有影响。
2.3 底层实现逻辑:自动创建目录到底是怎么执行的?
我当时为了排查那个故障,专门翻了KES的开源代码,把整个流程理清楚了,这里给大家说清楚核心逻辑,就能理解为什么会有坑:
当你执行CREATE TABLESPACE ... LOCATION 'xxx'语句,流程是这样的:
- 首先校验LOCATION路径的合法性,KES只支持绝对路径,不支持相对路径,如果写相对路径直接报错
- 检查路径对应的目录是否已经存在:
- 如果目录已经存在:校验目录权限,校验通过就把表空间元信息写入系统表
pg_tablespace,创建完成 - 如果目录不存在:检查
auto_createtblspcdir参数,如果参数是off,直接报错could not create directory "xxx": No such file or directory,创建失败;如果参数是on,尝试用KES运行用户(一般是kes用户,不是root)调用系统mkdir创建目录
- 如果目录已经存在:校验目录权限,校验通过就把表空间元信息写入系统表
- 核心坑点在这里:如果尝试自动创建目录失败(比如权限不够),KES不会在
CREATE TABLESPACE阶段报错,只会把错误打在系统日志里,仍然会把表空间的元信息写入系统表,返回创建成功给用户 - 只有当你第一次往这个表空间的表插入数据,需要创建数据文件的时候,才会再次尝试访问目录,找不到目录才会报错,这个时候你才会发现出问题了。
这个设计逻辑怎么说呢,我一开始觉得挺反直觉的,创建失败为什么不直接报错?后来和金仓的内核朋友聊了下,才理解这个设计的考量:表空间只是一个逻辑容器,创建的时候不需要实际写数据,就算目录暂时有问题,你后续可以手动修复目录之后再用,不需要删掉重建,给用户留了灵活处理的空间。但对于运维来说,这个设计确实非常容易踩坑,我那个老伙计就是遇到了这个问题,创建成功了以为没问题,第二天业务上线插数据才报错,差点误了上线时间。
三、KES表空间机制深度解析:物理存储隔离与性能调优
讲完了参数,我们再整体梳理一下KES的表空间机制,很多人用了很久KES,只知道表空间是用来放表的,不知道怎么用它做性能优化,这里讲透。
3.1 KES表空间的核心本质是什么
表空间本质上就是一个逻辑存储容器,对应物理磁盘上的一个独立目录,所有你指定放到这个表空间的对象:表、索引、大对象、数据文件,都会存在这个目录下。
和原生PostgreSQL比,KES在表空间这块做了几个非常实用的企业级增强:
- 支持表空间配额:可以限制整个表空间最多能用多少存储空间,避免单个业务把磁盘占满影响整个实例,这个原生PG是没有的,非常适合多租户的场景
- 更好的大文件支持:原生PG单个数据文件最大是1GB,KES支持最大16GB的单个数据文件,对于大表来说,减少了文件句柄的开销,性能更好
- 兼容Oracle的表空间操作习惯:很多Oracle的语法,比如
ALTER TABLE ... MOVE TABLESPACE,KES都原生支持,不需要改逻辑,迁移方便
3.2 表空间的核心应用场景
我做了这么多项目,总结下来,生产环境用表空间核心就是三个场景:
(1)物理存储分离,隔离IO负载
这个是最常用的场景,现在企业的服务器基本都是混插不同性能的存储介质:
- 核心业务的热数据(比如交易表、用户表)放到SSD盘的自定义表空间,保证低延迟
- 冷数据(比如三年前的历史订单、日志)放到大容量SATA盘的冷数据表空间,降低存储成本
- 索引和表分开:把所有索引单独放到另一个SSD表空间,避免表的IO和索引的IO竞争
- 系统日志、wal日志放到单独的表空间/分区,避免业务数据把磁盘占满,导致数据库无法写wal宕机
我给大家拿我们公司核心交易系统举个实际的例子,原来我们所有数据都放在一个表空间,全部在一块SSD上,峰值的时候IO使用率能跑到92%,IO平均等待时间是118ms,业务高峰期经常有超时报错。后来我们做了拆分:
- 核心交易表:SSD表空间
- 二级索引:单独一块SSD表空间
- 历史订单:SATA盘冷表空间
- wal日志:单独的SSD分区
拆分完之后,峰值IO使用率最高降到了35%,IO平均等待时间降到了22ms,业务超时率从原来的0.12%降到了0,性能提升非常明显,这个就是表空间做IO隔离的价值。
(2)在线扩展存储空间
当数据库根目录所在的磁盘满了,不需要重新分区,也不需要重启数据库,只要挂载新的磁盘,然后在新磁盘上新建一个表空间,把部分大表移过去就能完成扩展,非常方便,对业务完全无感知。
(3)多租户权限隔离
不同业务的用户,分配不同的表空间,还可以给每个表空间设置配额,限制最大使用空间,避免一个业务把整个存储占满,影响其他业务,非常适合SaaS或者多部门共用数据库实例的场景。
3.3 表空间性能调优的几个实用技巧
我整理了几个生产环境验证过的调优技巧,都是能实实在在提升性能的:
- 不要把所有数据都放在默认的pg_default表空间:默认表空间和系统表放在一起,业务数据大了会影响系统表的访问性能,一定要给业务数据新建独立的表空间
- 大表分区+表空间结合:如果你用分区表存按时间递增的数据,可以把每个季度的分区放到对应表空间,最新的分区放SSD,历史分区放冷盘,既保证性能又省成本,我们公司就是这么用的,存储成本降了60%
- 一个表空间对应一个独立的磁盘分区:不要多个表空间共用一个分区,否则没法做到真正的IO隔离,也不好做磁盘限额
- 不要把表空间建在系统盘:系统盘如果满了会直接导致服务器宕机,一定要把业务表空间放到数据盘,这个是常识但是很多新手踩坑
- 顺序写多的业务(比如日志),可以把块大小调整成更大的值:KES支持自定义块大小,如果你的业务顺序写多,可以把表空间对应的块大小设成8K或者16K,比默认的4K性能更好。
四、目录权限控制:最容易踩坑的环节,核心规则整理
auto_createtblspcdir出问题,90%都是出在目录权限上,我整理了生产环境遇到的所有权限坑,把核心规则给大家列出来,只要照着做,就能避开99%的问题。
4.1 目录权限的核心强制规则
KES对表空间目录的权限有非常严格的要求,不符合要求直接不让用,核心规则:
- 表空间目录的属主必须是KES的运行用户(一般是kes:kes),如果属主不对,就算权限开的再大,也会报错
- 表空间目录的权限必须是700,也就是只有属主有读写执行权限,开755或者777都会报错,因为KES认为权限开太大是安全风险,会拒绝访问,我第一次建表空间的时候就踩了这个坑,给了755,死活创建不成功,查了半天才知道这个规则
- 自动创建目录的时候,父目录必须给kes用户写权限 :如果你的表空间目录是
/data/tbs/biz1,要自动创建biz1,那么/data/tbs这个父目录必须给kes用户写权限,否则自动创建肯定失败,这个就是我开头说的那个故障的核心原因。
4.2 常见权限坑点排查
我整理了几个最常见的坑,给大家说怎么解决:
坑1:自动创建目录失败,CREATE TABLESPACE不报错,插数据才报错
这个就是我们之前说的,核心原因就是父目录权限不对,kes用户没法创建子目录,报错只会打在系统日志里,不会返回给客户端。排查方法:
- 去看KES的系统日志,一般在
$KINGBASE_HOME/log/下面,搜could not create directory就能看到具体的权限错误 - 检查你指定的目录是不是存在,
ls -ld 你的目录路径,看属主和权限对不对 - 如果目录不存在,检查父目录的权限,给kes用户加写权限:
chown kes:kes 父目录路径,然后手动创建目录mkdir 你的目录路径,chown kes:kes 你的目录路径,chmod 700 你的目录路径,修复完就能正常用了。
坑2:软链接能不能用?怎么用才对
很多人跨磁盘扩展的时候,喜欢用软链接把表空间目录指向别的磁盘,问我KES支持吗?我可以明确说,支持,生产环境用了两年多没问题,但是要注意两个点:
- 软链接本身的属主必须是kes用户,权限也要对
- 软链接指向的实际目录的属主和权限也要符合规则,两个都对才能用
我之前遇到过,软链接权限对了,实际目录权限不对,还是报错,所以两个都要检查。
坑3:删除表空间之后磁盘空间没释放
很多人都不知道,KES删除表空间的时候,只会删掉系统表里面的元信息,不会自动删掉物理目录和里面的文件,所以你删了表空间之后,一定要手动去删掉对应的物理目录,不然会一直占磁盘空间,我见过一个客户,删了十几个表空间没清目录,一年后磁盘满了才发现,这个坑一定要记住。
坑4:NFS共享存储做表空间需要注意什么
很多企业用共享存储做数据备份或者存冷数据,把表空间建在NFS上,是可以用的,但是要注意三个点:
- NFS挂载的时候要加上
noatime参数,关闭访问时间更新,提升IO性能 - NFS的权限要配置对,必须允许kes用户读写,很多NFS配置了root squash,kes的uid会被映射成nobody,导致权限不够,这个要提前改NFS配置
- 核心业务的热数据不要放NFS,NFS的网络延迟比本地磁盘高很多,性能会差很多,只适合放冷数据。
4.3 超级用户与路径管理规范
创建表空间这个操作,默认只有超级用户才能执行,很多人问能不能给普通DBA授权?可以,执行下面的语句就行:
sql
GRANT CREATE TABLESPACE TO 用户名;
但是我不建议在生产环境给普通用户开这个权限,表空间涉及到存储规划和权限,随便创建容易把整个存储搞乱,生产环境最好只有专职DBA才有创建表空间的权限,这个是规范,能避免很多问题。
路径管理还有一个点:KES只支持绝对路径,不支持相对路径,如果你写./data/biz1,直接报错,一定要写完整的绝对路径,比如/opt/kes/data/tbs_biz1,这个也是新手常犯的错。
五、GUC参数治理:参数冲突与最佳配置建议
讲完了权限,我们再回到参数本身,讲一下GUC参数冲突和生产环境的参数配置建议。
5.1 常见的参数冲突场景
auto_createtblspcdir本身不会有什么冲突,但是如果和其他GUC参数配合不对,就会出问题,我整理了两个最常见的冲突场景:
场景1:auto_createtblspcdir开启,但是data目录挂载为只读
很多企业为了安全,会把KES的data目录挂载成只读,这个时候你新建表空间,开启自动创建,肯定没法创建目录,报错Permission denied,这个就是典型的参数和环境配置冲突,遇到这种情况,必须提前手动建好目录,然后把auto_createtblspcdir关掉,或者把表空间目录建在可写的挂载点上。
场景2:default_tablespace指向了不存在的表空间
很多人会把默认表空间改成自定义的,这样新建表不用指定表空间就自动放到自定义表空间,但是如果你不小心把默认表空间删了,或者目录出问题,那么所有新建表都会报错,这个也是常见的配置错误,改默认表空间之前一定要确认表空间是正常可用的。
5.2 生产环境参数配置建议:auto_createtblspcdir到底开不开?
这个问题我被问过很多次,我的结论是:看场景,生产环境规范运维建议关掉,理由如下:
- 开启自动创建虽然方便,但是把权限检查和目录创建的过程隐藏了,出问题不能提前发现,等到写数据的时候才报错,影响业务
- 手动创建目录的过程,其实就是一次提前检查:你可以提前检查磁盘空间够不够,权限对不对,父路径对不对,提前把问题解决了,避免后续出问题
- 生产环境的存储规划都是提前做好的,表空间都是提前建好的,不需要自动创建的便利性
当然,也不是说所有场景都要关,如果你是测试环境,或者刚上手KES的新手,想要方便,可以开,但是一定要把父目录的权限给对,而且要在创建完表空间之后,手动验证一下目录存在,权限对,没问题再用。
5.3 GUC参数治理的最佳实践
我整理了整个KES GUC参数治理的最佳实践,不止是auto_createtblspcdir,所有参数都适用:
- 不要随便改默认参数:KES的默认参数都是官方根据大多数场景优化过的,除非你明确知道改了之后有什么好处,否则不要乱改
- 所有自定义修改都要记录:建一个参数配置文档,所有改了的参数都记下来,为什么改,改了之后的值是什么,方便后续排查问题
- 修改参数先在测试环境验证:任何参数修改,都先在测试环境测一遍,没问题再上生产,不要直接在生产改
- 修改完参数一定要验证生效 :改完之后执行
show 参数名,确认值已经改对了,不要改了配置文件没重载,白忙活 - 可以用系统表查所有参数信息,想要看所有参数的当前值、默认值、上下文,可以用这个sql查询:
sql
SELECT name, setting, boot_val, context FROM pg_settings WHERE name LIKE '%tablespace%';
非常方便,能快速看到所有表空间相关的参数配置。
六、完整生产故障排查实战:我帮朋友解决表空间问题的全过程
讲了这么多理论,给大家放一个完整的实战案例,就是我开头说的那个故障,整个排查过程给大家理清楚,大家以后遇到一样的问题就能直接照着解决。
6.1 故障背景
朋友的公司做政务系统,刚完成核心系统从Oracle到KES V8的迁移,准备上线,需要给12个业务线每个建一个独立的表空间。他们提前查了文档,知道V9默认开auto_createtblspcdir,以为V8也默认开,就把参数改成了on,重载配置生效了。
然后他们用root用户登录服务器,在/data目录下给已经有的11个业务线提前建了目录,最后一个业务线(biz12)忘了建,想着参数开着会自动创建,就直接执行了创建语句:
sql
CREATE TABLESPACE biz12 OWNER biz12 LOCATION '/data/biz12';
执行完返回成功,没有报错,所有人都以为没问题,就准备上线,结果往biz12的表里插数据的时候,直接报错:
ERROR: could not create file "/data/biz12/16423/345678": No such file or directory
当时离上线不到两个小时,所有人都慌了,找我过去排查。
6.2 排查过程
我第一步先登到数据库,看表空间有没有创建成功:
sql
SELECT * FROM pg_tablespace WHERE spcname = 'biz12';
查到了,表空间元信息已经存在,说明创建成功了,那为什么找不到文件?
然后我去看物理目录:
bash
ls -ld /data/biz12
ls: cannot access /data/biz12: No such file or directory
果然目录不存在,那为什么自动创建没创建出来?参数不是开了吗?
然后我看父目录/data的权限:
bash
ls -ld /data
drwxr-xr-x. 3 root root 4096 Jul 10 10:24 /data
哦,问题找到了,/data的属主是root,权限是755,KES是用kes用户运行的,kes用户对/data只有读和执行权限,没有写权限,所以没法在/data下面创建biz12目录,自动创建失败。
然后我去看KES的系统日志,果然找到了错误日志:
2025-08-12 14:32:15 CST [12345]: [1-1] ERROR: could not create directory "/data/biz12": Permission denied
和我们猜的一样,错误打在了日志里,创建表空间的时候没返回给客户端,所以大家都没发现。
6.3 解决过程
问题找到了,解决就很简单了,两步:
- 手动创建目录,授权正确:
bash
mkdir /data/biz12
chown kes:kes /data/biz12
chmod 700 /data/biz12
- 验证插入数据,成功,问题解决,顺利上线。
6.4 故障总结
这次故障总结下来,三个坑点:
- 没注意不同版本
auto_createtblspcdir的默认值不一样,改参数的时候没验证,虽然这次改对了,但是反映了很多人改参数不验证的问题 - 不知道自动创建失败不会在创建阶段报错,只有写数据才会报错,所以没提前发现问题
- 父目录权限没给对,导致自动创建失败,这个是最核心的原因。
这次故障之后,他们把所有表空间都检查了一遍,把auto_createtblspcdir关掉了,以后所有表空间都手动提前建目录,再也没出过类似的问题。
七、KES存储空间运维最佳实践总结
我把生产环境这么多年总结的表空间和存储运维最佳实践整理出来,大家照着做就能避开绝大多数坑:
(1)参数配置
- 生产环境关闭
auto_createtblspcdir,所有表空间提前手动创建目录,提前检查权限和磁盘空间 - 测试环境或者开发环境可以开,方便开发使用,但是也要把父目录权限给对
- 任何参数修改之后都要执行
show验证生效,不要改完就不管了
(2)目录权限规范
- 所有表空间目录的属主必须是
kes:kes,权限必须是700,符合KES的安全要求 - 自动创建场景下,父目录必须给kes用户写权限,否则创建失败
- 删除表空间之后一定要手动删除物理目录,释放磁盘空间
(3)存储规划
- 业务数据一定要拆分到独立的自定义表空间,不要都放在默认的
pg_default - 按照数据热度分表空间:热数据放SSD,冷数据放SATA,索引和表分离,wal日志和业务数据分离,实现IO隔离,提升整体性能
- 一个表空间对应一个独立的磁盘分区,不要多个表空间共用分区,保证真正的IO隔离
- 预留至少30%的磁盘空间,不要把磁盘用满,提前监控表空间使用率,提前扩容
(4)运维规范
- 只有超级DBA才有创建表空间的权限,不要随便给普通用户开权限
- 新建表空间之后一定要提前验证:创建一个测试表,插入一条数据,确认能正常读写,没问题再给业务用
- 所有表空间配置要做好文档记录,记录对应业务、存储位置、磁盘大小、配额,方便后续维护
八、常见问题FAQ
我整理了大家问的最多的几个问题,统一解答:
- 怎么把已经存在的表移到另一个表空间?
sql
-- 移动单表
ALTER TABLE 表名 SET TABLESPACE 新表空间名;
-- 移动索引
ALTER INDEX 索引名 SET TABLESPACE 新表空间名;
-- 移动整个表空间所有表
SELECT 'ALTER TABLE ' || relname || ' SET TABLESPACE 新表空间;' FROM pg_class WHERE reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = '旧表空间');
把查询结果拿出来执行就行。
-
表空间满了怎么办?
两种方法:如果是挂载的LVM磁盘,可以直接扩展磁盘,扩展完不需要改表空间,直接用;如果磁盘没法扩展,新建一个表空间在新磁盘上,把部分大表移过去就行。
-
删除表空间提示"tablespace X is not empty"怎么办?
说明还有对象在这个表空间里面,先把所有对象移走或者删掉,再删表空间,可以用这个sql查有哪些对象在里面:
sql
SELECT relname FROM pg_class WHERE reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = 'X');
-
auto_createtblspcdir支持在已经存在的实例上修改吗?
支持,因为是Sighup级,改完重载配置就生效,不需要重启。
-
表空间能不能设置配额?怎么设置?
KES支持表空间配额,创建的时候指定就行:
sql
CREATE TABLESPACE biz1 LOCATION '/data/biz1' WITH (quota 100GB);
超过配额就不能再写数据了,非常适合多租户场景。
结尾
说了这么多,其实auto_createtblspcdir就是一个很小的参数,但是牵出了KES表空间管理、权限控制、GUC参数治理一整套存储管理的内容,国产数据库用起来其实没有那么难,就是很多小细节没人说透,踩坑都是踩在小细节上。
现在国产数据库已经不是当年"能用就行"的状态了,不管是功能还是稳定性,都能满足企业级核心业务的需求,就是很多运维细节需要大家慢慢摸索,我把这些踩坑经验整理出来,就是希望能帮大家少走点弯路。