当前位置: 首页 > news >正文

“An I/O error occurred while sending to the backend” 时,可能是你的 SQL 参数超过了 32767

一、现象:一个看似“网络”的异常

生产日志里突然冒出:复制

org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend. Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 34524

第一反应:
“网断了?连接池炸了?数据库挂了?”
结果 DBA 说数据库一切正常,别的业务也稳如老狗。于是开始漫长的“甩锅”之旅……


二、定位:34524 到底是个啥?

把异常栈翻到底,发现关键行:

at org.postgresql.core.PGStream.sendInteger2(PGStream.java:347)

PostgreSQL JDBC 驱动在组装Parse报文时,需要把参数个数写成 2 字节。
34524 > 32767(0x7FFF),直接越界,驱动抛错。
所以,这不是网络 I/O,而是协议层面的“参数超限”。


三、复现:一条 SQL 如何把参数干到 3W+

简化后的伪代码:

List<String> teams = dealerTeamService.selectAll(); // 1,800+ Map<String, Object> param = new HashMap<>(); param.put("dataDate", dataDate); param.put("teams", teams); // 1,800 个字符串 return mapper.queryWaitDeal(param);

对应的 XML:

<select id="queryWaitDeal" resultType="xxx"> SELECT 'survey' AS stage, 1 AS orderId, COALESCE(SUM(wait_cnt), 0) AS waitCnt FROM t_wait_deal WHERE data_date = #{dataDate} AND (dealer_id::text||','||dealer_team_id::text) = ANY <foreach collection="teams" item="t" open="ARRAY[" separator="," close="]"> #{t} </foreach> UNION ALL <!-- 还有 3 个 UNION,每个都 copy 一遍 teams --> </select>

1 800 × 4 条 UNION × 3 天批次 ≈ 34 000 个参数,
完美踩雷。


四、协议天花板:32767 的由来

PostgreSQL Frontend/Backend Protocol 文档里写得明明白白:

Parse (F) Int32 length String statementName String query Int16 number of parameter types (→ 2 字节) ...

JDBC 驱动只是忠实实现,想改协议?先 fork PG 源码


五、解法:参数瘦身三板斧

方案思路代码量级推荐指数
1. 临时表/VALUES 表COPY团队列表到临时表,SQL 里直接JOIN中等★★★★☆
2. 分批查询内存拆队,每批 1k,结果归并★★★★★
3. 服务端数组类型Array[text]换成int[],一条= ANY(?::int[])即可需 DBA 配合★★★☆☆

临时表演示:

sql

-- 会话级临时表,自动回收 CREATE TEMP TABLE tmp_team ON COMMIT DROP AS SELECT unnest(?::text[]) AS team_id; SELECT ... FROM t_wait_deal w JOIN tmp_team t ON (w.dealer_id||','||w.dealer_team_id) = t.team_id;

Java 端只需传1 个 java.sql.Array,参数个数瞬间降到 3 个。

分批演示:

List<List<String>> partitions = Lists.partition(allTeams, 800); return partitions.stream() .map(p -> mapper.queryWaitDeal(dataDate, p)) .reduce(this::merge) .orElse(Collections.emptyList());

六、踩坑小结

  1. 看到“An I/O error occurred while sending to the backend”先别急着重启,
    Caused by翻到底,关键字“out-of-range integer as a 2-byte value”直指参数过多。

  2. MyBatis 的<foreach>爽归爽,集合 size 超过 1k 就要警惕

  3. 架构评审时,把“列表查询”当潜在 SQL 炸弹,提前留好分批/临时表口子。

  4. 32767 是硬天花板,任何 ORM、任何驱动都绕不过去


七、一行代码应急兜底

if (teams.size() > 1000) { throw new BizException("一次最多选择 1000 个团队,请缩小范围"); }

先保命,再优化。


八、参考

  • PostgreSQL Protocol 3.0 – Frontend/Backend Protocol

  • PostgreSQL JDBC Driver –PGStream.javahistory #2471

  • MyBatis Foreach 陷阱 – MyBatis Documentation

http://www.cnnetsun.cn/news/8242.html

相关文章:

  • 以向善价值观构建数字信任,数美2025 AI风控大会重磅发布“AI风控新范式”
  • 终极嵌入式按键解决方案:MultiButton状态机库实战指南
  • ZyPlayer终极配置指南:3步打造专属影院级体验
  • gmhelper:5分钟快速掌握国密算法SM2/SM3/SM4的完整应用方案
  • 19、高级Shell编程与正则表达式过滤器
  • PHP兼容性检查工具完整指南
  • 基于元胞自动机交通模型的三车道Matlab代码:模拟车辆多车道行驶及可视化分析系统
  • 豆包手机二手价被炒到3.6万元;被曝开发“芯片定位”技术,英伟达:这不是后门;千问月活突破3000万 | 极客头条
  • Wan2.2-T2V-A14B适合哪些行业?五大垂直领域推荐
  • 基于西门子S7-200PLC的自动灌溉系统组态王组态 带解释的梯形图程序,接线图原理图图纸,io分配
  • 机器学习驱动的智能化电池管理技术与应用
  • 如何优化MinerU项目的PaddleOCR模型部署效率
  • SVG.js动画开发终极指南:从入门到精通
  • Python 对象序列化与存储库pickle详细介绍
  • 圣诞快乐!来自代码世界的祝福
  • 一致性的威力:AI如何在B/G端释放数据治理的真正生产力
  • 知行之桥三种接口详解:Webhook、Flow API 与 Admin API
  • CPFEM晶塑动态展示:VUMAT子程序高效率与多维度模型应用测试
  • 毕业设计实战:基于SpringBoot+MySQL的旅游网站设计与实现,从需求到测试全流程拆解,新手也能轻松通关!
  • 如何在没有电脑的情况下备份 iPhone
  • Python HTTPX性能优化实战:10个技巧解决90%的连接问题
  • 空调加热器MPC模型预测控制程序与修正Kalman滤波:附简洁文献与附图与运行指南
  • Spring Boot日志文件未生成问题排查
  • 2025CRM选型手册:主流CRM品牌客户 - 销售 - 团队管理能力 场景化对比
  • AI口碑决胜未来:2025年智能洞察与AI市场舆情分析平台深度对决
  • 5分钟掌握Nginx LDAP认证系统部署技巧
  • 基于否定选择算法的异常检测技术详解
  • 手把手根治Qwen-Agent工具重复调用:实战优化指南
  • 系统管理shutdown命令
  • AOT 与 GraalVM Native Image 深度解析