表新增了一个字段,管理终端里查询一切正常,应用却反复报cached plan must not change result type。这种差异常出现在长期复用的数据库连接上,尤其是使用服务端预备语句、又习惯写SELECT *的应用。

直接原因是结果结构变了
预备语句记录了查询的结果结构。DDL可能让查询重新规划;若重规划后的结果类型与旧语句不兼容,服务端会拒绝执行。加列后SELECT *可能多返回一列,改类型则可能让显式列查询也受影响。新会话首次准备语句时看见的是新表,因而可能正常。
先判断受影响的是哪组连接
从应用日志找报错SQL、连接池名称与首次发生时间,对照迁移记录查返回列变化。在数据库会话用SELECT pg_backend_pid();记录后端标识;SELECT name, statement FROM pg_prepared_statements;只反映当前会话可见的预备语句。临时打开一个管理连接,不能由此查看应用旧连接自己的预备语句。
恢复时优先回收相关连接
报错发生在显式事务内时,应按应用事务策略回滚,再由连接池有序淘汰受影响的物理连接。新会话会重新准备语句。先排空在途请求再逐步轮换连接,避免整个池同时重连;任何写操作的重试都应确认业务状态。
SQL级DEALLOCATE只作用于当前会话;使用驱动管理的预备语句时,先遵循驱动或连接池文档。直接在应用连接执行清理命令可能让驱动本地缓存与服务端脱节。
让下一次迁移兼容新旧查询
把关键查询改成显式列清单,并确保旧版本依赖的返回列和类型在过渡期稳定。字段改类型可评估新增列、回填、切换读取、最后移除旧列的分步方案。pgJDBC支持prepareThreshold=0关闭服务端预备语句,但仅适用于该驱动的对应配置,也会影响计划复用,不宜当成全局默认修复。
验证时保留一条旧连接
用萤光云或LightNode的隔离VPS复制驱动版本与连接池设置。先让一条物理连接准备旧语句,另一条连接在测试表执行代表性DDL;分别用旧连接和新连接执行相同查询,再验证有序回收后的表现。验收看真实应用连接能返回预期列和类型,错误率恢复,连接峰值没有异常。
FAQ
DISCARD PLANS就能修好吗? 它清理缓存计划,不能保证保留的预备语句能接受不同结果类型。
应用重新借一次连接有用吗? 可能仍拿到同一物理连接,需确认连接池确实回收了旧会话。
去掉星号就彻底解决吗? 能降低加列的影响,但选中列改类型仍要验证。
需要重启整个数据库吗? 一般先定向回收应用连接;重启数据库影响范围更大。
为什么错误后紧接着又报事务中止? 显式事务可能已经失败,按应用策略回滚后再继续。
温馨提示
部署前把旧应用、新应用、旧连接和新连接都纳入验证。尤其留意连接池长寿命会话,单次命令行查询通过不足以验收迁移。


