【发布时间】:2019-01-18 14:45:43
【问题描述】:
我正在做一些事情,我需要从 MariaDB 中提取数据(使用 HikariCP),然后通过 Redis 发送。最终,当我尝试从数据库中提取数据时,连接将开始泄漏。这只会随着时间的推移而突然发生。
这是泄漏开始时的完整日志:https://hastebin.com/sekiximehe.makefile
这是一些调试信息:
21:04:40 [INFO] 21:04:40.680 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Before cleanup stats (total=6, active=2, idle=4, waiting=0)
21:04:40 [INFO] 21:04:40.680 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After cleanup stats (total=6, active=2, idle=4, waiting=0)
21:04:40 [INFO] 21:04:40.682 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.mariadb.jdbc.MariaDbConnection@4b7a5e97
21:04:40 [INFO] 21:04:40.682 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After adding stats (total=7, active=2, idle=5, waiting=0)
21:05:05 [INFO] 21:05:05.323 [HikariPool-1 housekeeper] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.mariadb.jdbc.MariaDbConnection@52ede989 on thread Thread-272, stack trace follows
java.lang.Exception: Apparent connection leak detected
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
at us.survivewith.bungee.database.FetchPlayerInfo.run(FetchPlayerInfo.java:29)
at java.lang.Thread.run(Thread.java:748)
21:05:10 [INFO] 21:05:10.681 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Before cleanup stats (total=7, active=2, idle=5, waiting=0)
21:05:10 [INFO] 21:05:10.681 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After cleanup stats (total=7, active=2, idle=5, waiting=0)
21:05:39 [INFO] 21:05:39.352 [HikariPool-1 housekeeper] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.mariadb.jdbc.MariaDbConnection@3cba7850 on thread Thread-274, stack trace follows
java.lang.Exception: Apparent connection leak detected
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
at us.survivewith.bungee.database.FetchPlayerInfo.run(FetchPlayerInfo.java:29)
at java.lang.Thread.run(Thread.java:748)
这里是 FetchPlayerInfo.run() 方法:
@Override
public void run()
{
String select = "SELECT `Rank`,`Playtime` FROM `Players` WHERE PlayerUUID=?;";
// This is line 29. How can this possibly be causing a leak?
try(Connection connection = Database.getHikari().getConnection())
{
// Get the data by querying the Players table
try(PreparedStatement serverSQL = connection.prepareStatement(select))
{
serverSQL.setString(1, player);
// Execute statement
try(ResultSet serverRS = serverSQL.executeQuery())
{
// If a row exists
if(serverRS.next())
{
String rank = serverRS.getString("Rank");
Jedis jPublisher = Redis.getJedis().getResource();
jPublisher.publish("playerconnections", player + "~" + serverRS.getInt("Playtime") + "~" + rank);
}
else
{
Jedis jPublisher = Redis.getJedis().getResource();
jPublisher.publish("playerconnections", player + "~" + 0 + "~DEFAULT");
}
}
}
}
catch(SQLException e)
{
//Print out any exception while trying to prepare statement
e.printStackTrace();
}
}
这就是我设置数据库类的方式:
/**
* This class is used to connect to the database
*/
public class Database
{
private static HikariDataSource hikari;
/**
* Connects to the database
*/
public static void connectToDatabase(String address,
String db,
String user,
String password,
int port)
{
// Setup main Hikari instance
hikari = new HikariDataSource();
hikari.setMaximumPoolSize(20);
hikari.setLeakDetectionThreshold(60 * 1000);
hikari.setDataSourceClassName("org.mariadb.jdbc.MariaDbDataSource");
hikari.addDataSourceProperty("serverName", address);
hikari.addDataSourceProperty("port", port);
hikari.addDataSourceProperty("databaseName", db);
hikari.addDataSourceProperty("user", user);
hikari.addDataSourceProperty("password", password);
}
/**
* Returns an instance of Hikari.
* This instance is connected to the database that contains all data.
* The stats table is only used in this database every other day
*
* @return The main HikariDataSource
*/
public static HikariDataSource getHikari()
{
return hikari;
}
这就是我调用 FetchPlayerInfo 类的方式:
new Thread(new FetchPlayerInfo(player.getUniqueId().toString())).start();
编辑:
使用 Database 类中的同步 getConnection() 方法后问题仍然存在。
【问题讨论】:
-
我明白你的意思了......我唯一的猜测是 getConnection() 在某种程度上不是线程安全的?
-
@moilejter 即使随着时间的推移会发生这种情况,而且不是每次调用该方法时,这仍然是您的猜测吗?
-
在看似随机的时间失败是它可能是某个地方的竞争条件的线索之一 - 你必须恰到好处地让它失败......它很容易测试 - 写
Database.getConnection()作为同步方法,只调用getHikari().getConnection()- 然后使用它,看看问题是否消失...... -
这怎么可能是竞争条件呢? FetchPlayerInfo 类是唯一访问 Database.getHikari().getConnection() 的东西,而且它一次只被调用一次。我只是误解了什么是比赛条件吗?
-
啊!好吧,我以为你实际上是在启动一堆线程来运行它,而不仅仅是一个......
标签: java sql redis mariadb hikaricp