修改PostgreSQL字段长度导致cached plan must not change result type错误
问题
有业务反馈在修改一个表字段长度后,Java应用不停的报下面的错误,但是越往后错误越少,过了15分钟错误就没有再发生。
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: cached plan must not change result type
原因
调查判断原因是修改字段长度导致执行计划缓存失效,继续使用之前的预编译语句执行会失败。
很多人遇到过类似错误,比如:
但是,有两个疑问没有解释清楚。
- 以前业务也改过字段长度,但为什么没有触发这个错误?
- 这个错误能否自愈?
下面是进一步的分析
PostgreSQL中抛出此异常的代码如下:
static List * RevalidateCachedQuery(CachedPlanSource *plansource, QueryEnvironment *queryEnv) { if (plansource->fixed_result) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cached plan must not change result type"))); ... }
pgjdbc代码里有对该异常的判断,发生异常后,后续的执行会重新预编译,不会继续使用已经失效的预编译语句。这说明pgjdbc对这个错误有容错或自愈能力。
protected boolean willHealViaReparse(SQLException e) { ... // "cached plan must not change result type" String routine = pe.getServerErrorMessage().getRoutine(); return "RevalidateCachedQuery".equals(routine) // 9.2+ || "RevalidateCachedPlan".equals(routine); // <= 9.1 }
发生条件
经验证,使用Java应用时本故障的发生条件如下:
- 使用非自动提交模式
- 使用prepareStatement执行相同SQL 5次以上
- 修改表字段长度
- 表字段长度修改后第一次使用prepareStatement执行相同SQL
测试验证
以下代码模拟Java连接多次出池->执行->入池,中途修改字段长度。可以复现本问题
Connection conn = DriverManager.getConnection(...); conn.setAutoCommit(false); //自动提交模式下,不会出错,pgjdbc内部会处理掉 String sql = "select c1 from tb1 where id=1"; PreparedStatement prest =conn.prepareStatement(sql); for(int i=0;i<5;i++) { System.out.println("i: " + i); prest =conn.prepareStatement(sql); ResultSet rs = prest.executeQuery(); prest.close(); conn.commit(); } //在这里设置断点,手动修改字段长度: alter table tb1 alter c1 type varchar(118); for(int i=5;i<10;i++) { System.out.println("i: " + i); try { prest =conn.prepareStatement(sql); ResultSet rs = prest.executeQuery(); prest.close(); conn.commit(); } catch (SQLException e) { System.out.println(e.getMessage()); conn.rollback(); } } conn.close();
测试程序执行结果如下:
i: 0 i: 1 i: 2 i: 3 i: 4 i: 5 ERROR: cached plan must not change result type i: 6 i: 7 i: 8 i: 9
回避
- 在不影响业务逻辑的前提下,尽量使用自动提交模式
- 修改表字段长度后重启应用,或者在业务发生该SQL错误后重试(等每个Jboss缓存的连接都抛出一次错误后会自动恢复)
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
物联网给IT带来了新的挑战:海量数据、复杂性和超动态应用
云栖号:https://www.aliyun.com/#module-yedOfott8第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 来源:物联之家网(iothome.com) 转载请注明来源! 物联网已经在许多领域成为现实:智能路灯、智能电表和自耕农田等等。设备可以决定何时启动,何时购买能源(因为价格便宜)以及何时开始浇灌田地。决策基于数据,而不仅仅是预编程的激活。 甚至还有更多:可预测即将到来疾病的可穿戴设备,或者在出现问题时可自行通知客服人员的电子设备。 这些用例都有一个共同点:数据。数据量已经达到历史新高,问题是谁应该分析这些海量数据?自从物联网时代开始以来,就不可能再手动监控IT运营。物联网将很快成为标准解决方案,因此您将必须自动进行可用性检查和监控。 复杂性爆发 云技术和物联网几乎同时席卷全球。这意味着通过设备的广泛联网,数据量爆炸式增长,以及当今基于云的超动态应用的高变化率。 麦肯锡预计,到2025年,物联网每年将带来11万亿美元的全球经济价值。例如,总价值的90%将通过降低费用或节省时间而使用户(使用物联网应用的消...
- 下一篇
用JavaScript开发Stellar区块链应用
Stellar JS SDK封装了Stellar交易的提交,以及与Stellar Horizon API服务器的交互过程,可以运行在Node.js环境或Web浏览器中。js-stellar-sdk主要有两个作用:1、通过HorizonAPI服务器查询Stellar区块链数据 2、构建Stellar交易、签名并提交到Stellar网络中。 相关推荐:汇智网 区块链开发系列教程 1、构造Horizon访问请求 js-stellar-sdk使用Builder模式来创建要发送给Horizon API服务器的请求。从一个server对象开始,你可以通过链式调用来生成最终的查询请求。例如: var StellarSdk = require('stellar-sdk'); var server = new StellarSdk.Server('https://horizon-testnet.stellar.org'); // get a list of transactions that occurred in ledger 1400 server.transactions() .forLedger...
相关文章
文章评论
共有0条评论来说两句吧...