jdbc 流方式读取大表大数据量报错

为提高效率,请提供以下信息,问题描述清晰能够更快得到解决:

【TiDB 版本】

5.7.25-TiDB-v4.0.10

【问题描述】
使用jdbc StreamResult的方式读取TiDB大表(总数3亿)中3kw+的记录(有等值索引)
mysql驱动:mysql-connector-java-8.0.18
关键代码如下:

//查询SQL
String sql="select belnr,buzei,bukrs,gjahr,sys_code,mandt,lessee_id,blart,budat,monat,cpudt,usnam,tcode,xblnr,stblg,stjah,bktxt,waers,kursf,bstat,awtyp,awkey,xstov,ppnam,kurst,augdt,augcp,augbl,bschl,koart,umskz,umsks,zumsk,shkzg,mwskz,dmbtr,wrbtr,pswbt,pswsl,zuonr,sgtxt,vbund,kokrs,kostl,anln1,anbwa,hkont,kunnr,lifnr,xopvw,zfbdt,zterm,zlsch,ebeln,ebelp,prctr,lokkt,geber,xref1,xref2,xref3,fkber,xnegp,kidno,auggj,agzei,fkber_long,bvtyp,hwaer,update_time from XXX where cpudt='20150703' and bukrs like 'A0%' order by belnr asc,buzei asc,bukrs asc,gjahr asc,sys_code asc,mandt asc,lessee_id asc"
//cpudt 是索引字段,order by的字段是一组联合主键

conn.setAutoCommit(false);
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
return stmt.executeQuery(sql);

【异常如下】:

  1. 如果我不在url设置net_read_timeout 和 net_write_timeout (默认都是30)
    执行query一段时间后出现The last packet successfully received from the server was 211,836 milliseconds ago错误
  2. 如果我在url中设置url=xxx/db?net_read_timeout=300&net_write_timeout=300后,程序执行query出现Packet for query is too large (5,526,600 > 65,535). You can change this value on the server by setting the ‘max_allowed_packet’ variable. 错误。

代码和sql是正常的,如果我把cpudt的条件改成其他数量小的查询,是可以正常执行并返回结果。并且程序已经验证过其他单次查询约400W记录的情况

请问是不是stream result对查询数据量由限制,并且限制由参数max_allowed_packet控制呢?


若提问为性能优化、故障排查类问题,请下载脚本运行。终端输出的打印结果,请务必全选并复制粘贴上传。

1 个赞

这是2个问题:
1、第一个问题咱们有使用中间件吗?这个报错的原因是会话被杀掉了,返回结果集的时候,发现连接已经被杀掉了
2、第二个问题,按照提示调整一下参数吧

1、我直接连接的TiDB一个节点,没有中间件
2、server端目前默认max_allowed_packet是64M(67108864bytes),错误信息提示的“65,535”单位应该是KB( 65,535KB约等于64M),那这样算的话 5,526,600KB = 5397M,不可能把max_allowed_packet调整到这个值吧?亦或者是,我上边的算法有误,麻烦指正

1、一问题,我建议你程序使用长链接,或探活机制
2、改成提示的值有什么问题吗?这个参数并不是只有数据库有这个参数,按照提示的值修改就可以

1、jdbc statement直连tidb server,就是长连接
2、我再url中传入sessionVariables=max_allowed_packet=1073741824&sessionVariables=net_read_timeout=600&sessionVariables=net_write_timeout=600 依然是一会出现com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure 或者 com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (5,526,600 > 65,535). You can change this value on the server by setting the ‘max_allowed_packet’ variable
是我参数不对?我更怀疑是不是tidb 出现GC或者其他情况导致返回这种错误呢?
对了,这个问题不是说在执行查询过程出现,而是第一次查询就失败
可惜我没法登陆tidb服务器。

可以分别看一下下面2个链接:
https://docs.pingcap.com/zh/tidb/v4.0/loader-overview#原因
https://docs.pingcap.com/zh/tidb/v4.0/java-app-best-practices#开发-java-应用使用-tidb-的最佳实践

问题已解决。
1.因为我是两个任务使用相同的sql读取相同的tidb表,然后进行处理的。第一个任务连接开启正常,第二个在连接阶段就出异常。
2.目前我在集群中新建了一个schema,并且把这个表的数据同步过去,再执行相同的两个任务,相同SQL,但是不同schema查询。任务执行正常,未见异常

疑问:未新建schema前,即使statement.executeQuery(“set max_allowed_packet=1073741824”)或者url中加上max_allowed_packet=1073741824参数也依然报错: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (5,526,600 > 65,535). You can change this value on the server by setting the ‘max_allowed_packet’ variable*,那是不是tidb对1个以上相同的stream query会有问题呢?

不会:sweat_smile:,至于你的情况,目前没想到理由,不过并发 在 tidb 中很常见,不会有问题

我昨天想了下,我是通过streaming result的方式去查询的,理论上只会一行一行返回结果,不应该出现 query is too large的问题吧?

你可以看看你的表一行的大小:laughing:

肯定远没有64M呀。还是怀疑内部GC或者啥导致的,等我哪天能看到监控在贴出来

你能登陆 tidb 把 max_allowed_packet 参数改成1073741824试试嘛

相同的环境,就是新建了一个Schema,就解决问题了?

那(原来环境)执行报错的日志有没有?
statement.executeQuery(“set max_allowed_packet=1073741824”)
或者url中加上max_allowed_packet=1073741824

你能发一段 explain SQL 的信息么? 然后 EXPLAIN ANALYZE SQL 也来一段看看,对比一下

没有权限呢,但是我确信已在连接session中设置

新建的schema有相同的索引么?原表的表健康度正常么?

上面有个小疑问的地方就是:看 链接参数确实设置了这个参数,但报的错还是 65535,这个有点怪

是不是他用的账户没配置权限? :nerd_face:
所以无法生效,然后被忽略了

不会的,会话参数权限,都有的

那要看日志才能知道原因了 ,坐等他上传

:grin: