PostgreSQL持续调用场景下安全修改存储函数/存储过程的方法
PostgreSQL持续调用场景下安全修改存储函数/过程的方案
问题背景
使用JDBC连接AWS Aurora PostgreSQL 14.4集群,通过Liquibase管理数据库架构(含存储函数/过程),其中一个runOnChange: true的changeSet负责在SQL文件变更时执行CREATE OR REPLACE语句更新函数bar()。滚动部署应用并修改bar()逻辑时,替换瞬间其他应用副本调用bar()会报错:
ERROR: function bar(arg1 => text, arg2 => text) does not exist Hint: No function matches the given name and argument types. You might need to add explicit type casts. Position: 4 : errorCode = 42883
无法从应用层重试事务,要求修改过程中事务不失败,允许短暂阻塞(数百毫秒)。
可行解决方案
方案一:利用函数锁控制替换时机
在执行CREATE OR REPLACE前,先获取函数的SHARE UPDATE EXCLUSIVE锁,该锁的特性是:
- 允许已在执行的
bar()调用继续完成 - 阻止新的
bar()调用直到锁释放,新调用会进入阻塞等待状态
将以下逻辑放入Liquibase的changeSet中(Liquibase默认每个changeSet在单一事务内执行):
BEGIN; -- 获取函数锁,确保替换前已有调用完成,新调用排队等待 LOCK FUNCTION bar(text, text) IN SHARE UPDATE EXCLUSIVE MODE; -- 执行函数替换 CREATE OR REPLACE FUNCTION bar(arg1 text, arg2 text) RETURNS [返回类型] AS $$ -- 新的函数逻辑 $$ LANGUAGE plpgsql; COMMIT;
此方案逻辑简单,替换过程原子性强,阻塞时间仅为函数替换的耗时,适合轻量修改场景。
方案二:版本化函数+原子别名切换
如果担心锁阻塞时间过长,可采用版本化分步替换,全程在事务内完成,无函数不存在的间隙:
- 创建新版本函数
- 原子性切换原函数名称到新版本
对应SQL如下:
BEGIN; -- 1. 创建新版本函数 CREATE OR REPLACE FUNCTION bar_v2(arg1 text, arg2 text) RETURNS [返回类型] AS $$ -- 新的函数逻辑 $$ LANGUAGE plpgsql; -- 2. 原子性切换函数名称:先将旧函数改名,再把新函数改为原名称 ALTER FUNCTION bar(text, text) RENAME TO bar_v1; ALTER FUNCTION bar_v2(text, text) RENAME TO bar; COMMIT;
此方案中,应用始终能找到bar()函数,旧函数仅被重命名而非删除,切换过程瞬间完成,适合复杂函数的修改场景,可避免长时间锁函数导致的请求堆积。
注意事项
- 确保Liquibase的changeSet未设置
noTransaction: true,否则锁和替换操作会不在同一事务内,失去原子性。 - AWS Aurora集群中,函数修改仅需在主节点执行,只读副本会自动同步,无需额外操作。
内容的提问来源于stack exchange,提问作者Nagendra U M
相关产品推荐
相关产品推荐

