異常信息
org.hibernate.exception.JDBCConnectionException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:
74
)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:
43
)
.......
Caused by: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error:
** BEGIN NESTED EXCEPTION **
com.mysql.jdbc.CommunicationsException
MESSAGE: Communications link failure due to underlying exception:
** BEGIN NESTED EXCEPTION **
java.net.SocketException
MESSAGE: Broken pipe
STACKTRACE:
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
......
** END NESTED EXCEPTION **
原因分析
查看了Mysql的文檔暂殖,以及Connector/J的文檔以及在線說明發(fā)現(xiàn)抵乓,出現(xiàn)這種異常的原因是:
Mysql服務(wù)器默認(rèn)的“wait_timeout”是8小時摆寄,也就是說一個connection空閑超過8個小時颂碘,Mysql將自動斷開該connection。這就是問題的所在返敬,在C3P0 pools中的connections如果空閑超過8小時遂庄,Mysql將其斷開,而C3P0并不知道該connection已經(jīng)失效救赐,如果這時有Client請求connection涧团,C3P0將該失效的Connection提供給Client只磷,將會造成上面的異常经磅。
解決方案
解決的方法有3種:
增加 wait_timeout 的時間泌绣。
減少 Connection pools 中 connection 的 lifetime。
測試 Connection pools 中 connection 的有效性预厌。
當(dāng)然最好的辦法是同時綜合使用上述3種方法阿迈,下面就 DBCP、C3P0 和 simple jdbc dataSource 分別做一說明轧叽,假設(shè) wait_timeout 為默認(rèn)的8小時
DBCP 增加以下配置信息:
validationQuery = "select 1"testWhileIdle = "true"http://some positive integertimeBetweenEvictionRunsMillis = 3600000//set to something smaller than 'wait_timeout'minEvictableIdleTimeMillis = 18000000//if you don't mind a hit for every getConnection(), set to "true"testOnBorrow = "true"
C3P0 增加以下配置信息:
//獲取connnection時測試是否有效testConnectionOnCheckin = true//自動測試的table名稱automaticTestTable=C3P0TestTable//set to something much less than wait_timeout, prevents connections from going staleidleConnectionTestPeriod = 18000//set to something slightly less than wait_timeout, preventing 'stale' connections from being handed outmaxIdleTime = 25000//if you can take the performance 'hit', set to "true"testConnectionOnCheckout = true
simple jdbc dataSource 增加以下配置信息:
Pool.PingQuery = select 1Pool.PingEnabled = truePool.PingConnectionsOlderThan = 0//對于空閑的連接一個小時檢查一次Pool.PingConnectionsNotUsedFor = 3600000
其他方案(不推薦)
對于 MySQL5 之前的版本苗沧,如 Mysql4.x,只需要修改連接池配置中的 URL炭晒,添加一個參數(shù):autoReconnect=true(如jdbc:mysql://hostaddress:3306/schemaname?autoReconnect=true)待逞,如果是 MySQL5 及以后的版本,則需要修改 my.cnf(或者my.ini) 文件网严,在 [mysqld] 后面添加上:
wait_timeout = n
interactive-timeout = n
其中 n 為服務(wù)器關(guān)閉交互式連接前等待活動的秒數(shù)识樱。可是就部署而言每次修改 my.ini 比較麻煩震束,而且 n 等于多少才是合適的值呢怜庸? 所以并不推薦這個解決辦法。)
- Apache-DBCP
? BasicDataSource 相關(guān)的參數(shù)說明
dataSource: 要連接的 datasource (通常我們不會定義在 server.xml)
defaultAutoCommit: 對于事務(wù)是否 autoCommit, 默認(rèn)值為 true
defaultReadOnly: 對于數(shù)據(jù)庫是否只能讀取, 默認(rèn)值為 false
driverClassName:連接數(shù)據(jù)庫所用的 JDBC Driver Class,
maxActive: 可以從對象池中取出的對象最大個數(shù)垢村,為0則表示沒有限制割疾,默認(rèn)為8
maxIdle: 最大等待連接中的數(shù)量,設(shè) 0 為沒有限制 (對象池中對象最大個數(shù))
minIdle:對象池中對象最小個數(shù)
maxWait: 最大等待秒數(shù), 單位為 ms, 超過時間會丟出錯誤信息
password: 登陸數(shù)據(jù)庫所用的密碼
url: 連接數(shù)據(jù)庫的 URL
username: 登陸數(shù)據(jù)庫所用的帳號
validationQuery: 驗證連接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned: 是否自我中斷, 默認(rèn)是 false
removeAbandonedTimeout: 幾秒后會自我中斷, removeAbandoned 必須為 true
logAbandoned: 是否記錄中斷事件, 默認(rèn)為 false
minEvictableIdleTimeMillis:大于0 ,進行連接空閑時間判斷嘉栓,或為0宏榕,對空閑的連接不進行驗證;默認(rèn)30分鐘
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔侵佃,如果小于等于0麻昼,不會啟動檢查線程,默認(rèn)-1
testOnBorrow:取得對象時是否進行驗證趣钱,檢查對象是否有效涌献,默認(rèn)為false
testOnReturn:返回對象時是否進行驗證,檢查對象是否有效首有,默認(rèn)為false
testWhileIdle:空閑時是否進行驗證燕垃,檢查對象是否有效,默認(rèn)為false
? 在使用DBCP的時候井联,如果使用默認(rèn)值卜壕,則數(shù)據(jù)庫連接因為某種原因斷掉后,再從連接池中取得連接又不進行驗證烙常,這時取得的連接實際上就會是無效的數(shù)據(jù)庫連接轴捎。因此為了防止獲得的數(shù)據(jù)庫連接失效鹤盒,在使用的時候最好保證:
username: 登陸數(shù)據(jù)庫所用的帳號
validationQuery:SELECT COUNT(*) FROM DUAL
testOnBorrow、testOnReturn侦副、testWhileIdle:最好都設(shè)為true
minEvictableIdleTimeMillis:大于0 侦锯,進行連接空閑時間判斷,或為0秦驯,對空閑的連接不進行驗證
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔尺碰,如果小于等于0,不會啟動檢查線程
? PS:在構(gòu)造GenericObjectPool [BasicDataSource在其createDataSource () 方法中也會使用GenericObjectPool] 時译隘,會生成一個內(nèi)嵌類Evictor亲桥,實現(xiàn)自Runnable接口。如果timeBetweenEvictionRunsMillis大于0固耘,每過timeBetweenEvictionRunsMillis毫秒Evictor會調(diào)用evict()方法题篷,檢查對象的閑置時間是否大于 minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小于等于0時則忽略,默認(rèn)為30分鐘)厅目,是則銷毀此對象番枚,否則就激活并校驗對象,然后調(diào)用ensureMinIdle方法檢查確保池中對象個數(shù)不小于_minIdle璧瞬。在調(diào)用returnObject方法把對象放回對象池户辫,首先檢查該對象是否有效,然后調(diào)用PoolableObjectFactory 的passivateObject方法使對象處于非活動狀態(tài)嗤锉。再檢查對象池中對象個數(shù)是否小于maxIdle渔欢,是則可以把此對象放回對象池,否則銷毀此對象
? 上述特性的可設(shè)置性已在代碼中驗證瘟忱,具體性能是否能實現(xiàn)有待實際驗證
- C3P0
? C3P0的官方example中使用的數(shù)據(jù)源為ComboPooledDataSource奥额,網(wǎng)上一篇文章詳細介紹了C3P0連接池配置中各項含義[這些配置項的含義在下載解壓c3p0的壓縮包之后目錄的doc\index.html中的Configuration部分也有詳細的介紹,這里偷下懶:P]访诱,現(xiàn)摘錄如下:
<c3p0-config>
<default-config>
<property name="acquireIncrement">3</property>
<property name="acquireRetryAttempts">30</property>
<property name="acquireRetryDelay">1000</property>
<property name="autoCommitOnClose">false</property>
<property name="automaticTestTable">Test</property>
<property name="breakAfterAcquireFailure">false</property>
<property name="checkoutTimeout">100</property>
<property name="connectionTesterClassName"></property>
<property name="factoryClassLocation">null</property>
<property name="forceIgnoreUnresolvedTransactions">false</property>
<property name="idleConnectionTestPeriod">60</property>
<property name="initialPoolSize">3</property>
<property name="maxIdleTime">60</property>
<property name="maxPoolSize">15</property>
<property name="maxStatements">100</property>
<property name="maxStatementsPerConnection"></property>
<property name="numHelperThreads">3</property>
<property name="overrideDefaultUser">root</property>
<property name="overrideDefaultPassword">password</property>
<property name="password"></property>
<property name="preferredTestQuery">select id from test where id=1</property>
<property name="propertyCycle">300</property>
<property name="testConnectionOnCheckout">false</property>
<property name="testConnectionOnCheckin">true</property>
<property name="user">root</property>
<property name="usesTraditionalReflectiveProxies">false</property>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">25</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">0</property>
<user-overrides user="swaldman">
</user-overrides>
</default-config>
<named-config name="dumbTestConfig">
<property name="maxStatements">200</property>
<user-overrides user="poop">
<property name="maxStatements">300</property>
</user-overrides>
</named-config>
</c3p0-config>
? 上述特性的可設(shè)置性已在代碼中驗證垫挨,具體性能是否能實現(xiàn)有待實際驗證
? 從配置項的內(nèi)容來看,C3P0和DBCP都有比較詳細的有關(guān)連接檢測保證的配置触菜,我們可以看到C3P0可以控制數(shù)據(jù)源內(nèi)加載的PreparedStatements數(shù)量九榔,并且可以設(shè)置幫助線程的數(shù)量來提升JDBC操作的速度,這些是DBCP未提供的涡相;另外從網(wǎng)絡(luò)上的評價來看哲泊,DBCP出現(xiàn)Bug的頻率要大于C3P0,不過這一點有待于我們自己實際的檢測催蝗。
- Proxool
? Proxool的使用和dbcp以及c3p0稍有不同切威,我們需要并且只需要在使用基本的java.sql.DriverManager之前加載org.logicalcobwebs.proxool.ProxoolDriver驅(qū)動類,并且按照proxool定義的url格式 ["proxool." + alias + ":" + driverClass + ":" + driverUrl ,其中alias是為連接池自定義的別名] 來獲得connection丙号;具體的可以參看proxool doc下的UserGuide先朦,或本文所附的示例代碼缰冤。下面對連接池的特性配置作詳細說明 [這個是自己翻譯的,不一定準(zhǔn)確喳魏,有問題時請參看doc下的Properties ~]棉浸。
n fatal-sql-exception
以逗號隔開的異常列表,當(dāng)設(shè)置了此項之后截酷,每當(dāng)出現(xiàn)SQLException時都將與列表中異常項作比較涮拗,如果匹配則認(rèn)為出現(xiàn)fatal異常乾戏,這將導(dǎo)致connection被丟棄迂苛,并且不論出現(xiàn)任何情況該異常將被重拋一次以通知用戶發(fā)生的情況。默認(rèn)值為null
n fatal-sql-exception-wrapper-class
如果配置了fatal-sql-exception鼓择,則默認(rèn)的操作是丟 棄引起SQLException的原因而只是拋出原始異常三幻。使用fatal-sql-exception-wrapper-class這個特性可以將 SQLException包裝到繼承SQLException或RunTimeException的任何異常類里。Proxool提供了兩個類供使用 FatalSQLException和FatalRunTimeException呐能;使用這兩個類的話就將該選項設(shè)置為 'org.logicalcobwebs.proxool.FatalSQLException'或者 'org.logicalcobwebs.proxool.FatalRuntimeException'念搬。默認(rèn)值為null
n house-keeping-sleep-time
proxool自動偵察各個連接狀態(tài)的時間間隔(毫秒),偵察到空閑的連接就馬上回收,超時的銷毀,默認(rèn)值為30秒
n house-keeping-test-sql
如果偵察線程發(fā)現(xiàn)閑置連接摆出,則會使用這個SQL語句來對這些連接進行檢查朗徊;這項設(shè)置的語句應(yīng)該能夠被很快的執(zhí)行,例如查詢當(dāng)前時間 [info.setProperty("proxool.house-keeping-test-sql", "select CURRENT_DATE");] 偎漫。如果不設(shè)置則該選項被忽略
n injectable-connection-interface爷恳、injectable-statement-interface、injectable-prepared-statement-interface象踊、injectable-callable-statement-interface
n jmx
如果此項設(shè)為true温亲,則連接池將被以名稱"Proxool:type=Pool, name=<alias>"注冊為JMS Server的MBean。默認(rèn)值為false
n jmx-agent-id
當(dāng)且僅當(dāng)jmx選項設(shè)為true時使用杯矩,為以逗號分隔的連接持注冊到的JMS代理名稱列表栈虚;如果不設(shè)置則所有注冊的JMX Server都將被使用
n maximum-active-time
線程最大存活時間,超過此時間的線程將被守護線程kill掉史隆,默認(rèn)值為5分鐘
n maximum-connection-count
到數(shù)據(jù)庫的最大連接數(shù)魂务,超過了這個連接,再有請求時泌射,就排在隊列中等候粘姜,最大的等待請求數(shù)由simultaneous-build-throttle決定;默認(rèn)值為15
n maximum-connection-lifetime
連接最大存活時間魄幕,毫秒為單位相艇,默認(rèn)值為4小時
n minimum-connection-count
不管是否被使用都保持開放的最小連接數(shù),默認(rèn)值為5
n overload-without-refusal-lifetime
用來判斷連接池狀態(tài)纯陨,如果在此選項設(shè)置時間內(nèi)(毫秒為單位)拒絕了連接坛芽,則認(rèn)為過負載留储。默認(rèn)值為60秒
n prototype-count
最少保持的空閑連接數(shù),注意與minimum-connection-count區(qū)分咙轩。默認(rèn)值為0
n simultaneous-build-throttle
最大的等待請求數(shù)获讳,默認(rèn)值為10
n test-before-use
如果設(shè)為true則connection在使用前將以house-keeping-test-sql設(shè)置的語句測試,如果測試不通過則該connection被丟棄并會重新分配一個connection活喊。默認(rèn)為false
n test-after-use
如果設(shè)為true則connection在關(guān)閉(放回連接池)前將以house-keeping-test-sql設(shè)置的語句測試丐膝,如果測試不通過connection將被丟棄。默認(rèn)值為false
? 與其它連接池特性的設(shè)置方法不同钾菊,Proxool不提供相應(yīng)的set方法帅矗,所有特性都要以諸如info.setProperty("proxool.jmx", "false");方式設(shè)定
? 上述特性的可設(shè)置性已在代碼中驗證,具體性能是否能實現(xiàn)有待實際驗證