druid連接泄露故障全面分析
1、問題的如何發(fā)生的
1.1、應(yīng)用功能介紹
系統(tǒng)是一個雙數(shù)據(jù)源雙寫單獨的服務(wù)。
(兩個數(shù)據(jù)源是不同的存儲,所以無法使用主從復(fù)制的模式,是一個切換存儲介質(zhì)的過渡態(tài))。
歷史代碼有個更新邏輯update xx set a=b where m=n。
但是這個表中的記錄超10億。遇到需要更新的記錄比較多的場景下存在問題。
故對這個進(jìn)行了sql優(yōu)化。采用的邏輯是查詢出需要更新的記錄id,然后分頁更新。
1.2、關(guān)鍵代碼
雙數(shù)據(jù)源操作
private Object runSql(List<String> sqlSessionFactotyBeanNameList, MethodInvocation invocation) throws InvocationTargetException, IllegalAccessException { List<SqlSession> sqlSessionList = Lists.newArrayList(); Object result = null; try { for (String sessionFactotyBeanName : sqlSessionFactotyBeanNameList) { SqlSessionFactory sqlSessionFactory = RgApplicationContextUtil.getBean( sessionFactotyBeanName, SqlSessionFactory.class); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); Object mapper = sqlSession.getMapper(invocation.getMethod().getDeclaringClass()); Object[] param = invocation.getArguments(); result = invocation.getMethod().invoke(mapper, param); sqlSessionList.add(sqlSession);//問題代碼,注意!!!! sqlSession.commit(); } } catch (Exception ex) { sqlSessionList.stream() .forEach( x -> { x.rollback(); }); } finally { sqlSessionList.stream() .forEach( x -> { x.close(); }); } return result; }
問題的sql
<select id="getBatchIdWithLimit" resultType="java.lang.Long"> SELECT x.id FROM context x WHERE x.oid = #{oid} ORDER BY id ASC LIMIT #{offset}, #{limit} </select>
關(guān)鍵的配置
maxWait 獲取連接時最大等待時間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發(fā)效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
當(dāng)前系統(tǒng)此參數(shù)未進(jìn)行配置,所以會無限等待,使用的是公平鎖
1.3、問題出現(xiàn)的步驟
- sql中存在問題,部分?jǐn)?shù)據(jù)的長度超過Integer的最大值(2147483647),映射存在問題。
- 雙數(shù)據(jù)源代碼存在bug。 List的代碼結(jié)合的 add 位置過于落后,導(dǎo)致反射出現(xiàn)異常的時候。當(dāng)次的SqlSession未關(guān)聯(lián)到待處理的集合中,進(jìn)而也就未rollback和close。造成鏈接泄露。
- 當(dāng)出現(xiàn)問題的數(shù)據(jù)的時候,結(jié)合雙數(shù)據(jù)源的代碼的bug。會造成List為空,所以未進(jìn)行釋放操作,(鏈接泄露了)
- 當(dāng)前系統(tǒng)最大的連接數(shù)是100,出現(xiàn)了100次這樣的數(shù)據(jù),這個服務(wù)就回?zé)o盡的等待獲取鏈接中的狀態(tài)。
1.4、問題的表象
2、如何復(fù)現(xiàn)問題
2.1、問題數(shù)據(jù)復(fù)現(xiàn)
- 把數(shù)據(jù)庫的最大連接數(shù)調(diào)整成1,maxWaitTime不設(shè)置
- 構(gòu)造一條id大于2147483647的數(shù)據(jù)
- 使用api 觸發(fā)調(diào)用到這個邏輯
- 結(jié)果是:第一次調(diào)用報錯,第二次調(diào)用會卡的客戶端設(shè)置的超時時間。
2.2、數(shù)據(jù)庫連接異常復(fù)現(xiàn)
還有一種路徑是代碼都沒問題,但是由于高并發(fā)造成數(shù)據(jù)庫是鎖。mybatis是可以設(shè)置sql的執(zhí)行時長的。一旦出現(xiàn)了這種場景。問題也是會出現(xiàn)的。
但是這種場景比較難以復(fù)現(xiàn),那么有沒有一種手段可以高效的偽造這個場景。
準(zhǔn)備知識
set autocommit=0; //關(guān)閉數(shù)據(jù)的事務(wù)自動提交 SELECT * FROM xxx a WHERE a.id='111' for update; //獲取數(shù)據(jù)庫的行鎖 commit;//提交事務(wù)
數(shù)據(jù)默認(rèn)是自動提交的,所以前置set autocommit=0;這個操作不要忘記了,踩過幾次坑。完成后執(zhí)行commit;進(jìn)行解鎖。
測試完畢記得set autocommit=1;來恢復(fù)數(shù)據(jù)庫的事務(wù)自動提交的特性。
- 準(zhǔn)備一條接口測試用的數(shù)據(jù)
- 執(zhí)行sql select …for update 進(jìn)行行記錄鎖定
- 接口調(diào)用使用同一個id進(jìn)行請求。因為記錄鎖定了,所以api的更新是失敗了,成功的偽造了高并發(fā)形成了行鎖造成的sql問題
3、問題總結(jié)
- 數(shù)據(jù)庫的保護(hù)配置:maxActive、maxWait都配置上,相當(dāng)于熔斷保護(hù)
- mybatis對象映射需要關(guān)注數(shù)據(jù)的范圍
- 利用select for update制造行鎖偽造高并發(fā)造成的數(shù)據(jù)問題
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot實現(xiàn)統(tǒng)一封裝返回前端結(jié)果集的示例代碼
在實際項目開發(fā)過程中,我們經(jīng)常將返回數(shù)據(jù)的基本形式統(tǒng)一為JSON格式的數(shù)據(jù)。但項目可能是由很多人開發(fā)的,所以我們最好將返回的結(jié)果統(tǒng)一起來。本文介紹了SpringBoot實現(xiàn)統(tǒng)一封裝返回前端結(jié)果集的示例代碼,需要的可以參考一下2022-06-06java 調(diào)用wsdl協(xié)議接口簡單實用方法最新推薦
文章介紹了如何使用POM導(dǎo)入依賴,并編寫一個測試類來調(diào)用不同的Web服務(wù)接口,通過訪問接口地址,我們可以獲取請求和返回的body,并進(jìn)一步解析返回的JSON結(jié)果,感興趣的朋友一起看看吧2025-03-03記錄jdk21連接SQLServer因為TLS協(xié)議報錯問題
在使用Druid連接池連接SQL Server時,可能會遇到因TLS版本不匹配導(dǎo)致的連接失敗問題,具體表現(xiàn)為客戶端使用TLS1.3或TLS1.2,而SQL Server僅支持TLS1.0,導(dǎo)致無法建立安全連接,解決方法是修改JDK的安全配置,啟用TLS1.02024-10-10Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題整理
在本篇文章里小編給各位整理了關(guān)于Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題相關(guān)內(nèi)容,有興趣的朋友們跟著參考學(xué)習(xí)下。2019-07-07SpringCloud Netfilx Ribbon負(fù)載均衡工具使用方法介紹
Ribbon是Netflix的組件之一,負(fù)責(zé)注冊中心的負(fù)載均衡,有助于控制HTTP和TCP客戶端行為。Spring Cloud Netflix Ribbon一般配合Ribbon進(jìn)行使用,利用在Eureka中讀取的服務(wù)信息,在調(diào)用服務(wù)節(jié)點時合理進(jìn)行負(fù)載2022-12-12詳解SpringBoot整合RabbitMQ如何實現(xiàn)消息確認(rèn)
這篇文章主要介紹了SpringBoot整合RabbitMQ是如何實現(xiàn)消息確認(rèn)的,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05