溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

數(shù)據(jù)庫連接池如何配置

發(fā)布時(shí)間:2021-12-08 09:33:58 來源:億速云 閱讀:863 作者:小新 欄目:數(shù)據(jù)庫

這篇文章將為大家詳細(xì)講解有關(guān)數(shù)據(jù)庫連接池如何配置,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

一、連接池配置


1.1 maxWait

參數(shù)表示從連接池獲取連接的超時(shí)等待時(shí)間,單位毫秒,需要注意這個參數(shù)只管理獲取連接的超時(shí)。獲取連接等待的直接原因是池子里沒有可用連接,具體包括:連接池未初始化,連接長久未使用已被釋放,連接使用中需要新建連接,或連接池已耗盡需等待連接用完后歸還。這里有一個很關(guān)鍵的點(diǎn)是 maxWait 未配置或者配置為 0 時(shí),表示不設(shè)等待超時(shí)時(shí)間(可能與一些人認(rèn)為 -1 表示無限等待的預(yù)期不符合,雖然在 druid 中 maxWait 配置成 -1 的含義也相同)。對實(shí)現(xiàn)細(xì)節(jié)感興趣的讀者可以從 com.alibaba.druid.pool.DruidDataSource#getConnection 方法入手,查看參數(shù)的使用邏輯。

推薦配置:內(nèi)網(wǎng)(網(wǎng)絡(luò)狀況好) 800;網(wǎng)絡(luò)狀況不是特別好的情況下推薦大于等于 1200,因?yàn)?tcp 建連重試一般是 1 秒。

如果不配置maxWait,后果會怎么樣呢?可能有些應(yīng)用就這么干的,而且也沒發(fā)生過異常,不過最終墨菲定律還是會顯靈的,下面來看幾個真實(shí)的案例:

案例一

// 參數(shù)配置

maxWait=0,
maxActive=5,

正常流量下業(yè)務(wù)沒有發(fā)現(xiàn)任何問題,但突發(fā)大流量涌入時(shí),造成連接池耗盡,所有新增的DB請求處于等待獲取連接的狀態(tài)中。由于 maxWait=0 表示無限等待,在請求速度大于處理速度的情況下等待隊(duì)列會越排越長,最終業(yè)務(wù)上的表現(xiàn)就是業(yè)務(wù)接口大量超時(shí),流量越大造成實(shí)際吞吐量反而越低。

案例二

maxWait=0,

removeAbandoned=true,
removeAbandonedTimeout=180,

現(xiàn)象:業(yè)務(wù)代碼正常運(yùn)行了很長時(shí)間沒有出現(xiàn)過消息積壓情況,在一次全鏈路壓測后產(chǎn)生大量的壓測數(shù)據(jù),造成了 MQ 消息的堆積。即使重啟服務(wù),也只能保持幾十秒的正常運(yùn)行,隨后又進(jìn)入消費(fèi)停滯的狀態(tài)。使用 jstack 發(fā)現(xiàn)是卡在獲取數(shù)據(jù)庫連接中,再過3分鐘左右后出現(xiàn)錯誤:abandon connection, owner thread: xxx 。最后業(yè)務(wù)通過配置 maxWait=3000(3秒超時(shí)),業(yè)務(wù) MQ 消息正常消費(fèi)。

原因分析:業(yè)務(wù)依賴兩個數(shù)據(jù)源,這里表示為 datasource1 與 datasource2,其中在部分代碼段中同時(shí)開啟了兩個庫的事務(wù)。如圖1 所示,線程1 獲取了 datasource1 中的最后一個連接 connection[n],等待獲取 datasource2 的連接,此時(shí)線程2也正在執(zhí)行類似的操作,造成了死鎖等待。大家對這種互鎖一定很熟悉,只是這次是發(fā)生在 DB 上。為什么一段時(shí)間后程序報(bào) abandon connection 的錯誤,這是因?yàn)榕渲昧?{removeAbandoned:true, removeAbandonedTimeout:180} 這兩個參數(shù),這個配置的含義是如果一個連接持有 180 秒還沒有歸還,就被認(rèn)為是異常連接(對于 OLTP 的業(yè)務(wù)查詢通常都是毫秒級的),需要關(guān)閉掉這條連接。之所以正常情況下沒有發(fā)生問題是因?yàn)檫B接池水位比較低,資源充足沒有造成相互等待的情況。

數(shù)據(jù)庫連接池如何配置

圖1. 雙DB連接池死鎖問題

1.2 connectionProperties


參數(shù)是以鍵值對表示的字符串,其中可以配置 connectTimeout 和 socketTimeout,它們的單位都是毫秒,這兩個參數(shù)在應(yīng)對網(wǎng)絡(luò)異常方面非常重要。connectTimeout 配置建立 TCP 連接的超時(shí)時(shí)間,socketTimeout 配置發(fā)送請求后等待響應(yīng)的超時(shí)時(shí)間。這兩個參數(shù)也可以通過在 jdbc url 中添加 connectTimeout=xxx&socketTimeout=xxx 的方式配置,試過在 connectinoProperties 中和 jdbc url 兩個地方都配置,發(fā)現(xiàn)優(yōu)先使用 connectionProperties 中的配置。如果不設(shè)置這兩項(xiàng)超時(shí)時(shí)間,服務(wù)會有非常高的風(fēng)險(xiǎn)?,F(xiàn)實(shí)案例是在網(wǎng)絡(luò)異常后發(fā)現(xiàn)應(yīng)用無法連接到 DB,但是重啟后卻能正常的訪問 DB。因?yàn)樵诰W(wǎng)絡(luò)異常下 socket 沒有辦法檢測到網(wǎng)絡(luò)錯誤,這時(shí)連接其實(shí)已經(jīng)變?yōu)椤八肋B接”,如果沒有設(shè)置 socket 網(wǎng)絡(luò)超時(shí),連接就會一直等待 DB 返回結(jié)果,造成新的請求都無法獲取到連接。

推薦配置:
socketTimeout=3000;connectTimeout=1200

1.3 keepAlive


參數(shù)表示是否對空閑連接保活,布爾類型??赡懿簧偃苏J(rèn)為 druid 連接池默認(rèn)會維持DB連接的心跳,對池子中的連接進(jìn)行?;睿貏e配置了 minIdle 這個參數(shù)后覺得,有了 minIdle 最少應(yīng)該會保持這么多空閑連接。其實(shí),keepAlive 這個參數(shù)是在 druid 1.0.28 后新增的,并且默認(rèn)值是 false,即不進(jìn)行連接?;睢?/p>

那么需要?;钸B接,是不是將 keepAlive 配置成 true 就完事了呢?雖然 true 的確是開啟了?;顧C(jī)制,但是應(yīng)該?;疃嗌賯€,心跳檢查的規(guī)則是什么,這些都需要正確配置,否則還是可能事與愿違。這里需要了解幾個相關(guān)的參數(shù):minIdle 最小連接池?cái)?shù)量,連接?;畹臄?shù)量,空閑連接超時(shí)踢除過程會保留的連接數(shù)(前提是當(dāng)前連接數(shù)大于等于 minIdle),其實(shí) keepAlive 也僅維護(hù)已存在的連接,而不會去新建連接,即使連接數(shù)小于 minIdle;minEvictableIdleTimeMillis 單位毫秒,連接保持空閑而不被驅(qū)逐的最小時(shí)間,保活心跳只對存活時(shí)間超過這個值的連接進(jìn)行;maxEvictableIdleTimeMillis 單位毫秒,連接保持空閑的最長時(shí)間,如果連接執(zhí)行過任何操作后計(jì)時(shí)器就會被重置(包括心跳?;顒幼鳎?;timeBetweenEvictionRunsMillis 單位毫秒,Destroy 線程檢測連接的間隔時(shí)間,會在檢測過程中觸發(fā)心跳。?;顧z查的詳細(xì)流程可參見源碼com.alibaba.druid.pool.DruidDataSource.DestroyTask,其中心跳檢查會根據(jù)配置使用 ping 或 validationQuery 配置的檢查語句。

推薦配置:如果網(wǎng)絡(luò)狀況不佳,程序啟動慢或者經(jīng)常出現(xiàn)突發(fā)流量,則推薦配置為true;

案例一

keepAlive=true,
minIdle=5,
timeBetweenEvictionRunsMillis=10000,
minEvictableIdleTimeMillis=100000,
maxEvictableIdleTimeMillis=100000,

請問上述配置連接能?;畛晒??不能,由于 minEvictableIdleTimeMillis == maxEvictableIdleTimeMillis,所以連接在開始檢測時(shí)就會被斷定超過 maxEvictableIdleTimeMillis 需要丟棄。

案例二

keepAlive=true,
minIdle=5,
timeBetweenEvictionRunsMillis=10000,
minEvictableIdleTimeMillis=95000,
maxEvictableIdleTimeMillis=100000,

請問上述配置連接能?;畛晒??具有隨機(jī)性,由于 maxEvictableIdleTimeMillis - minEvictableIdleTimeMillis < timeBetweenEvictionRunsMillis,所以有可能在這個窗口期并沒有執(zhí)行 Destroy 線程檢測任務(wù),無法保證心跳一定會被執(zhí)行。

1.4 maxActive


最大連接池?cái)?shù)量,允許的最大同時(shí)使用中的連接數(shù)。這里特地嘮叨一下,配置 maxActive 千萬不要好大喜多,雖然配置大了看起來業(yè)務(wù)流量飆升后還能處理更多的請求,但切換到 DB 視角會發(fā)現(xiàn)其實(shí)連接數(shù)的增多在很多場景下反而會減低吞吐量,一個非常典型的例子就秒殺,在更新熱點(diǎn)數(shù)據(jù)時(shí) DB 需要加鎖操作,這個時(shí)候再讓更多的連接操作 DB 就有點(diǎn)像假日往高速上涌入的車輛,只會給 DB 添堵。

推薦配置:20,多數(shù)場景下 20 已完全夠用,當(dāng)然這個參數(shù)跟使用場景相關(guān)性很大,一般配置成正常連接數(shù)的 3~5 倍。

二、DB“慢查”排查記


上面講了一些配置的坑,那么是否中規(guī)中矩的按照推薦配置就萬事大吉了呢,現(xiàn)實(shí)中的世界往往沒這么簡單的事,下面分享一個“慢查”排查的一個案例,了解一下DB慢查的排查思路。

有應(yīng)用反饋發(fā)現(xiàn)大量 DB 慢查,并且日志上還記錄了詳細(xì)的執(zhí)行時(shí)間和SQL語句。接到問題后我們第一時(shí)間排查 DB 發(fā)現(xiàn)并沒有異常,也沒有慢查記錄,并且日志中的大部分 SQL 都能匹配索引,測試執(zhí)行都在毫秒級。于是開始排查網(wǎng)絡(luò)是否正常,有沒丟包、重傳等現(xiàn)象,查詢監(jiān)控?cái)?shù)據(jù)發(fā)現(xiàn)也很正常,然后進(jìn)行抓包分析發(fā)現(xiàn)實(shí)際請求處理的速度非常正常,至此可以排除 DB 問題。

于是再深入分析,查詢 DB 其實(shí)可分為兩個階段:1. 獲取連接階段;2. 執(zhí)行查詢階段;絕大部分情況下獲取連接代價(jià)非常小,直接就能從連接池獲取到,即使需要新建連接代價(jià)往往也不大,所以使用時(shí)非常容易忽略獲取連接這個階段。什么情況下獲取連接會出問題呢?一種情況是建立連接慢,一種是連接池已經(jīng)耗盡,再對照上面的案例進(jìn)行排查,依次排除了這兩種情況。至此問題還是一籌莫展,還好高手在場,想到用 strace 跟蹤 SQL 請求前后干了什么,最后發(fā)現(xiàn)記錄慢查日志開始和結(jié)束之間有寫日志操作,這里的寫日志是同步的并且在特定情況下正好觸發(fā)了另一個問題導(dǎo)致寫日志非常慢,并且這個日志操作是封裝在底層的,連業(yè)務(wù)開發(fā)都不清楚有這么個操作。至此真相水落石出,最終修復(fù)了寫日志慢的問題后就不再出現(xiàn)類似的“慢查”了。

關(guān)于“數(shù)據(jù)庫連接池如何配置”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI