前段时间系统在晚上突然会出现应用不可用的情况,只有经过长时间(大约10分钟)或者应用重启后,访问才能再次正常。查看日志,其中报大量的数据库连接异常。如下:
Caused by: com.ibatis.common.jdbc.exception.NestedSQLException: --- The error occurred in sqlmaps/price.xml. --- The error occurred while applying a parameter map. --- Check the dayprice.QUERY-PRICE-BY-THEME-ID-InlineParameterMap. --- Check the parameter mapping for the 'themeId' property. --- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed. at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWithCallback(GeneralStatement.java:185) at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForList(GeneralStatement.java:123) at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExecutorDelegate.java:615) at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExecutorDelegate.java:589) at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMapSessionImpl.java:118) at org.springframework.orm.ibatis.SqlMapClientTemplate$3.doInSqlMapClient(SqlMapClientTemplate.java:298) at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:209) ... 50 more Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed.
主库和备份库之接的切换。为什么要做这个切换呢?这是由于数据库那边有一个HA机制。所谓HA机制就是数据库的高可用性保障机制,目前是通过一个心跳机制,每隔一段时间会由备份库去ping主库,如果无法ping通,则会进行一次主库和备份库之间的切换。
一开始,认为这是由于数据库重启,导致了不能访问数据库从而导致了系统不可用。但是数据库重启时间只有20秒左右,应该无法导致这么长时间的不可用。然后在本地机器上安装了mysql数据库进行了测试发现,系统在数据库重启,正常可用之后仍然无法正常访问。后发现这是由于在数据库重启之后,在连接池中已经在使用的连接均会失效,如果后续的访问不幸取到这些连接的话,则仍然无法正常访问数据库。
那么如何使应用在数据库连接失效之后,能够将这些连接马上失效,并取到可用的连接呢?其实很简单,只需要在数据库连接池配置中加一个配置项即可。目前看了一下各个应用,有三种不同的实现方式:
如果使用的proxool,则可以在数据库的连接url中加一个autoReconnect参数即可,如下:
<property name="connection.url"> <!CDATA[$system_db_url?autoReconnect=true&setUnicode=true&characterEncoding=GBK ]]> </property>
如果使用的是DBCP,则有两种方式,如下:
方式A:使用先验SQL来验证连接是否可用。
<check-valid-connection-sql>SELECT @@SQL_PING</check-valid-connection-sql>
方式B:使用异常捕获来验证连接是否可用。
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
DBCP中的两种不同的配置方式,各有长处吧,用check-valid-connection-sql可以在连接失效时重新取得连接来尽量保证此次数据库请求正常,而缺点是每次执行sql都要额外执行一次验证sql;而exception-sorter-class- name虽然不用每次额外执行一条sql,但是在取到失效连接那次访问依然不能使用。