【问题标题】:why lost Mysql connection pool after a period?为什么在一段时间后丢失了Mysql连接池?
【发布时间】:2015-06-09 08:02:51
【问题描述】:

我的应用同时使用Mysql和Postgres,web服务器是tomcat 7.0。应用程序运行一段时间后,如 15、16 小时,它丢失了 mysql 连接池,并且我在日志中收到“无法获取连接,池错误超时等待空闲对象”错误消息。所以我需要启动tomcat来重新启动应用程序以再次重新连接到Mysql连接池。我在完成查询后关闭每个连接。这在 Postgres 上从来没有发生过,只是在 Mysql 上,所以……很奇怪。

我使用 Singleton 连接到 Mysql 和 Postgres 的连接池。下面是Mysql的代码。

public class DB {
    private static DB database = new DB();
    private DataSource ds;

    private DB() {
        try {
            InitialContext ic = new InitialContext();
            ds = (DataSource)ic.lookup("java:comp/env/jdbc/mobile_recharge");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static DB getInstance() {
        return database;
    }

    public DataSource getDS() throws SQLException {
        return ds;
    }
}

其中一个查询:

public boolean preSaveData(HashMap<String, String> mapXML, String date, String serialNum) {
    try {
        con = open();
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        log.writeLog(mapXML.get("terminalId"), mapXML.get("date_time"), "fails to insert order info into mysql: " + e.getMessage());
        return false;
    }

    String table = new JudgeTidField().table(mapXML.get("tidField"));

    String query = "insert into " + table + " (Terminal_id, Yewu, Phone_number, Company, " + 
                   "Order_money, Balance, Actual_money, Order_record_date, Recharge_record_date, Province, Serial_number) values " +
                   "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";

    try {
        presta = (PreparedStatement) con.prepareStatement(query);
        presta.setString(1, mapXML.get("terminalId"));
        presta.setString(2, "recharge");
        presta.setString(3, mapXML.get("phoneNum"));
        presta.setString(4, mapXML.get("type"));
        presta.setFloat(5, Float.parseFloat(mapXML.get("price")));
        presta.setFloat(6, (float) 0.000);
        presta.setFloat(7, Float.parseFloat(mapXML.get("actualMoney")));
        presta.setString(8, date);
        presta.setString(9, date);
        presta.setString(10, mapXML.get("province"));
        presta.setString(11, serialNum);

        presta.executeUpdate();
        log.writeLog(mapXML.get("terminalId"), mapXML.get("date_time"), "success to insert order info into mysql");
        return true;
    } catch (SQLException e) {
        e.getMessage();
        log.writeLog(mapXML.get("terminalId"), mapXML.get("date_time"), "fail to insert order info into mysql: " + e.getMessage());
        return false;
    } finally {
        try {
            presta.close();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

在 context.xml 中:

<Context  docBase="Mobile" path="/Mobile" reloadable="true">
  <Resource
    name="jdbc/mobile_recharge"
    auth="Container"
    type="javax.sql.DataSource"
    maxActive="500"
    maxIdle="5"
    maxWait="5000"
    driverClassName="com.mysql.jdbc.Driver"
    username="root"
    password="xxxxxxxx"
    url="jdbc:mysql://xxx.xxx.x.xxx:3306/mobile_recharge?characterEncoding=utf8" />

  <Resource
    name="jdbc/pgsql"
    type="javax.sql.DataSource"
    maxActive="500"
    maxIdle="2"
    username="postgres"
    maxWait="5000"
    driverClassName="org.postgresql.Driver"
    password="xxxxxxxx"
    url="jdbc:postgresql://xxx.xxx.x.xxx:5555/runningacounts"/>
</Context>

在 web.xml 中:

<resource-ref> 
  <description>MysqlDB Connection</description> 
  <res-ref-name>jdbc/mobile_recharge</res-ref-name> 
  <res-type>javax.sql.DataSource</res-type> 
  <res-auth>Container</res-auth> 
</resource-ref>
<resource-ref> 
  <description>PGDB Connection</description> 
  <res-ref-name>jdbc/pgsql</res-ref-name> 
  <res-type>javax.sql.DataSource</res-type> 
  <res-auth>Container</res-auth> 
</resource-ref>

【问题讨论】:

  • 您的应用服务器和数据库服务器之间有防火墙吗?
  • 假设你连接到MySQL服务器做得很好,你确定你在使用连接对象后释放了对象引用吗?否则池继续创建并达到maxActive,然后无法再创建一个。此外,maxActive=500?您已经调整 MySQL 以允许 500 个活动连接?默认值为 151。
  • 肯回答表明您已达到 maxActive 限制:根据您的代码,Connection 对象“con”和 PreparedStatement“presta”是成员字段。尽量使用局部变量,如果JDK1.7的可用try-with语句对每个Closeable类进行说明。
  • (我问防火墙的原因是因为如果一段时间不使用连接可能会失去状态)
  • @Alastair McCormack 应用服务器和数据库服务器之间没有防火墙

标签: java mysql tomcat


【解决方案1】:

至少对于 MySQL,我建议您研究连接池验证和清理选项,特别是 validationQuerytestWhileIdletestOnBorrowtimeBetweenEvictionRunsMillisremoveAbandonedremoveAbandonedTimeout 等 - 更多详细信息请点击此处:http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html#Common_Attributes

我在 Tomcat 中通过 JDBC 连接 MySQL 时遇到过类似的问题,这些属性的合理应用解决了这个问题,因此在长时间的空闲期后无需重新启动 Tomcat。希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2017-05-09
    • 2018-10-13
    • 2014-03-11
    • 2021-12-16
    • 2020-04-07
    • 1970-01-01
    相关资源
    最近更新 更多