<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 超時配置的問題,引發(fā)對 Druid 時間配置項的探究

          共 5286字,需瀏覽 11分鐘

           ·

          2022-08-03 09:57

          開心一刻


          一天在路邊看到一個街頭采訪

          記者:請問,假如你兒子娶媳婦,給多少彩禮合適呢

          大爺:一百萬吧,再給一套房,一輛車

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

          記者:那你兒子多大了?

          大爺:我沒有兒子,有兩個女兒


          問題背景


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


          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 失敗,因為從 Druid 連接池獲取 connection 超時了。

           


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


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


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


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


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


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


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


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


          我們先來模擬下上述問題:


          • MySQL 版本:5.7.21 ,隔離級別:RR

          • Druid 版本:1.1.12

          • spring-jdbc 版本:5.2.3.RELEASE

          • DruidDataSource 初始化

          為了方便演示,就手動初始化了:



          多線程查詢


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



          模擬慢查詢


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


          很簡單,給表加寫鎖:


          LOCK TABLES tbl_user WRITE

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


          異常演示


          先鎖表,再啟動程序



          可以看到,15 個線程中,有 5 個線程獲取 connect 失敗:


          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


          時間配置項


          Druid 中關(guān)于時間的配置項有很多,我們我們重點來看下如下幾個:


          maxWait


          最大等待時長,單位是毫秒,-1 表示無限制。


          • 從連接池獲取 connect;

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

          • 如果沒有則最長等待 maxWait 毫秒;

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


          removeAbandonedTimeout


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


          從連接池獲取到 connect 開始算起,超過此值后, Druid 將強制回收該連接。


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

          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


          檢測連接是否有效的超時時間,單位是秒,-1 表示無限制。


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


          timeBetweenEvictionRunsMillis

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


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


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


          收縮的過程會回收一些空閑的 connect ,而 timeBetweenEvictionRunsMillis 就是檢查空閑連接的間隔時間。


          queryTimeout


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


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


          transactionQueryTimeout


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


          minEvictableIdleTimeMillis


          最小空閑時間,單位是毫秒,默認 30 分鐘。


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


          maxEvictableIdleTimeMillis


          最大空閑時間,單位是毫秒,默認 7 小時。


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


          當(dāng)然不是。


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


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


          再看問題


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



          獲取 connect 的最大等待時長是 10000 毫秒,也就是 10 秒。


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


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


          為什么會拋出 GetConnectionTimeoutException 異常了?


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


          源碼探究


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



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



          重點來了,我們看下 DestroyTask 到底是怎么樣一個邏輯:



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



          如果 connect 正在運行中是不會被強制回收進連接池的。


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


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


          那么問題又來了:removeAbandonedTimeout 作用在哪?


          我們再仔細閱讀下:連接泄漏監(jiān)測

          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)測連接泄露,回收那些長時間游離在連接池之外的空閑 connect。


          可能因為程序問題,導(dǎo)致申請的 connect 在處理完 SQL 查詢后,不能回到連接池的懷抱。那么這個 connect 處理游離態(tài),它真實存在,但后續(xù)誰也申請不到它,這就是連接泄露。


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


          解決問題


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


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


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


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


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


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



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


          總結(jié)


          • Druid 的 removeAbandoned 對性能有影響,不建議開啟。removeAbandoned 的開啟后的作用要捋清楚,而非簡單的過期強制回收;

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

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


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

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


          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構(gòu)師(附各大廠中臺建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)


          瀏覽 131
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  肏逼乱伦视频 | 日韩经典一级片 | 91最新网址 | 精品人人| 大鸡巴操逼高潮视频 |