Частые запросы в базу данных, реализация
Я пишу плагины для игрового сервера, и тут у меня возник вопрос, при использовании бд приходится заново открывать соединение с MYSQL, а запросы происходят часто, раз в 10 секунд точно(возможно и больше). И вопрос заключается в том, будет ли это производительно или прийти к другому решению. Я пытался сохранить подключение, но у меня ошибка вот код
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/db?useUnicode=true&serverTimezone=UTC&useSSL=true&verifyServerCertificate=false", "user", "password")) {
this.getLogger().info("MYSQL connected!");
AccountManager.connection = connection;
} catch (SQLException e) {
throw new IllegalStateException("Cannot connect the server!", e);
}
Вот запрос.
public static HashMap<String, Object> executeSelect(String query) {
try {
Statement stmt = null;
ResultSet resultset = null;
try {
stmt = connection.createStatement();
resultset = stmt.executeQuery(query);
if (stmt.execute(query)) {
resultset = stmt.getResultSet();
}
return resultSetToList(resultset);
}
catch (SQLException ex){
// handle any errors
ex.printStackTrace();
}
finally {
// release resources
if (resultset != null) {
try {
resultset.close();
} catch (SQLException sqlEx) { }
resultset = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) { }
stmt = null;
}
}
}catch (Exception e){
}
return null;
}
Ошибка
Caused by: com.mysql.cj.exceptions.ConnectionIsClosedException: No operations allowed after connection closed.
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
at com.mysql.cj.NativeSession.checkClosed(NativeSession.java:1274)
at com.mysql.cj.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:575)
at com.mysql.cj.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java)
Ответы (1 шт):
Открытие соединения с БД это небыстрая операция: в лучшем случае один вызов по сети (а то и больше), для некоторых СУБД (например, postgres) - старт процесса для обслуживания запросов и его инициализация. И это все еще до того, как приложение сможет выполнить собственно запрос к БД, ради которого все и затевается.
Для этого есть решение - использование пула соединений (англ. connection pool).
Идея заключается в том, что программа запрашивает соединение не напрямую в БД, а через пул. Пул вначале создает соединение, как обычно, и возвращает программе.
Программа работает, как обычно используя JDBC (или любой ORM), но когда программа закрывает соединение, пул вместо того, чтоб его реально закрыть сохраняет его в список свободных к использованию соединений.
В следующий раз, например, при следующем запросе, пул отдаст готовое соединение, и не будет делать эту дорогостоящую операцию.
Дополнительное преимущество в том, что БД обычно не могут эффективно обрабатывать большое количество запросов одновременно, и пул соединений используется для ограничения одновременно открытых соединений и потому активных запросов к БД. Если приложение уже достигло лимита открытых приложений, то при запросе на открытие нового, оно будет ждать пока одно из используемых не будет возвращено в пул.
Примеры библиотек, реализующих пул соединений: HikariCP и commons DBCP и еще много других.
Приложению достаточно сконфигурировать пул и соединения создавать используя объект пула (вместо DriverManager). Пример для HikariCP:
// этот объект нужно создать один раз на все приложение при старте
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("user");
dataSource.setPassword("password");
dataSource.setMaximumPoolSize(10); // так можно ограничить размер пула
В запросах использовать:
try (Connection connection = dataSource.getConnection()) {
this.getLogger().info("MYSQL connected!");
// используем connection как обычно
}