GBase 8a
运维管理
文章
精选

GBase 8a 存储过程的执行身份与权限链风险

发表于2026-04-07 10:18:42176次浏览5个评论

GBase 8a 存储过程的执行身份与权限链风险

我最近看 GBase 8a 相关资料时,越来越明显的一个感受是:很多现场里存储过程出问题,并不是语法没写对,而是对象能创建、也能编译通过,等真正交给业务账号调用时,才开始暴露出权限链、执行身份和对象变更带来的连锁问题。

这类问题很容易被误判成“数据库偶发异常”或者“用户授权没生效”。但我自己整理下来觉得,GBase 8a 里存储过程最难缠的地方,不在过程体本身,而在它背后的执行上下文。尤其是多人协作、测试库向生产迁移、账号改名、对象归属调整这几类场景,只要前面设计得不规整,后面就特别容易出现“过程看得到但不能调”“能调过程却查不到表”“改了用户名后过程失效”这类现场问题。

真正落到现场时,我一般不会上来就反复 grant。我更关注三件事:过程是谁定义的、调用时到底按谁的权限执行、过程依赖的对象和账号后来有没有变更过。把这三件事拆清楚,排查效率会高很多。

先把问题拆开看

GBase 8a 里的存储过程问题,表面现象经常很像,但根因并不一样。按我自己的排查习惯,至少要先分成下面几类。

现场现象 常见根因 第一优先检查点
过程创建成功,但业务账号 CALL 失败 缺少 EXECUTE 权限,或执行身份不是预期 GRANT EXECUTESQL SECURITY
调过程能进入,但过程内访问表报权限错误 定义者/调用者权限边界没理顺 过程定义、依赖表授权
测试正常,换账号或迁移后失效 DEFINER 失配、依赖对象归属变化 SHOW CREATE PROCEDURE、系统表
用户名修改后,过程不可见或不可用 过程中的定义者仍指向旧账号 gbase.proc
同样一套脚本,不同环境执行结果不同 默认库、调用入口、权限模型不一致 过程归属库、调用方式

我最近整理下来觉得,这类问题最容易踩坑的误区,是把“谁创建了过程”和“谁在执行过程”当成同一件事。实际上这两件事在 GBase 8a 里是可以分开的,而且一旦分开,就会牵出完整的权限链问题。

我更关注的其实是 SQL SECURITY

从落地角度看,存储过程真正容易埋雷的点不是 BEGIN ... END,而是 SQL SECURITY。GBase 8a 支持 SQL SECURITY DEFINERSQL SECURITY INVOKER,默认是 DEFINER。这意味着,过程跑起来时,到底按定义者权限还是调用者权限执行,行为可能完全不同。

DEFINER 和 INVOKER 的区别

选项 执行时采用的权限主体 适合场景 风险点
SQL SECURITY DEFINER 过程定义者 固化统一的数据访问入口 定义者变更、账号失效、环境迁移后容易出问题
SQL SECURITY INVOKER 过程调用者 权限边界清晰、便于分环境控制 调用账号需要具备过程访问对象的权限

我自己更倾向于把这两个模式分开使用:

  • 面向业务应用的标准化查询入口,如果希望屏蔽底层表结构,可以考虑 DEFINER
  • 面向运维脚本、批处理任务或者多租户隔离场景,我个人更倾向于 INVOKER,因为后续排障更直接。

很多问题恰恰出在这里:开发阶段用 DBA 或高权限账号建了过程,测试时一切正常;上线后换成业务账号调用,表面上过程有 EXECUTE 权限,但过程体里访问的对象实际上还是靠定义者权限兜底。一旦定义者账号被调整、禁用、改名,问题就全冒出来了。

一类很容易忽略的风险:账号变了,过程没跟着变

我看到社区里就有一个很典型的例子:用户通过 rename 改了账号名,但原账号创建的存储过程在新账号下无法正常查看和使用,最后定位到系统表里的 definer 仍然是旧值。

这个现象特别像现场常见的“授权明明加了,为什么还是不行”。如果只盯着库级权限和表级权限,很容易转半天都找不到根因。因为过程对象本身保存的定义者信息并不会自动跟着用户改名同步。

我实际排查时一般先看:

SHOW CREATE PROCEDURE app_core.proc_sync_order;

如果输出里能明确看到 DEFINER,那后面的方向就清楚了。再进一步,可以核对系统表中的过程元数据。

SELECT db, name, definer
FROM gbase.proc
WHERE db = 'app_core'
  AND name = 'proc_sync_order';

如果已经确认是账号改名或归属迁移造成的失配,处理思路通常有两种:

处理方式 适合场景 风险
重新按目标账号重建过程 规范治理、适合正式修复 需要重新发版或执行 DDL
修正系统表中的 definer 信息 应急恢复、时间窗口很紧 必须严格评估,变更前后都要校验

应急处理时,有些现场会直接调整系统表,例如:

UPDATE gbase.proc
SET definer = 'svc_app@%'
WHERE db = 'app_core'
  AND name = 'proc_sync_order';

我个人更关注的是,这种动作一定不能只停留在“改完能跑就行”。后面至少还要补三步:

FLUSH PRIVILEGES;
SHOW CREATE PROCEDURE app_core.proc_sync_order;
CALL app_core.proc_sync_order(...);

同时要补做依赖对象校验,否则过程本身恢复了,里面引用的表、视图、函数如果还挂在旧权限链上,业务一样会报错。

过程能执行,不代表权限链完整

还有一类问题也很常见:业务账号已经拿到了过程的 EXECUTE 权限,但执行到过程内部语句时还是报权限不足。这个时候我不会只看过程授权,而是会把权限链按“入口权限”和“对象权限”分开检查。

先看入口权限

GRANT EXECUTE ON PROCEDURE app_core.proc_sync_order TO 'svc_job'@'%';

再看过程内部依赖对象

如果过程定义为 SQL SECURITY INVOKER,那么调用账号本身必须具备过程体里涉及对象的访问权限,例如:

GRANT SELECT, INSERT, UPDATE ON app_core.order_stage TO 'svc_job'@'%';
GRANT SELECT ON app_core.dim_store TO 'svc_job'@'%';

如果过程是 SQL SECURITY DEFINER,那就要确认定义者账号是否仍然有效、是否还保留对象权限。

我自己更喜欢用一张表把现场逻辑先摊开:

检查层级 具体内容 常见误判
过程级 是否有 EXECUTE 权限 只授过程执行权限,忽略对象访问链
对象级 表、视图、函数是否已授权 误以为定义者权限一定长期可用
账号级 定义者账号是否存在、是否改名 账号重命名后遗漏过程归属
环境级 当前库、VC、调用方式是否一致 测试脚本省略库名,上线后跑偏

现场里更稳的写法,我一般这样收敛

如果一个过程要长期在线上跑,我自己更倾向于让它的归属、权限和依赖关系尽量“可读、可迁移、可审计”。下面这种写法虽然不花哨,但现场稳定性通常更好。

DELIMITER //
CREATE DEFINER = 'svc_proc'@'%'
PROCEDURE app_core.proc_sync_order(IN p_batch_id VARCHAR(32))
SQL SECURITY DEFINER
BEGIN
    INSERT INTO app_core.order_stage_hist
    SELECT *
    FROM app_core.order_stage
    WHERE batch_id = p_batch_id;

    UPDATE app_core.order_stage
       SET sync_flag = 'Y'
     WHERE batch_id = p_batch_id;
END //
DELIMITER ;

配套授权不要省:

GRANT EXECUTE ON PROCEDURE app_core.proc_sync_order TO 'svc_job'@'%';
GRANT SELECT, INSERT, UPDATE ON app_core.order_stage TO 'svc_proc'@'%';
GRANT INSERT ON app_core.order_stage_hist TO 'svc_proc'@'%';

如果要走调用者权限,那过程定义我会改成更显式:

DELIMITER //
CREATE PROCEDURE app_core.proc_check_batch(IN p_batch_id VARCHAR(32))
SQL SECURITY INVOKER
BEGIN
    SELECT batch_id, COUNT(*) AS row_cnt
    FROM app_core.order_stage
    WHERE batch_id = p_batch_id
    GROUP BY batch_id;
END //
DELIMITER ;

这样谁能查、谁不能查,边界更清楚。代价是调用账号授权要提前设计好,不能临时糊。

我实际排查时常用的一组核对动作

很多时候,问题不是不会处理,而是现场时间紧,动作顺序一乱就容易漏。按我自己的经验,下面这组检查顺序比较稳。

1. 先确认过程定义

SHOW CREATE PROCEDURE app_core.proc_sync_order;
SHOW CREATE FUNCTION app_core.fn_calc_amt;

重点看这些信息:

  • 是否带 DEFINER
  • SQL SECURITYDEFINER 还是 INVOKER
  • 过程归属的数据库是否正确
  • 是否存在跨库调用

2. 再确认系统元数据

SELECT db, name, type, definer
FROM gbase.proc
WHERE db = 'app_core';

3. 最后再检查授权链

SHOW GRANTS FOR 'svc_job'@'%';
SHOW GRANTS FOR 'svc_proc'@'%';

如果现场没有统一命名,我还会额外核对过程名和调用库名,避免因为不同环境脚本写法不一致导致误判。

这类问题最容易留下的坑

我最近整理下来觉得,GBase 8a 里存储过程相关问题,真正麻烦的不是报错本身,而是“修好了这次,却没修治理方式”。下面这些坑基本都值得单独留意。

常见坑 现场表现 我个人更倾向的处理方式
用高权限个人账号创建过程 测试正常,生产迁移后权限混乱 使用专门的过程定义账号
过程依赖跨库对象 不同环境库名不同,迁移后失效 统一命名,显式写全限定名
改账号名不重建过程 过程不可见或不可调用 统一重建或集中核对 definer
只做 GRANT EXECUTE 过程进入后对象访问报错 同步核对对象级授权
环境脚本不保留 DDL 故障后只能手工补过程 过程定义纳入版本管理

我自己更看重的不是“能不能跑”,而是后面还能不能维护

说到底,GBase 8a 的存储过程并不只是一个 SQL 封装壳。它本质上是对象定义、执行身份和权限模型绑在一起后的产物。现场里一旦发生账号调整、环境迁移、对象归属变化,这几部分任何一处没收口,后面都可能变成隐患。

我最近看资料时最深的一个感受是,存储过程治理做得好不好,不取决于你会不会写循环、条件和变量,而取决于你有没有把“谁创建、谁执行、谁拥有底层对象权限”这条链条提前讲清楚。真正落到现场时,很多问题都不是临时多加一条授权能解决的。

我自己现在更倾向于把存储过程当成“可运维对象”去管理:

  • 过程定义账号单独规划;
  • SQL SECURITY 明确写出,不依赖默认值;
  • 过程 DDL 纳入版本管理;
  • 账号改名、对象迁移后,补做一次过程元数据校验;
  • 上线前不只测“能创建”,还要测“业务账号能不能按预期执行”。

这样做看起来前期多花一点时间,但后面真遇到问题,排查路径会清楚很多。

参考资料

[1] 创建存储过程/函数
https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-procedures-functions/dm-procedures-functions-create

[2] GBase 8a 存储过程的赋权
https://www.gbase.cn/community/post/6198

[3] 用户名修改导致存储过程无法识别
https://www.gbase.cn/community/post/8232

[4] GBase 8a
https://www.gbase.cn/community/section/11

评论

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