[英]Java, tomcat8: MYSQL query start to become very low after some hours that the deploy of a new .war has been done
由于tomcat8,我有一个Java网站(更确切地说是一个论坛),该网站由debian VPS托管。
该网站不是由我编写的,几天前已移交给我的所有权,我开始编写代码。
该应用程序的DAO(存储库)基于JOOQ,但我不喜欢它,因此我决定只使用mysql jdbc连接器来编写新查询。 目前,我只编写了一个新查询来获取论坛的所有部分。
该查询运行良好,但仅持续了几个小时。 部署新WAR后的几个小时,查询开始变得如此缓慢,以至于无法完成。 因此,我必须重新启动Web服务器。 重新启动后,查询运行良好,但仍然只能运行几个小时。
在下面,您可以看到MiscDAO类的代码,其中包含方法“ getForums”。 这是包含查询的方法。 dataSource(BasicDataSource)传递给基类,该基类将引用保留为类成员(与jooq变量相同)。
public class MiscDAO extends BaseDAO {
public MiscDAO(DSLContext jooq, BasicDataSource dataSource) {
super(jooq, dataSource);
}
public List<String> getForums() {
String query = "select forums.id\n" +
"from forums\n" +
"left join messages on forums.id=messages.forum\n" +
"and forums.enabled=1\n" +
"group by forums.id\n" +
"order by count(forums.id) desc;";
Connection conn = null;
ResultSet result = null;
PreparedStatement stat = null;
try {
conn = dataSource.getConnection();
stat = dataSource.getConnection()
.prepareStatement(query);
result = stat.executeQuery();
List<String> res = new ArrayList<String>();
while(result.next()) {
res.add(result.getString("id"));
}
return res;
} catch (SQLException e) {
return null;
}
finally {
CloseDbResources(conn, stat, result);
}
}
}
在此之下,BaseDAO类中的方法“ CloseDbResources”。
protected void CloseDbResources(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) { }
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) { }
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) { }
}
}
最后,DAO Factory中的init方法的代码,其中创建了MiscDAO类的实例。 请注意,MiscDAO类是将BasicDataSource接收为参数的唯一类,原因是我想开始从jooq转换为简单sql。
void init(Properties databaseConfig) throws ClassNotFoundException {
String driver = databaseConfig.getProperty("driverclass");
Class.forName(driver);
String username = databaseConfig.getProperty("username");
String password = databaseConfig.getProperty("password");
String url = databaseConfig.getProperty("url");
dataSource = new BasicDataSource();
dataSource.setMaxTotal(15);
dataSource.setMaxIdle(10);
dataSource.setMinIdle(3);
dataSource.setMaxWaitMillis(30000);
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setValidationQuery("SELECT 1");
dataSource.setValidationQueryTimeout(30);
DSLContext jooq = DSL.using(dataSource, SQLDialect.MYSQL);
authorsDAO = new AuthorsDAO(jooq);
threadsDAO = new ThreadsDAO(jooq);
messagesDAO = new MessagesDAO(jooq);
pollsDAO = new PollsDAO(jooq);
quotesDAO = new QuotesDAO(jooq);
bookmarksDAO = new BookmarksDAO(jooq);
adminDAO = new AdminDAO(jooq);
miscDAO = new MiscDAO(jooq, dataSource);
privateMsgDAO = new PrivateMsgDAO(jooq);
digestDAO = new DigestDAO(jooq);
loginsDAO = new LoginsDAO(jooq);
}
DAOFactory类是一个单例,因此在类被实例化时,init方法仅被调用一次。
static synchronized DAOFactory getInstance() {
if (instance == null) {
String persistenceName = FdTConfig.getProperty("persistence.name");
try {
instance = new DAOFactory();
instance.init(FdTConfig.getDatabaseConfig(persistenceName));
} catch (Exception e) {
LOG.fatal("Cannot instantiate Persistence '" + persistenceName + "'", e);
return null;
}
}
return instance;
}
在下面,您可以看到旧代码,该旧代码是由网站的旧所有者编写的,并且运行良好。 该查询基于jooq。 请注意,该查询用于从邮件表中获取论坛部分。 原因是表“论坛”不存在。 它是由我几天前创建的,所以这就是我编写新查询的原因。
public List<String> getForums() {
Result<Record1<String>> records = jooq.select(MESSAGES.FORUM)
.from(MESSAGES)
.where(MESSAGES.FORUM.isNotNull())
.groupBy(MESSAGES.FORUM)
.orderBy(DSL.count(MESSAGES.ID).desc(), MESSAGES.FORUM.asc())
.fetch();
List<String> res = new ArrayList<String>(records.size());
for (Record1<String> record : records) {
res.add(record.getValue(FORUMS.ID));
}
return res;
}
在DaoFactory中,曾经以这种方式创建MiscDAO类的实例(唯一的区别是该类没有将BaiscDataSource用作参数,因此BasicDataSource封装在jooq内,并且从未直接使用。
miscDAO = new MiscDAO(jooq);
您可以在下面的日志文件中读取错误,该错误是在部署新战争数小时后发生的。
2018-04-01 13:46:50,154 ERROR MainServlet:509 -
java.lang.reflect.InvocationTargetException at
sun.reflect.GeneratedMethodAccessor98.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497) at
com.forumdeitroll.servlets.MainServlet.doDo(MainServlet.java:241) at
com.forumdeitroll.servlets.MainServlet.doGetPost(MainServlet.java:120)
at com.forumdeitroll.servlets.MainServlet.doGet(MainServlet.java:110)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618) at
javax.servlet.http.HttpServlet.service(HttpServlet.java:725) at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.forumdeitroll.filters.MainFilter.doFilter(MainFilter.java:86)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
at
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1081)
at
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
at
org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1580)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1537)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745) Caused by:
org.jooq.exception.DataAccessException: Error getting connection from
data source org.apache.commons.dbcp2.BasicDataSource@41b7afc6 at
org.jooq.impl.DataSourceConnectionProvider.acquire(DataSourceConnectionProvider.java:89)
at
org.jooq.impl.DefaultExecuteContext.connection(DefaultExecuteContext.java:583)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:311) at
org.jooq.impl.AbstractResultQuery.fetchLazy(AbstractResultQuery.java:319)
at
org.jooq.impl.AbstractResultQuery.fetchLazy(AbstractResultQuery.java:306)
at
org.jooq.impl.AbstractResultQuery.fetchAny(AbstractResultQuery.java:523)
at org.jooq.impl.SelectImpl.fetchAny(SelectImpl.java:2512) at
com.forumdeitroll.persistence.dao.BaseDAO.getAuthor(BaseDAO.java:51)
at
com.forumdeitroll.persistence.dao.MessagesDAO.recordToDTO(MessagesDAO.java:289)
at
com.forumdeitroll.persistence.dao.MessagesDAO.getMessages(MessagesDAO.java:110)
at com.forumdeitroll.servlets.Messages.getMessages(Messages.java:781)
at com.forumdeitroll.servlets.Messages.getMessages(Messages.java:87)
... 33 more Caused by: java.sql.SQLException: Cannot get a
connection, pool error Timeout waiting for idle object at
org.apache.commons.dbcp2.PoolingDataSource.getConnection(PoolingDataSource.java:142)
at
org.apache.commons.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:1533)
at
org.jooq.impl.DataSourceConnectionProvider.acquire(DataSourceConnectionProvider.java:86)
... 44 more Caused by: java.util.NoSuchElementException: Timeout
waiting for idle object at
org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
at
org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at
org.apache.commons.dbcp2.PoolingDataSource.getConnection(PoolingDataSource.java:134)
... 46 more
这确实很难检测。 我不确定100%的确切原因,但以下是您应该考虑的一些技巧:
最大连接数不足。 因此,尝试将最大连接数增加到50。
Connection
可能无法正确关闭。 从代码的角度看,事情看起来不错,但我仍然建议try with resource
使用try with resource
因为它在内部确保关闭连接。
try(conn = dataSource.getConnection(); stat = dataSource.getConnection() .prepareStatement(query); result = stat.executeQuery();) { List<String> res = new ArrayList<String>(); while(result.next()) { res.add(result.getString("id")); } return res; } catch (SQLException e) { return null; }
确保每次部署新战争时,您实际上都完全关闭了Tomcat。 在某些情况下,由于tomcat的活动,您的代码永远没有机会释放它获得的连接。
尝试找出有多少并发用户正在尝试访问该应用程序。 如果这些超过了最大连接数。 您需要增加最大编号。 允许的连接数。
我正在进一步探索以找出更多可能的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.