<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          記一次 Druid 超時(shí)配置的問(wèn)題?

          共 5243字,需瀏覽 11分鐘

           ·

          2022-08-01 14:21

          點(diǎn)擊上方“Java金融”,選擇“設(shè)為星標(biāo)”

          后臺(tái)回復(fù)"888"獲取bat面試題集


          開(kāi)心一刻




          一天在路邊看到一個(gè)街頭采訪

          記者:請(qǐng)問(wèn),假如你兒子娶媳婦,給多少彩禮合適呢

          大爺:一百萬(wàn)吧,再給一套房,一輛車

          大爺沉思一下,繼續(xù)說(shuō)到:如果有能力的話再給老丈人配一輛車,畢竟他把女兒養(yǎng)這么大也不容易

          記者:那你兒子多大了?

          大爺:我沒(méi)有兒子,有兩個(gè)女兒


          問(wèn)題背景


          最近生產(chǎn)環(huán)境出現(xiàn)了一個(gè)問(wèn)題,錯(cuò)誤日志類似如下:



          Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 1010, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user



          日志信息提示的很明顯:獲取 JDBC Connection 失敗,因?yàn)閺?Druid 連接池獲取 connection 超時(shí)了。

           


          上圖的意思是:執(zhí)行 select * from tbl_user 之前,需要從 druid 連接池中獲取一個(gè) connect


          而此時(shí)連接池的狀態(tài)是:一共 10 個(gè)激活的 connect ,連接池最大創(chuàng)建 10 個(gè) connect ,正在執(zhí)行 SQL 的 connect 也是 10 個(gè)。


          所以不能創(chuàng)建新的 connect 那就等唄。一共等了 1010 毫秒,還是拿不到 connect ,就拋出 GetConnectionTimeoutException 異常。


          簡(jiǎn)單點(diǎn)說(shuō)就是,連接池中連接數(shù)不夠,在規(guī)定的時(shí)間內(nèi)拿不到 connect。


          那有人就說(shuō)了:連接池的最大數(shù)量設(shè)置大一點(diǎn),問(wèn)題不就解決了嗎?


          最大連接數(shù)設(shè)置大一點(diǎn)只能降低問(wèn)題發(fā)生的概率,不能完全杜絕問(wèn)題。因?yàn)榫W(wǎng)絡(luò)條件、硬件資源的使用情況等等都是不確定因素。


          今天要講的不是連接池大小問(wèn)題,而是超時(shí)設(shè)置問(wèn)題。我們慢慢往下看。


          問(wèn)題復(fù)現(xiàn)


          我們先來(lái)模擬下上述問(wèn)題:


          • MySQL 版本:5.7.21 ,隔離級(jí)別:RR

          • Druid 版本:1.1.12

          • spring-jdbc 版本:5.2.3.RELEASE

          • DruidDataSource 初始化

          為了方便演示,就手動(dòng)初始化了:



          多線程查詢


          線程數(shù)多于連接池中 connect 數(shù)。



          模擬慢查詢


          如果查詢飛快,15 個(gè)查詢可能都用不上 10 個(gè) connect ,所以我們需要簡(jiǎn)單處理下。


          很簡(jiǎn)單,給表加寫(xiě)鎖:



          LOCK TABLES tbl_user WRITE


          給表 tbl_user 加上寫(xiě)鎖,然后跑線程去查詢 tbl_user 的數(shù)據(jù)。


          異常演示


          先鎖表,再啟動(dòng)程序



          可以看到,15 個(gè)線程中,有 5 個(gè)線程獲取 connect 失?。?/span>



          Thread-13 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_userThread-5 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_userThread-10 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_userThread-7 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_userThread-8 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user


          示例代碼:druid-timeout
          https://gitee.com/youzhibing/qsl-project/tree/master/druid-timeout


          時(shí)間配置項(xiàng)


          Druid 中關(guān)于時(shí)間的配置項(xiàng)有很多,我們我們重點(diǎn)來(lái)看下如下幾個(gè):


          maxWait


          最大等待時(shí)長(zhǎng),單位是毫秒,-1 表示無(wú)限制。


          • 從連接池獲取 connect;

          • 如果有空閑的 connect ,則直接獲取到;

          • 如果沒(méi)有則最長(zhǎng)等待 maxWait 毫秒;

          • 如果還獲取不到,則拋出 GetConnectionTimeoutException 異常。


          removeAbandonedTimeout


          設(shè)置 Druid 強(qiáng)制回收連接的時(shí)限,單位是秒。


          從連接池獲取到 connect 開(kāi)始算起,超過(guò)此值后, Druid 將強(qiáng)制回收該連接。


          官網(wǎng)也有說(shuō)明:連接泄漏監(jiān)測(cè)。

          https://github.com/alibaba/druid/wiki/%E8%BF%9E%E6%8E%A5%E6%B3%84%E6%BC%8F%E7%9B%91%E6%B5%8B


          validationQueryTimeout


          檢測(cè)連接是否有效的超時(shí)時(shí)間,單位是秒,-1 表示無(wú)限制。


          Druid 內(nèi)部的一個(gè)檢測(cè) connect 是否有效的超時(shí)時(shí)間,需要結(jié)合 validationQuery 來(lái)配置。


          timeBetweenEvictionRunsMillis

          檢查空閑連接的頻率,單位是毫秒, 非正整數(shù)表示不進(jìn)行檢查。


          空閑連接檢查的間隔時(shí)間, Druid 池中的 connect 數(shù)量是一個(gè)動(dòng)態(tài)從 minIdle 到 maxActive 擴(kuò)張與收縮的過(guò)程。


          connect 使用高峰期,數(shù)量會(huì)從 minIdle 擴(kuò)張到 maxActive 。使用低峰期, connect 數(shù)量會(huì)從 maxActive 收縮到 minIdle 。


          收縮的過(guò)程會(huì)回收一些空閑的 connect ,而 timeBetweenEvictionRunsMillis 就是檢查空閑連接的間隔時(shí)間。


          queryTimeout


          執(zhí)行查詢的超時(shí)時(shí)間,單位是秒,-1 表示無(wú)限制。


          最終會(huì)應(yīng)用到 Statement 對(duì)象上,執(zhí)行時(shí)如果超過(guò)此時(shí)間,則拋出 SQLException 。


          transactionQueryTimeout


          執(zhí)行一個(gè)事務(wù)的超時(shí)時(shí)間,單位是秒。


          minEvictableIdleTimeMillis


          最小空閑時(shí)間,單位是毫秒,默認(rèn) 30 分鐘。


          如果連接池中非運(yùn)行中的連接數(shù)大于 minIdle ,并且某些連接的非運(yùn)行時(shí)間大于 minEvictableIdleTimeMillis ,則連接池會(huì)將這部分連接設(shè)置成 Idle 狀態(tài)并關(guān)閉。


          maxEvictableIdleTimeMillis


          最大空閑時(shí)間,單位是毫秒,默認(rèn) 7 小時(shí)。


          如果 minIdle 設(shè)置的比較大,連接池中的空閑連接數(shù)一直沒(méi)有超過(guò) minIdle ,那么那些空閑連接是不是一直不用關(guān)閉?


          當(dāng)然不是。


          如果連接太久沒(méi)用,數(shù)據(jù)庫(kù)也會(huì)把它關(guān)閉(MySQL 默認(rèn) 8 小時(shí)),這時(shí)如果連接池不把這條連接關(guān)閉,程序就會(huì)拿到一條已經(jīng)被數(shù)據(jù)庫(kù)關(guān)閉的連接。


          為了避免這種情況, Druid 會(huì)判斷池中的連接狀態(tài)。如果非運(yùn)行時(shí)間大于 maxEvictableIdleTimeMillis ,也會(huì)強(qiáng)行把它關(guān)閉,而不用判斷空閑連接數(shù)是否小于 minIdle 。


          再看問(wèn)題


          其實(shí)前面的示例中設(shè)置了。



          獲取 connect 的最大等待時(shí)長(zhǎng)是 10000 毫秒,也就是 10 秒。


          而 removeAbandonedTimeout 設(shè)置是 7 秒。


          照理來(lái)說(shuō) connect 如果 7 秒未執(zhí)行完 SQL 查詢,就會(huì)被 Druid 強(qiáng)制回收進(jìn)連接池,那么等待 10 秒應(yīng)該能夠獲取到 connect 。


          為什么會(huì)拋出 GetConnectionTimeoutException 異常了?


          這也就是文章標(biāo)題中的超時(shí)設(shè)置問(wèn)題。


          源碼探究


          很顯然,我們從 dataSource.init(); 開(kāi)始跟源碼,會(huì)看到如下一段代碼:



          我們繼續(xù)跟 createAndStartDestroyThread():



          重點(diǎn)來(lái)了,我們看下 DestroyTask 到底是怎么樣一個(gè)邏輯:



          我們接著跟進(jìn) removeAbandoned ,關(guān)鍵代碼:



          如果 connect 正在運(yùn)行中是不會(huì)被強(qiáng)制回收進(jìn)連接池的。


          回到我們的示例:connect 都是在運(yùn)行中,只是都在進(jìn)行慢查詢,所以是無(wú)法被強(qiáng)制回收進(jìn)連接池的,那么其他線程自然在 maxWait 時(shí)間內(nèi)無(wú)法獲取到 connect 。


          至此,文章標(biāo)題中的問(wèn)題的原因就找到了。


          那么問(wèn)題又來(lái)了:removeAbandonedTimeout 作用在哪?


          我們?cè)僮屑?xì)閱讀下:連接泄漏監(jiān)測(cè)

          https://github.com/alibaba/druid/wiki/%E8%BF%9E%E6%8E%A5%E6%B3%84%E6%BC%8F%E7%9B%91%E6%B5%8B


          Druid 提供了 RemoveAbandanded 相關(guān)配置,目的是監(jiān)測(cè)連接泄露,回收那些長(zhǎng)時(shí)間游離在連接池之外的空閑 connect。


          可能因?yàn)槌绦騿?wèn)題,導(dǎo)致申請(qǐng)的 connect 在處理完 SQL 查詢后,不能回到連接池的懷抱。那么這個(gè) connect 處理游離態(tài),它真實(shí)存在,但后續(xù)誰(shuí)也申請(qǐng)不到它,這就是連接泄露。


          而 removeAbandoned 的設(shè)計(jì)就是為了幫助這些泄露的 connect 回到連接池的懷抱。


          解決問(wèn)題


          開(kāi)啟 removeAbandoned 對(duì)性能有影響,官方不建議在生產(chǎn)環(huán)境使用。


          那么我們接受官方的建議,不開(kāi)啟 removeAbandoned (不配置即可,默認(rèn)是關(guān)閉的)。


          為了不讓慢查詢占用整個(gè)連接池,而拖垮整個(gè)應(yīng)用,我們?cè)O(shè)置查詢超時(shí)時(shí)間 queryTimeout 。


          有兩種方式,一個(gè)是設(shè)置 DataSource 的 queryTimeout ,另一個(gè)是設(shè)置 JdbcTemplate 的 queryTimeout 。


          如果兩個(gè)都設(shè)置,最終生效的是哪個(gè),為什么?大家自己去分析,權(quán)當(dāng)是給大家留個(gè)一個(gè)作業(yè)。


          這里就配置 DataSource 的 queryTimeout ,給大家演示下效果:



          可以看到,所有線程都獲取到了 connect。


          總結(jié)


          • Druid 的 removeAbandoned 對(duì)性能有影響,不建議開(kāi)啟。removeAbandoned 的開(kāi)啟后的作用要捋清楚,而非簡(jiǎn)單的過(guò)期強(qiáng)制回收;

          • Druid 的時(shí)間配置項(xiàng)有很多,不局限于文中所講,但常用的就那么幾個(gè),其他的保持默認(rèn)值就好。配置的時(shí)候一定要弄清楚各個(gè)配置項(xiàng)的具體作業(yè),不要去猜!

          • 查詢超時(shí) queryTimeout 即可在 DataSource 配置,也可在 JdbcTemplate 配置。


          轉(zhuǎn)自:青石路,

          鏈接:cnblogs.com/youzhibing/p/16458860.html


          推薦:

          feign的一個(gè)注解居然隱藏這么多知識(shí)!
          關(guān)于線程池的面試,看這篇就夠了!
          PS:因?yàn)楣娞?hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下“在看”,加個(gè)“星標(biāo)”,這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。點(diǎn)“在看”支持我們吧! 


          瀏覽 91
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久三级片 | 大雞巴弄得我好舒服视频 | 青青草偷窥美女屄 | 亚洲偷拍22| 久久三级片 |