[alibaba/druid]1.2.6版本中仍然存在同一个holder在connections出现两次的问题,高并发下会出现statement is closed等问题。

2025-11-10 307 views
7
1.druid配置参数如下:

minEvictableIdleTimeMillis=100000 setMaxEvictableIdleTimeMillis=1000L 60L 60L * 7 keepAliveBetweenTimeMillis=120000 minIdle=2 timeBetweenEvictionRunsMillis=70000 validationQuery=select 1 keepAlive=true

2.测试代码如下:
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setUsername("");
    druidDataSource.setPassword("");
    druidDataSource.setUrl("jdbc:mysql://192.168.253.129:3306/user?characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=false&allowMultiQueries=true");
    druidDataSource.setValidationQuery("select 1");
    druidDataSource.setMinEvictableIdleTimeMillis(100000);
    druidDataSource.setMaxEvictableIdleTimeMillis(DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS);
    druidDataSource.setKeepAliveBetweenTimeMillis(120000);
    druidDataSource.setMinIdle(2);
    druidDataSource.setTimeBetweenEvictionRunsMillis(70000);
    druidDataSource.setKeepAlive(true);
    DruidPooledConnection connection1 = druidDataSource.getConnection();
    DruidPooledConnection connection2 = druidDataSource.getConnection();
    connection2.close();
    Thread.sleep(95000);
    connection1.close();
    Thread.sleep(140000);
    DruidPooledConnection connection3 = druidDataSource.getConnection();
    DruidPooledConnection connection4 = druidDataSource.getConnection();
    System.out.println(connection3.getConnectionHolder() == connection4.getConnectionHolder());
    connection3.close();
    connection4.close();
3.运行结果: 4.原因分析:

问题出现在DestoryTask的shrink函数中。 时间点70000ms时:connections[connection2],connection2的空闲时间均小于minEvictableIdleTimeMillis和keepAliveBetweenTimeMillis跳过

时间点95000时,connection1返还连接池,此时connections[connection2,connection1],connections中的元素空闲时间是递减的

时间点140000ms时,connection2因为空闲时间大于120000会加到keepConnections数组中,后返还,返还时未修改lastActiveTimeMillis,此时connections[connection1,connection2],connection2的空闲时间比connection1的要大

时间点210000ms时,connections1因为空闲时间大于100000,但由于设置minidel=2会跳过shrink中所有if代码块,connections2由于空闲时间大于120000,会加入keepconnection中。进而导致connections会有两个相同的holder

5.总结:

1.2.6版本中限制timeBetweenEvictionRunsMillis小于keepAliveBetweenTimeMillis并不能解决问题。该问题出现的原因还是shrink函数中connections拷贝的bug,希望官方能解决这个bug,可参考pullrequest:https://github.com/alibaba/druid/pull/4218

另一方面keepConnection放回connections未修改lastActiveTimeMills,会导致connections的空闲时间不是递减的。

@wenshao

回答

0

这里并发多大会发生,我这边用最新版本,并发在56个就频繁的出现。

1

这里并发多大会发生,我这边用最新版本,并发在56个就频繁的出现。

导致这个问题的根本原因不是因为并发高,是某种特定场景下,连接池持有了两个相同的连接。在高并发下会相互影响。 你可以尝试把keepAliveBetweenTimeMillis的配置设置小于minEvictableIdleTimeMillis暂时可以避免这个问题的出现。但彻底解决问题要等官方回应了。

3

@Linliangkung 请问你给的例子也是

keepAliveBetweenTimeMillis大于minEvictableIdleTimeMillis

依然也会有重复链接的问题。

为何说用这个配置可以暂时避免这个问题?

9

@Linliangkung 请问你给的例子也是

keepAliveBetweenTimeMillis大于minEvictableIdleTimeMillis

依然也会有重复链接的问题。

为何说用这个配置可以暂时避免这个问题?

写错了,是小于

9

@Linliangkung 感谢回复。 把keepAliveBetweenTimeMillis的配置设置小于minEvictableIdleTimeMillis就可以保证不会有重复连接吗?

由于keepConnection放回connections未修改lastActiveTimeMills,会导致connections的空闲时间不是递减的。

例如[a,b,c,d] c和d是keep alive后放回的连接,那么c和d的lastActiveTimeMills大于a和b,下次还是有可能c和d放入keepaliveconnections,导致连接重复。不知道我理解的对不对。

7

@Linliangkung 把keepAliveBetweenTimeMillis的配置设置小于minEvictableIdleTimeMillis就可以保证不会有重复连接吗? 你试过吗

7

@Linliangkung 把keepAliveBetweenTimeMillis的配置设置小于minEvictableIdleTimeMillis就可以保证不会有重复连接吗? 你试过吗

可以,能保证

2

@Linliangkung 把keepAliveBetweenTimeMillis的配置设置小于minEvictableIdleTimeMillis就可以保证不会有重复连接吗? 你试过吗

可以,能保证

minEvictableIdleTimeMillis=180000 setMaxEvictableIdleTimeMillis=180000 keepAliveBetweenTimeMillis=120000 minIdle=4 timeBetweenEvictionRunsMillis=6000 validationQuery=select 1 keepAlive=true

依然会出现 statement is close,不过我用的druid是 1.1.22版本,我升到1.2.6再看看