GBase 8c schema 和 search_path 引发的对象定位问题

GBase 8c schema 和 search_path 引发的对象定位问题

我最近整理 GBase 8c 的开发使用资料时,发现 schema 相关问题很适合单独拿出来讲。很多 SQL 报错看起来像"表不存在"或"权限不足",但真正查下去,根因可能是当前 schema 不对、search_path 不对、对象建到了 $user 模式下,或者同名对象被优先级更高的 schema 命中了。

GBase 8c 文档里提到,默认情况下新的数据库对象会创建在 $user 模式下。这个细节如果没有意识到,就很容易出现一种情况:开发以为表建在公共 schema,实际表在自己用户名对应的 schema;另一个账号执行同样 SQL 时,就找不到对象。schema 本来是为了对象隔离和命名管理,但如果缺少规范,也会变成排查成本。

表不存在,不一定是真的不存在

现场常见报错是:

text 复制代码
relation "order_day" does not exist

很多人看到这个报错会马上查表有没有建。我的习惯是先问:在哪个数据库、哪个用户、哪个 schema 下执行的?同一个表名,如果 schema 不同,结果完全不同。

sql 复制代码
-- 查看当前数据库和用户
SELECT current_database(), current_user;

-- 查看当前 search_path
SHOW search_path;

-- 查找同名对象在哪些 schema 中存在
SELECT schemaname, tablename
  FROM pg_tables
 WHERE tablename = 'order_day'
 ORDER BY schemaname;

如果能查到对象存在,但不在当前 search_path 里,那么 SQL 不写 schema 前缀就可能找不到。更稳的写法是明确 schema:

sql 复制代码
SELECT COUNT(*)
  FROM ods.order_day;

我自己更倾向于在生产 SQL 里写完整对象名,尤其是调度任务、报表任务、数据服务接口。开发临时查询可以依赖 search_path,生产链路尽量不要依赖隐式路径。

search_path 是对象定位规则,不是装饰项

search_path 决定了没有写 schema 前缀时,数据库按什么顺序查找对象。这个参数看起来只是一个默认路径,但它会直接影响 SQL 命中哪个对象。

sql 复制代码
SHOW search_path;

SET search_path TO app_dw, public;

SELECT * FROM order_day LIMIT 10;

如果 app_dw.order_daypublic.order_day 同时存在,当前 SQL 会优先命中 app_dw。这在多环境、多团队共用数据库时很危险。同名表、同名视图、同名函数都可能引发误判。

场景 风险 建议
多 schema 有同名表 SQL 命中非预期对象 生产 SQL 写 schema 前缀
用户级 search_path 不同 不同账号结果不同 统一业务账号配置
应用连接初始化 SET DBA 验证和应用表现不同 查连接池初始化 SQL
临时调试改 search_path 后续会话误用 用完 RESET 或新建连接

我排查对象定位问题时,会让业务用"真实执行账号"连库验证。管理员账号能查到,不代表业务账号也能查到;手工 gsql 能查到,也不代表应用连接池里能查到。

对象创建时要明确 schema

很多问题是从建表那一刻埋下的。比如开发执行:

sql 复制代码
CREATE TABLE order_day (
    order_id bigint,
    order_day date,
    pay_amt numeric(18,2)
);

如果当前 search_path 里默认 schema 是用户自己的 schema,表就会建到那里,而不是团队以为的 odsapp_dw。后续调度账号查 ods.order_day 找不到,或者直接查 order_day 命中另一张表,问题就出来了。

我更建议在 DDL 中明确 schema:

sql 复制代码
CREATE SCHEMA IF NOT EXISTS ods;

CREATE TABLE ods.order_day (
    order_id bigint,
    order_day date,
    pay_amt numeric(18,2)
);

同时,授权也要跟 schema 对齐:

sql 复制代码
GRANT USAGE ON SCHEMA ods TO rpt_reader;
GRANT SELECT ON ods.order_day TO rpt_reader;

很多权限问题不是表没授权,而是 schema 没有 USAGE 权限。业务账号没有 schema 使用权限时,即使表权限看起来给了,也可能访问失败。

用户级和库级配置要纳入排查

search_path 可以在会话里设置,也可能通过用户级或数据库级配置影响新连接。排查时只看当前会话,有时候不够。

sql 复制代码
-- 查看数据库级配置
SELECT datname, datconfig
  FROM pg_database
 WHERE datconfig IS NOT NULL;

-- 查看用户级配置
SELECT rolname, rolconfig
  FROM pg_roles
 WHERE rolconfig IS NOT NULL;

如果发现某个业务用户配置了固定 search_path,就要确认它是否符合当前发布规范。比如原来对象都在 public,后来迁到 app_dw,但用户级配置没改,应用就会继续按旧路径找对象。

配置示例:

sql 复制代码
ALTER USER rpt_reader SET search_path TO app_dw, public;
ALTER DATABASE reportdb SET search_path TO app_dw, public;

我不建议随意在多个层级同时设置。最好明确一套规则:生产调度账号统一用户级 search_path,关键 SQL 仍写 schema 前缀;临时开发账号可以更灵活,但不能影响正式链路。

同名函数也会带来隐性问题

schema 问题不只影响表,也影响函数。比如两个 schema 下都有同名函数,参数类型又能匹配,SQL 可能调用到非预期版本。

sql 复制代码
SELECT routine_schema, routine_name
  FROM information_schema.routines
 WHERE routine_name = 'format_channel_code';

如果业务 SQL 里写:

sql 复制代码
SELECT format_channel_code(channel_code)
  FROM app_dw.order_day;

到底调用哪个函数,就要看路径和解析规则。生产里我更建议对关键自定义函数写 schema 前缀:

sql 复制代码
SELECT dim.format_channel_code(channel_code)
  FROM app_dw.order_day;

这看起来啰嗦,但在多人协作和长期维护里更稳。

我会固定保留的排查 SQL

sql 复制代码
-- 当前身份和路径
SELECT current_database(), current_user;
SHOW search_path;

-- 查同名表
SELECT schemaname, tablename
  FROM pg_tables
 WHERE tablename = '目标表名'
 ORDER BY schemaname;

-- 查同名视图
SELECT table_schema, table_name
  FROM information_schema.views
 WHERE table_name = '目标视图名'
 ORDER BY table_schema;

-- 查 schema 权限相关对象
SELECT nspname
  FROM pg_namespace
 WHERE nspname NOT LIKE 'pg_%'
 ORDER BY nspname;

如果是应用侧问题,我还会加一条:请应用把连接初始化 SQL 打出来。很多时候应用框架会自动执行 SET search_path,数据库侧手工验证不一定能复现。

常见坑

常见坑 表现 处理建议
建表不写 schema 表建到非预期 schema DDL 统一写完整对象名
只授权表不授权 schema 表权限看似有,访问仍失败 GRANT USAGE ON SCHEMA
多 schema 同名对象 同样 SQL 不同账号结果不同 生产 SQL 写 schema 前缀
用户级 search_path 旧值 应用找旧对象 pg_roles.rolconfig
应用连接池重设路径 DBA 手工验证正常,应用异常 查初始化 SQL
函数未加 schema 前缀 调用到旧函数 关键函数写完整名

结尾总结

GBase 8c 的 schema 和 search_path 本身是对象管理能力,不是问题来源。但如果没有命名和授权规范,它们很容易让"表不存在""权限不足""结果不一致"变得难排查。我的经验是,生产 DDL 和核心 SQL 尽量写完整 schema,业务账号的 search_path 统一管理,权限同时覆盖 schema 和对象。

排查时不要只问表有没有,而要问表在哪个 schema、当前账号路径是什么、是否存在同名对象、应用连接是否改过路径。把这些问题问清楚,对象定位类问题通常很快就能收敛。

参考资料

text 复制代码
[1] GBase 8c 数据库使用 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
[2] GBase 8c GUC参数说明 https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/GUC%E8%BF%90%E8%A1%8C%E5%8F%82%E6%95%B0
[3] GBase 社区优质文章区 https://www.gbase.cn/community/section/11
相关推荐
小猿姐18 小时前
Clickhouse Kubernetes Operator 实测:哪种方案更适合生产?
运维·数据库·kubernetes
2501_9219392619 小时前
MHA高可用
数据库·mysql
_Evan_Yao19 小时前
MySQL 基础:SELECT、WHERE、JOIN 的第一次使用
数据库·mysql
weixin_4440129320 小时前
c++如何将std--vector直接DUMP到二进制文件_指针地址直写【附代码】
jvm·数据库·python
woxihuan12345620 小时前
Go语言中--=运算符详解:位右移赋值操作的原理与应用
jvm·数据库·python
java1234_小锋20 小时前
SpringBoot为什么要禁止循环依赖?
java·数据库·spring boot
神仙别闹21 小时前
基于QT(C++)实现学生成绩管理系统
数据库·c++·qt
m0_6908258221 小时前
如何备份被破坏的数据表_强制跳过错误的导出尝试
jvm·数据库·python
tongyiixiaohuang21 小时前
轻易云平台助力快麦数据入库MySQL
android·数据库·mysql
残 风21 小时前
快速理解什么是MVCC?
数据库·postgresql·oracle·数据库开发