2010年10月23日 #
數(shù)據(jù)庫(kù)鏈接 常見(jiàn)的問(wèn)題:
1. 數(shù)據(jù)庫(kù)意外重啟后,原先的數(shù)據(jù)庫(kù)連接池能自動(dòng)廢棄老的無(wú)用的鏈接,建立新的數(shù)據(jù)庫(kù)鏈接
2. 網(wǎng)絡(luò)異常中斷后,原先的建立的tcp鏈接,應(yīng)該能進(jìn)行自動(dòng)切換。比如網(wǎng)站演習(xí)中的交換機(jī)重啟會(huì)導(dǎo)致網(wǎng)絡(luò)瞬斷
3. 分布式數(shù)據(jù)庫(kù)中間件,比如cobar會(huì)定時(shí)的將空閑鏈接異常關(guān)閉,客戶端會(huì)出現(xiàn)半開(kāi)的空閑鏈接。
大致思考解決思路:
1. sql心跳檢查(主動(dòng)式)
2. 拿鏈接嘗試一下,發(fā)現(xiàn)處理失敗丟棄鏈接,探雷的請(qǐng)求會(huì)失敗幾個(gè) (犧牲小我,完成大我的精神)
3. 設(shè)置合理的空閑鏈接的超時(shí)時(shí)間,避免半開(kāi)鏈接(懶模式,解決半開(kāi)鏈接)
下面我們來(lái)看看,在dbcp中是如何實(shí)現(xiàn)。
sql心跳檢查
sql validate配置
<property name="testWhileIdle"><value>true</value></property>
<property name="testOnBorrow"><value>false</value></property>
<property name="testOnReturn"><value>false</value></property>
<property name="validationQuery"><value>select sysdate from dual</value></property>
<property name="validationQueryTimeout"><value>1</value></property>
<property name="timeBetweenEvictionRunsMillis"><value>30000</value></property>
<property name="numTestsPerEvictionRun"><value>16</value></property>
參數(shù)說(shuō)明
dbcp是采用了commons-pool做為其連接池管理,testOnBorrow,testOnReturn, testWhileIdle是pool是提供的幾種校驗(yàn)機(jī)制,通過(guò)外部鉤子的方式回調(diào)dbcp的相關(guān)數(shù)據(jù)庫(kù)鏈接(validationQuery)校驗(yàn), dbcp相關(guān)外部鉤子類:PoolableConnectionFactory,繼承于common-pool PoolableObjectFactory , dbcp通過(guò)GenericObjectPool這一入口,進(jìn)行連接池的borrow,return處理。
具體參數(shù)描述:
1. testOnBorrow : 顧明思義,就是在進(jìn)行borrowObject進(jìn)行處理時(shí),對(duì)拿到的connection進(jìn)行validateObject校驗(yàn)
2. testOnReturn : 顧明思義,就是在進(jìn)行returnObject對(duì)返回的connection進(jìn)行validateObject校驗(yàn),個(gè)人覺(jué)得對(duì)數(shù)據(jù)庫(kù)連接池的管理意義不大
3. testWhileIdle : 關(guān)注的重點(diǎn),GenericObjectPool中針對(duì)pool管理,起了一個(gè)異步Evict的TimerTask定時(shí)線程進(jìn)行控制(可通過(guò)設(shè)置參數(shù) timeBetweenEvictionRunsMillis>0),定時(shí)對(duì)線程池中的鏈接進(jìn)行validateObject校驗(yàn),對(duì)無(wú)效的鏈接進(jìn)行關(guān)閉后,會(huì)調(diào)用ensureMinIdle,適當(dāng)建立鏈接保證最小的minIdle連接數(shù)。
4. timeBetweenEvictionRunsMillis,設(shè)置的Evict線程的時(shí)間,單位ms,大于0才會(huì)開(kāi)啟evict檢查線程
5. validateQuery, 代表檢查的sql
6. validateQueryTimeout, 代表在執(zhí)行檢查時(shí),通過(guò)statement設(shè)置,statement.setQueryTimeout(validationQueryTimeout)
7. numTestsPerEvictionRun,代表每次檢查鏈接的數(shù)量,建議設(shè)置和maxActive一樣大,這樣每次可以有效檢查所有的鏈接.
Sql心跳檢查幾點(diǎn)思考:
1.性能問(wèn)題。
目前網(wǎng)站的應(yīng)用大部分的瓶頸還是在I/O這一塊,大部分的I/O還是在數(shù)據(jù)庫(kù)的這一層面上,每一個(gè)請(qǐng)求可能會(huì)調(diào)用10來(lái)次SQL查詢,如果不走事務(wù),一個(gè)請(qǐng)求會(huì)重復(fù)獲取鏈接,如果每次獲取鏈接,比如在testOnBorrow都進(jìn)行validateObject,性能開(kāi)銷不是很能接受,可以假定一次SQL操作消毫0.5~1ms(一般走了網(wǎng)絡(luò)請(qǐng)求基本就這數(shù))
2.成本和收益
網(wǎng)站異常數(shù)據(jù)庫(kù)重啟,網(wǎng)絡(luò)異常斷開(kāi)的頻率是非常低的,一般也就在數(shù)據(jù)庫(kù)升級(jí),演習(xí)維護(hù)時(shí)才會(huì)進(jìn)行,而且一般也是選在晚上,訪問(wèn)量相對(duì)比較低的請(qǐng)求,而且一般會(huì)有人員值班關(guān)注,所以異步的validateObject是可以接受,但一個(gè)前提需要確保能保證在一個(gè)合理的時(shí)間段內(nèi),數(shù)據(jù)庫(kù)能完成自動(dòng)重聯(lián)。
請(qǐng)求探雷
相關(guān)配置
dbcp自身默認(rèn)支持,不需要配置
原理描述
common-pools通過(guò)borrowObject , returnObject完成連接的獲取和釋放,正常的情況是一次請(qǐng)求中borrow和return是一對(duì)的,有借就有還。
但在準(zhǔn)備returnObject時(shí),dbcp會(huì)做一件事,就是看看這個(gè)object是否已經(jīng)是壞了的,如果壞了就直接丟了,就直接給丟棄了。
代碼層面:
1. 在dbcp中PoolingDataSource(實(shí)現(xiàn)DataSource接口)調(diào)用 PoolableConnection(dbcp connnection相關(guān)的pool delegate操作)進(jìn)行相應(yīng)關(guān)閉時(shí),會(huì)檢查_conn.isClosed(),針對(duì)DataSource如果isClosed返回為 true的則不調(diào)用returnObject,直接丟棄了鏈接。
2. _conn.isClosed()是否保險(xiǎn),從jdk的api描述中: A connection is closed if the method close has been called on it or if certain fatal errors have occurred. 里面提供兩種情況,一種就是被調(diào)用了closed方法,另一種就是出現(xiàn)一些異常,說(shuō)的比較含糊。
空閑鏈接檢查
相關(guān)配置
<property name="minEvictableIdleTimeMillis"><value>18000000</value></property>
<property name="removeAbandoned"><value>true</value></property>
<property name="removeAbandonedTimeout"><value>180</value></property>
參數(shù)說(shuō)明
1.minEvictableIdleTimeMillis dbcp默認(rèn)是30分,需要開(kāi)啟異步線程Evict,否則不生效。原理很簡(jiǎn)單,就是通過(guò)一個(gè)異步線程,每次檢查connnection上一次使用的時(shí)間戳,看看是否已經(jīng)超過(guò)這個(gè)timeout時(shí)間設(shè)置。
2. removeAbandoned , removeAbandonedTimeout,主要是用于在出現(xiàn)鏈接緊張時(shí)候,會(huì)掃描一些鏈接未超過(guò)removeAbandonedTimeout時(shí)間還未被釋放,會(huì)主動(dòng)的關(guān)閉該鏈接。
適用情況
1. 我們使用的cobar后端會(huì)有定時(shí)關(guān)閉空閑鏈接的操作,默認(rèn)的空閑鏈接timeout時(shí)間為1小時(shí),和其他oracle , mysql各不相同,所以設(shè)置好這個(gè)空閑鏈接的timeout時(shí)間還是挺重要.
2. 一般會(huì)是幾種情況出現(xiàn)需要removeAbandoned:
* 代碼未在finally釋放connection , 不過(guò)我們都用sqlmapClientTemplate,底層都有鏈接釋放的過(guò)程
* 遇到數(shù)據(jù)庫(kù)死鎖。以前遇到過(guò)后端存儲(chǔ)過(guò)程做了鎖表操作,導(dǎo)致前臺(tái)集群中連接池全都被block住,后續(xù)的業(yè)務(wù)處理因?yàn)槟貌坏芥溄铀卸继幚硎×恕?/span>
聊聊c3p0配置
還有我們配置的c3p0所謂的自動(dòng)重連的3個(gè)參數(shù),
<prop key="acquireRetryAttempts">30</prop>
<prop key="acquireRetryDelay">1000</prop>
<prop key="breakAfterAcquireFailure">false</prop>
個(gè)人覺(jué)得就是一個(gè)誤導(dǎo),這幾個(gè)配置只是在從連接池獲取鏈接時(shí),獲取失敗多嘗試幾次,因?yàn)槲覀儚膒ool從獲取鏈接最多只會(huì)等待固定timeout時(shí)間。
如果要達(dá)到自動(dòng)重連的效果,必須要c3p0支持請(qǐng)求探雷或者是sql心跳檢查功能,能自動(dòng)的剔除無(wú)效的鏈接。
可見(jiàn)c3p0官方文檔描述:http://www.mchange.com/projects/c3p0/index.html#configuring_recovery
最后:
Dbcp將是我們以后數(shù)據(jù)庫(kù)驅(qū)動(dòng)選擇的趨勢(shì),最后我們?nèi)绾芜x擇如何自動(dòng)重連,這個(gè)也得根據(jù)我們的應(yīng)用場(chǎng)景而定。比如只讀的web系統(tǒng),后臺(tái)業(yè)務(wù)系統(tǒng),任務(wù)系統(tǒng)可能處理方式就不同。
只讀Web系統(tǒng):可采取請(qǐng)求探雷的策略,也就失敗連接池個(gè)數(shù)的請(qǐng)求,失敗了頁(yè)面刷新一次就好。
后臺(tái)業(yè)務(wù)系統(tǒng):一般業(yè)務(wù)都涉及數(shù)據(jù)庫(kù)的寫(xiě)操作,很多數(shù)據(jù)不可重入,一次處理失敗后就只能靠手工干預(yù)處理。這時(shí)候得考慮是否需要使用sql心跳檢查,比如testOnBorrow或者testWhileIdle.