簡體   English   中英

Java,tomcat8:在完成新的.war部署幾個小時后,MYSQL查詢開始變得很低

[英]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%的確切原因,但以下是您應該考慮的一些技巧:

  1. 最大連接數不足。 因此,嘗試將最大連接數增加到50。

  2. 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; } 
  3. 確保每次部署新戰爭時,您實際上都完全關閉了Tomcat。 在某些情況下,由於tomcat的活動,您的代碼永遠沒有機會釋放它獲得的連接。

  4. 嘗試找出有多少並發用戶正在嘗試訪問該應用程序。 如果這些超過了最大連接數。 您需要增加最大編號。 允許的連接數。

我正在進一步探索以找出更多可能的原因。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM