业务侧反馈,因为某业务积攒的单量太大,导致在数据批量入库的时候,产生如下报错,主要报错信息是:请求参数的整体大小不能超过2byte。
Tried to send an out-of-range integer as a 2-byte value: 53568

这个报错初步看起来,有个“out-of-range integer”,可能大家第一个想到的可能是:是不是表的字段不够长,但是插入的数据太长了导致的?但其实简单测试下就可以发现,列的长度不够,报的错误和这个是不一样的。
postgres=# create table t1(id int2);
CREATE TABLE
postgres=# insert into t1 values(53568);
ERROR: smallint out of range
CONTEXT: referenced column: id
postgres=# select 53568::int2;
ERROR: smallint out of range
CONTEXT: referenced column: int2
除此之外,查看了报错的这张表的表结构,除了一列为数字类型外,其余字段均为字符或者时间类型,而这一数字类型的列,根据业务场景来看,一般不会有超出长度的数值。
这个报错是驱动报出来的,可以查看下pgjdbc的驱动的代码,pgjdbc的源码地址为:https://github.com/pgjdbc/pgjdbc
报错的位置只有一处,如下是42.3.10版本pgjdbc的报错部分,其中,判断条件里Short.MIN_VALUE和Short.MAX_VALUE的值分别为-32768和32767,表示SQL 解析过程中, SQL中的参数的个数最大不超过32767。
...
public final class Short extends Number implements Comparable<Short> {
public static final short MIN_VALUE = -32768;
public static final short MAX_VALUE = 32767;
... ...
/**
* Sends a 2-byte integer (short) to the back end.
*
* @param val the integer to be sent
* @throws IOException if an I/O error occurs or {@code val} cannot be encoded in 2 bytes
*/
public void sendInteger2(int val) throws IOException {
if (val < Short.MIN_VALUE || val > Short.MAX_VALUE) {
throw new IOException("Tried to send an out-of-range integer as a 2-byte value: " + val);
}
int2Buf[0] = (byte) (val >>> 8);
int2Buf[1] = (byte) val;
pgOutput.write(int2Buf);
}
... ...
如下是42.4.0版本pgjdbc的报错部分,其中,判断条件里将之前版本的MIN_VALUE和MAX_VALUE直接替换成了0和65535,表示SQL 解析过程中, SQL中的参数的个数最大不超过65535。
/**
* Sends a 2-byte integer (short) to the back end.
*
* @param val the integer to be sent
* @throws IOException if an I/O error occurs or {@code val} cannot be encoded in 2 bytes
*/
public void sendInteger2(int val) throws IOException {
if (val < 0 || val > 65535) {
throw new IllegalArgumentException("Tried to send an out-of-range integer as a 2-byte unsigned int value: " + val);
}
int2Buf[0] = (byte) (val >>> 8);
int2Buf[1] = (byte) val;
pgOutput.write(int2Buf);
}
而进一步和业务确认,果然他们使用的pgjdbc驱动的版本比较低,并且其实日志的报错部分往上翻,其实也能大致看出这个报错的SQL,涉及到了大量的绑定变量。业务同事反馈,交易笔数大概几千比,但是每比交易涉及到很多绑定变量,这些总和超出了他们使用的pgjdbc驱动的参数个数限制。

因此,解决方案其实有两种:一种使用较高版本的pgjdbc驱动(42.4.0+),驱动支持的参数的最大个数提升到了65535,会很大程度上减少此类报错。一种是采用jdbc或者MyBatis等持久化框架提供的批量执行接口进行批量操作。调整业务,对拼接的SQL的逻辑进一步拆分,减少单个SQL绑定变量个数。




