一、报错场景
今天写了一个历史数据处理程序,在开发环境、测试环境都可以正常执行,但是放到生产环境上就不行,报了一个这样的错误:
- org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
意思大概是:当把数据发送给后端的时候,出现了一个 I/O 异常。
完整报错截图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e8e6b5119f187ccbac5932661e24bd97.png)
顺着日志中异常栈信息往下看,可以看到下面有一处具体的报错原因:
- Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 51000
意思大概是:出现了一个IO异常,尝试发送一个二进制 int 类型数值时,当前数值超出了大小限制:5100
Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 51000 at org.postgresql.core.PGStream.sendInteger2(PGStream.java:359) at org.postgresql.core.v3.QueryExecutorImpl.sendParse(QueryExecutorImpl.java:1604) at org.postgresql.core.v3.QueryExecutorImpl.sendOneQuery(QueryExecutorImpl.java:1929) at org.postgresql.core.v3.QueryExecutorImpl.sendQuery(QueryExecutorImpl.java:1487) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:347) ... 140 common frames omitted
![](https://i-blog.csdnimg.cn/blog_migrate/319181cbb79993612ca66dfb66afe236.png)
二、源码分析
我们根据报错提示,找到源码对应的位置,该源码位于 postgresql
依赖中,Maven坐标如下:
<!--postgresql 数据库驱动依赖 --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.6.0</version> <scope>runtime</scope> </dependency>
源码位置如下:
![](https://i-blog.csdnimg.cn/blog_migrate/88791ed3cc90741902fcf9dc80174147.png)
可以看到这里有一个大小限制,Short.MAX_VALUE
对应的就是 Short 类型的最大值,为 32767
。也就是说,在 PostgreSQL 中,如果参数的数量超过 32767 之后,就会抛出 Tried to send an out-of-range integer
这个异常。
![](https://i-blog.csdnimg.cn/blog_migrate/30a4db2221c0ce4f51ec537a2fa5d144.png)
三、实际原因(更加复杂)
道理都懂,但是细想的话还是有以下两个问题:
- 我在代码中明明根据入参按照 1000 分页处理的,没有超出 32767,为什么还会报这个错?
- 报错信息中的 51000 又是哪来的?
于是我们继续根据上面的异常栈信息进行排查,针对第2层栈信息在本地打断点调试。
Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 51000 at org.postgresql.core.PGStream.sendInteger2(PGStream.java:359) at org.postgresql.core.v3.QueryExecutorImpl.sendParse(QueryExecutorImpl.java:1604) at org.postgresql.core.v3.QueryExecutorImpl.sendOneQuery(QueryExecutorImpl.java:1929) at org.postgresql.core.v3.QueryExecutorImpl.sendQuery(QueryExecutorImpl.java:1487) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:347) ... 140 common frames omitted
![](https://i-blog.csdnimg.cn/blog_migrate/fa65930811d378bcb65da9c1f00d8b59.png)
首先找到第二层栈信息对应的源码位置,并在本地进行debug:
![](https://i-blog.csdnimg.cn/blog_migrate/e9833c8b0f0d3d809d855f6c60a71f20.png)
这里我本地调试的时候,是传了 11
个参数,但是在第二层断点中入参却变成了 352
个,这是为什么呢?
不要慌,其实这里的 queryUtf8
变量中存储的就是我们的 SQL 了,我们直接查看下这个变量的字符串即可:
![](https://i-blog.csdnimg.cn/blog_migrate/a48a36e33462040109384ad781d9b010.png)
终于,找到罪魁祸首了,由于项目中集成了 ShrdingJDBC,导致执行 SQL 的时候会自动拼接分表 SQL 并进行 UNION ALL
,导致入参个数猛增几十倍。所以:
- 开发环境中,分表是 ~,32张表 × 11个入参 = 352 个总入参。
- 生产环境中,分表是 ~,51张表 × 1000个入参 = 51000 个总入参。
51000 远远超出 32767 的限制,所以抛出 IO 异常。
四、解决思路
可以根据以下两种情况,分别进行解决:
- 如果没有使用 ShardingJDBC 进行分表:建议分页处理。
- 如果已经使用 ShardingJDBC 进行分表:在单个SQL入参不超过 32767 的情况下,一方面可以将分片键加入参数中;另一方面可以再进一步分页,细化分页颗粒度。
整理完毕,完结撒花~🌻
今天的文章 PostgreSQL(十二)报错:Tried to send an out-of-range integer as a 2-byte value: 51000分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/88760.html