注册
结果集fetch次数多+synchronized锁引发的性能衰减
技术分享/ 文章详情 /

结果集fetch次数多+synchronized锁引发的性能衰减

wuran 2025/11/07 121 0 0

一、问题

PK测试过程中遇到一个奇怪的问题,应用系统场景为:SQL查询逻辑简单且字段过虑性好,业务特点为小结果集、高批次。在数据和对方库一致,DM库sql总执行耗时更少,sql执行效率更高的情况下,应用全流程总执行耗时比对方库多1h。最终定位为两方面引起,一个是DM结果集fetch次数多导致IO交互次数多,另一个是应用和JDBC驱动均添加了synchronized锁,影响并发,拉大了和对方库的性能差距。

二、问题分析

1、数据库层面

通过博睿监控工具查看数据库服务器平均网络传输速度,发现对方库平均网络传输速度比DM快23%左右。监控会话使用情况,发现对方库比DM库并发高。猜测可能是网络传输存在限制,网络交互次数多但单次数据包小,说明DM结果集fetch策略存调整优化空间,可通过数据库参数FIRST_ROWS和FETCH_PACKAGE_SIZE控制。
FIRST_ROWS:结果集一个消息中返回的最大行数,默认100,不管FETCH_PACKAGE_SIZE参数值为多少,第一次都是返回100条,作用是提前响应,sqllog中的exec耗时,实际是FIRST_ROWS的返回耗时,不包括fetch时间。单条SQL最大返回396行结果场景,调整FIRST_ROWS=500足以减少IO交互次数。ORACLE和Gaussdb默认一次fetch所有结果集,DM默认首次只fetch 100行数据,多出的fetch操作,会产生额外传输开销。
FETCH_PACKAGE_SIZE(指定 第二批及之后的FETCH 操作时使用的消息包大小,单位 KB)。决定每次FETCH操作返回的数据量,减少MSG通信次数,每一次收发之前都会MSG通信,调整过大会造成IO等待过久反而引发性能衰减。
若应用中有添加synchronized锁,降低了程序整体并发性能,这时FIRST_ROWS调整太大,会放大同步锁的问题,因为需要更长时间的等待,调小FIRST_ROWS,提前返回,放大FETCH_PACKAGE_SIZE,同步锁接收到消息,会接着进行下一步操作,这样也能减少IO交互次数。
最终满足现场预期参数值如下:
FIRST_ROWS(100>>500)
FETCH_PACKAGE_SIZE(512>>1024)

2、应用层面

采用在应用代码块添加输出时间戳日志的方法,定位出可能存在问题的模块。通过分析耗时差异,发现添加synchronized锁会导致在执行数据库查询(selectByExample)方法时产生了大量的队列等待,等待时间2s~5s。怀疑是驱动层面的差异导致。
synchronized锁通过锁定整个方法或代码块实现线程安全,但会导致其他线程等待锁释放。在高并发场景中,多个线程竞争同一锁会造成锁竞争,增加线程上下文切换和等待时间,从而降低程序整体并发性能。
JDBC驱动同步锁设计是为解决驱动与数据库版本不匹配时的协议校验冲突问题,如旧版驱动连接高版本数据库,数据库服务端可能会因消息格式校验失败主动断开连接。此外,驱动层开启同步锁可规避因应用代码没有维护好连接建立/关闭操作的完整性造成的连接异常。可通过url串中指定unlockResultSet=0/1/2进行策略配置。0代表关闭优化,1代表不对结果集get接口上锁,2代表不对结果集get接口和next fetch接口上锁。如果应用连接控制得当,推荐配置unlockResultSet=2,若应用未合理控制代码逻辑,推荐配置unlockResultSet=1。
如果应用也添加synchronized锁,驱动层可放心关闭所有同步锁。同时开启应用层和驱动层synchronized锁的话,双重锁会触发锁竞争叠加效应,造成双重阻塞:线程需先后获取两把锁,若任一锁被占用,线程将进入等待队列,放大了队列等待时间,从而导致整个业务场景耗时过长。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服