快被系統(tǒng)性能逼瘋了?你需要這份性能優(yōu)化策略
作者介紹
劉迪偉,就職于世界五***銀行。負責(zé)公司網(wǎng)銀業(yè)務(wù)系統(tǒng)的設(shè)計和交付,擅長并持續(xù)關(guān)注Java性能優(yōu)化、DevOps等領(lǐng)域。
XX銀行網(wǎng)銀系統(tǒng)是一套全新的對公業(yè)務(wù)渠道類系統(tǒng),經(jīng)過兩年的建設(shè),將逐步對外提供服務(wù)。
該系統(tǒng)融合了原來多個對公渠道系統(tǒng),并發(fā)量是以前多個系統(tǒng)之和,吞吐量要求將大幅上升。為了使廣大對公客戶使用系統(tǒng)時獲得更快的響應(yīng)時間體驗,項目組對系統(tǒng)進行了持續(xù)的性能測試和優(yōu)化。這一過程中,形成了一套針對新建系統(tǒng)進行性能測試和優(yōu)化的方法論。
該方法論包括測試環(huán)境準備、測試功能優(yōu)先級、性能優(yōu)化原則、常用性能指標及工具、工具使用方法、常見性能問題原因和優(yōu)化方法,以及典型案例和進一步優(yōu)化方法的討論。
由于系統(tǒng)已經(jīng)開發(fā)完成,根據(jù)性能優(yōu)化修改范圍盡可能小且不引入更多問題的原則,本文將只討論對系統(tǒng)進行局部優(yōu)化的方法,而系統(tǒng)初始設(shè)計時為了提高性能而進行的設(shè)計不在討論范圍之內(nèi)。
我們使用Linux操作系統(tǒng),壓測工具為loadrunner,中間件版本包括IHS8.5.5.9、WAS8.5、IBMJDK1.7(IBM J9VM)、DB2V10、Redis3.2.3。
一、應(yīng)用系統(tǒng)性能評價指標
-
響應(yīng)時間:盡快的給用戶返回響應(yīng),體現(xiàn)系統(tǒng)處理請求的速度;
-
吞吐量TPS:每秒完成的事務(wù)數(shù),體現(xiàn)系統(tǒng)處理能力;
-
并發(fā)性:業(yè)務(wù)請求高并發(fā)時,系統(tǒng)能否穩(wěn)定運行;
-
擴展性:單機處理能力不足時,系統(tǒng)能否橫向擴展。
TPS = 并發(fā)用戶數(shù) / 響應(yīng)時間
二、常見性能監(jiān)控指標及工具
1、操作系統(tǒng)監(jiān)控指標及工具
主要監(jiān)控指標:CPU、系統(tǒng)CPU、內(nèi)存、磁盤IO、網(wǎng)絡(luò)IO、請求耗時。
常用命令:
-
top –H –p pid:cpu負載監(jiān)控,實時查看占用cpu高的線程;
-
vmstat:系統(tǒng)負載監(jiān)控;
-
pidstat:cpu讓步式上下文切換監(jiān)控,監(jiān)控鎖競爭;
-
iostat:磁盤利用率監(jiān)控;
-
nmon:監(jiān)控cpu、內(nèi)存、io使用率 ./nmon -f -t -s 2 -c 100 每2秒采集一次,共100次;
-
netstat -anp|grep端口或IP|grep ESTABLISHED|wc -l:服務(wù)器連接數(shù)監(jiān)控。
2、JVM監(jiān)控指標及工具
-
Jconsole,監(jiān)控cpu、內(nèi)存垃圾回收。
JVM啟動參數(shù)中增加:
-Dcom.sun.management.jmxremote.port=1088
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-
jvisualvm,cpu采樣、java方法耗時分析、jvm線程棧快照、監(jiān)控cpu、內(nèi)存垃圾回收。
CPU抽樣:
線程快照:
-
jca457.jar,ibm javacore線程快照分析:
-
ga456.jar,ibmjvm垃圾回收gc分析:
-
ha456.jar,ibmjvm內(nèi)存,heapdump分析,內(nèi)存溢出時使用;
-
TProfiler,cpu采樣、java方法cpu耗時分析,cpu高,響應(yīng)慢時使用;
-
JProfiler,cpu采樣、java方法cpu耗時分析,cpu高,響應(yīng)慢時使用;
-
OracleDeveloperStudio12.6-linux-x86,cpu采樣、java方法cpu耗時分析,cpu高,響應(yīng)慢時使用。
三、性能測試前置條件
1、數(shù)據(jù)庫表數(shù)據(jù)量準確
要和生產(chǎn)數(shù)據(jù)量保持一致,至少一個數(shù)量級。數(shù)據(jù)分布盡量均勻。
2、測試環(huán)境和生產(chǎn)一致
測試環(huán)境機器配置、參數(shù)、代碼盡可能和生產(chǎn)保持一致(數(shù)據(jù)庫服務(wù)器硬件除外)。
3、合理確定并發(fā)用戶量
系統(tǒng)并發(fā)用戶量有多種算法可以估算:
-
平均并發(fā)用戶數(shù):
C=nL/T(n是考察時間內(nèi)用戶登錄數(shù),L是用戶平均在線時間長度,T是考察值時間長度)
并發(fā)用戶數(shù)峰值:C’=C + 3*根號C
-
用戶總量/統(tǒng)計時間*影響因子:
網(wǎng)銀用戶量100萬,根據(jù)2/8原則, 80%用戶在上午9點到11點,下午2點到4點之間登錄系統(tǒng),每次登錄耗時1到1.5秒。則并發(fā)用戶為:
1000000*0.8/4/3600*1.5=82.5,
1000000*0.8/4/3600*1=55
-
根據(jù)系統(tǒng)用戶數(shù)計算:
并發(fā)用戶數(shù)=系統(tǒng)***在線用戶數(shù)的8%到12%
-
根據(jù)TPS估計:
C=(Think Time + 1)* TPS,
網(wǎng)銀用戶思考時間10s,
C=(10+1)* 3=33。
4、預(yù)估各功能交易量,確定壓測功能優(yōu)先級
根據(jù)交易量從大到小排名,排名靠前的優(yōu)先壓測。
5、設(shè)置性能問題認定標準
比如響應(yīng)時間超過3s、TPS低于10、服務(wù)器cpu占用率超過70%、jvm堆內(nèi)存使用100%、垃圾回收頻繁、網(wǎng)絡(luò)IO或磁盤IO達到瓶頸等……都可能是性能問題。
四、性能優(yōu)化一般思路
1、找到性能瓶頸
性能瓶頸定義:導(dǎo)致系統(tǒng)TPS低、響應(yīng)時間長、資源(CPU、內(nèi)存、網(wǎng)絡(luò))占用高等問題的關(guān)鍵程序模塊。提升該程序模塊的性能,可以大幅度改善性能。
常見的性能瓶頸原因包括:數(shù)據(jù)庫慢查詢SQL、日志打印、xml大報文解析和格式轉(zhuǎn)換、復(fù)雜業(yè)務(wù)邏輯、鎖競爭等。
2、如何找到性能瓶頸
-
使用LoadRunner給每個接口的增加事務(wù),記錄其響應(yīng)時間和TPS,最慢的那個接口往往是瓶頸;
-
分析慢交易的日志,查看是哪個操作耗時最長;
-
分析數(shù)據(jù)庫快照,看是否有執(zhí)行較慢或者全表掃描的SQL;
-
通過Javacore查看線程正在執(zhí)行的代碼,是大部分阻塞在IO上,還是大部分在進行計算。針對不同的問題,使用不同的分析工具。詳細內(nèi)容可以看下一章節(jié)。
3、針對性能瓶頸進行合理優(yōu)化
性能優(yōu)化原則:
-
先優(yōu)化瓶頸問題;
-
方案簡單,盡量不引入更多復(fù)雜性,盡量不降低業(yè)務(wù)體驗;
-
滿足系統(tǒng)性能要求即可,不引入新的bug。
五、常見問題及優(yōu)化方法
1、SQL執(zhí)行時間長
問題現(xiàn)象:系統(tǒng)響應(yīng)時間長、數(shù)據(jù)庫cpu高。
問題原因:全表掃描、索引低效、排序溢出。
解決方法:
-
通過DB2數(shù)據(jù)庫快照查看執(zhí)行時間長的SQL,查看該SQL執(zhí)行計劃,在cost比較高的SQL上增加合適索引。要求所有大表SQL執(zhí)行計劃的cost低于100,超過100的SQL要評審。對于排序溢出、可參考數(shù)據(jù)中心規(guī)范設(shè)置排序堆大小,規(guī)范中排序堆設(shè)置為AUTOMATIC。
-
制定數(shù)據(jù)庫表清理策略。根據(jù)數(shù)據(jù)的生命周期要求,對流水類的數(shù)據(jù)定期進行清理備份,不再長期保留。定期對全庫的表結(jié)構(gòu)進行reorg、runstats操作,以提高索引效率。
排查方法:
-
獲得數(shù)據(jù)庫快照:db2 get snapshot for all on corpdb >gswyzfzzshpl1207.log
-
從快照中提取慢SQL,Toad查看SQL執(zhí)行計劃
-
Db2命令方式查看SQL執(zhí)行計劃:db2expln -d corpdb -t -g -q "SQL語句"
-
執(zhí)行計劃查看方法:自上而下查看cost***的分支,找到未走索引或索引使用不當(dāng)?shù)谋?/p>
2、數(shù)據(jù)庫出現(xiàn)死鎖
問題現(xiàn)象:數(shù)據(jù)庫快照檢測到存在數(shù)據(jù)庫死鎖,或通過db2evmon -db corpdb -evm DB2DETAILDEADLOCK>dlock.txt生成死鎖監(jiān)控文件,快照或監(jiān)控文件中存在deadlock的情況。
問題原因:全表掃描、大事務(wù)、更新相同表記錄的SQL執(zhí)行順序交叉等。
解決方法:縮短事務(wù)路徑長度,避免全表掃描。如果必須存在大事務(wù),則更新相同表的SQL執(zhí)行順序一致,并且堅決避免全表掃描。網(wǎng)銀系統(tǒng)指令發(fā)送功能全表掃描+全局大事務(wù),導(dǎo)致數(shù)據(jù)庫死鎖。
排查方法:根據(jù)死鎖監(jiān)控文件dlock.txt找到導(dǎo)致死鎖的SQL,以及該SQL持有的鎖,分析該SQL可能存在的問題。
3、線程阻塞在日志記錄上
問題現(xiàn)象:系統(tǒng)響應(yīng)時間長、通過javacore查看很多線程阻塞在打印日志上。
問題原因:log4j1.x版本較低,性能較差;大報文日志多次輸出。
解決方法:
-
減少無效日志、刪除無用日志,減少大日志輸出。
-
升級log4j組件到log4j2,參考log4j2官方文檔,配置合理的日志緩沖區(qū),采用高效的Appenders,比如RollingRandomAccessFile。但log4j2仍然采用同步日志,不采用異步日志。因為網(wǎng)銀系統(tǒng)日志量較大,異步日志隊列很快就滿了,如果單條日志存在大報文,還有可能導(dǎo)致內(nèi)存溢出,因此不適合采用異步日志。如果日志量少(壓測產(chǎn)生日志的速度,低于日志寫入文件的速度),則可以使用異步日志,大幅提高性能。如果日志量較大,則不建議使用異步日志。
排查方法:
-
JVM啟動參數(shù)中增加-XX:+HeapDumpOnCtrlBreak,壓測進行時,kill -3 pid 殺幾個javacore,使用jca457.jar工具打開并分析。推薦使用該工具,因為該工具可以對所有線程狀態(tài)進行統(tǒng)計,并生成餅狀圖,方便查看。
-
壓測進行時,使用jvisualvm獲取jvm快照,分析線程堆棧。
4、多線程并發(fā)問題
問題現(xiàn)象:采用合理的并發(fā)數(shù)壓測,系統(tǒng)出現(xiàn)邏輯錯誤、交易失敗或異常報錯。經(jīng)查是由于對象中變量被異常修改導(dǎo)致。
問題原因:系統(tǒng)中全局對象中的類變量或全局對象,被多個線程修改。
解決方法:排查系統(tǒng)中所有持有全局對象或類變量的代碼,檢查其全局變量是否可能被多個線程并行修改。
修改方法:
-
將全局變量轉(zhuǎn)成方法內(nèi)的局部變量;
-
對全局變量進行同步控制比如syncronized代碼塊,或者java.util.concurrent鎖。
排查方法:并發(fā)問題很可能是由全局變量或者對象導(dǎo)致,準確識別全局變量,通過閱讀代碼找問題。建議應(yīng)用梳理所有可能存放全局對象的代碼,統(tǒng)一管控,或者把所有全局對象放到一個類中,方便管理。
5、打開了太多文件
問題現(xiàn)象:采用合理的并發(fā)數(shù)壓測,交易失敗,或后臺日志報錯:To many open files。
問題原因:
-
讀取配置文件或者業(yè)務(wù)數(shù)據(jù)文件后,未關(guān)閉文件流;
-
/etc/security/limits.conf中***打開文件數(shù)配置過小。
解決方法:
-
使用lsof –p pid 命令查看進程打開的文件,如果大部分文件都是同一類型的文件,說明可能未關(guān)閉文件流。找到打開文件的代碼,關(guān)閉文件流即可。
-
如果不存在未關(guān)閉文件流的問題,且業(yè)務(wù)本身就需要處理大量文件,則修改/etc/security/limits.conf文件如下內(nèi)容:
* hard nproc 10240
* soft nproc 10240
6、內(nèi)存泄漏
問題現(xiàn)象:JVM內(nèi)存耗盡,后臺日志拋出OutOfMemeryError異常 ;
問題原因:內(nèi)存溢出問題可能的原因比較多,可能是全局的List、Map等對象不斷被擴大,也可能是程序不慎將大量數(shù)據(jù)讀到內(nèi)存里;可能是循環(huán)操作導(dǎo)致,也可能后臺線程定時觸發(fā)加載數(shù)據(jù)導(dǎo)致。
解決方法:對于ibmjdk純java應(yīng)用,在jvm啟動時設(shè)置-XX:+HeapDumpOnOutOfMemory Error參數(shù),會在內(nèi)存溢出時生成heapdump文件。使用ha456.jar工具打開heapdump文件,分析大對象是如何產(chǎn)生的。
當(dāng)然,在heapdump中對象類型可能只是List這種結(jié)構(gòu),看不出具體哪個業(yè)務(wù)代碼創(chuàng)建的對象。此時要分析所有的全局對象,列出可疑的List或Map對象,排查其溢出原因。
全局對象、引用的初始化、修改要慎重。建議應(yīng)用梳理所有可能存放全局對象的代碼,統(tǒng)一管控。
7、JVM垃圾回收頻繁
問題現(xiàn)象:top –H –p pid命令查看,GC Slave線程CPU占用排名始終為前三名,同時Jconsole查看jvm內(nèi)存占用較高,垃圾回收頻繁。使用ga456.jar分析gc日志,查看gc頻率、時長。
打印gc日志的方法:在jvm啟動參數(shù)里增加-verbose:gc -Xverbosegclog:/usr/ebank/ logs/cobp/gcdetail.log
問題原因:高并發(fā)下,內(nèi)存對象較多,jvm堆內(nèi)存不夠用 。
解決方法:擴大堆內(nèi)存大小–Xmx2048m –Xms2048m。
8、CPU高
問題現(xiàn)象:50并發(fā)壓測,監(jiān)控工具顯示bp、前置CPU占用90%以上。
問題原因:業(yè)務(wù)處理中存在大量CPU計算操作。
解決方法:采用更高效的算法、數(shù)據(jù)結(jié)構(gòu)替換原來消耗CPU的代碼,或者采用新的設(shè)計繞過瓶頸代碼,比如查找數(shù)據(jù)的邏輯,可以把List改為Map,以空間換時間;比如用Json報文替換XML報文,提高傳輸、解析和打印日志的效率。
導(dǎo)致Cpu計算資源高消耗的代碼:報文格式轉(zhuǎn)換、加解密、正則表達式、低效的循環(huán)、低效的正則表達式。
排查方法:
-
壓測進行時,使用jvisualvm工具遠程連接應(yīng)用,點抽樣器àCPU,點快照生成線程快照。采樣一段時間后,抽樣器會顯示各個方法占用cpu時間,可以針對CPU時間占用高的方法進行優(yōu)化。
-
使用tprofiler,jprofiler,OracleDeveloperStudio12.6-linux-x86工具分別分析消耗CPU時間長的方法,以上工具分析結(jié)果可能有些差別。針對CPU計算耗時最長的方法進行優(yōu)化。
9、批處理時間長、數(shù)據(jù)庫逐筆插入緩慢
問題現(xiàn)象:大批量數(shù)據(jù)(10萬條以上)更新或插入數(shù)據(jù)庫,耗時較長。
問題原因:批量數(shù)據(jù)處理時,如果逐條更新數(shù)據(jù)庫,則會存在大量網(wǎng)絡(luò)io、磁盤io,耗時較長,而且對數(shù)據(jù)庫資源消耗較大。
解決方法:
-
采用java提供的batchUpdate方法批量更新數(shù)據(jù)庫,每1000條commit一次,可大幅提高數(shù)據(jù)更新效率。
-
單線程改成線程池,并行處理,充分利用多核CPU,通過數(shù)據(jù)庫或者其他同步鎖控制并行性;增加緩沖池,降低數(shù)據(jù)庫或磁盤IO訪問頻次。
10、數(shù)據(jù)庫CPU高
問題現(xiàn)象:后臺指令發(fā)送滿負荷工作時,數(shù)據(jù)庫CPU高。
問題原因:后臺指令發(fā)送線程每次對全量查詢結(jié)果排序,結(jié)果集很大,然后取一條記錄;索引區(qū)分度不高,滿負荷執(zhí)行時;查詢頻率很高;壓測顯示,并行發(fā)送指令的后臺線程越多,數(shù)據(jù)庫CPU越高,效率越低。
解決方法:
-
去掉ORDER BY,增加索引后,效果不明顯。因為結(jié)果集大和查詢頻繁兩個問題沒有解決,因此考慮使用設(shè)計新的方案。
-
新方案:設(shè)計指令發(fā)送線程池,生產(chǎn)者線程每臺任務(wù)服務(wù)器只有一個線程,負責(zé)查詢待發(fā)送指令,每次查詢50條指令。每條指令包裝成一個Runnable對象,放進ThreadPoolExecutor線程池,線程池大小參數(shù)設(shè)置為100或200。每當(dāng)線程池滿時,生產(chǎn)者停止生產(chǎn)指令,休息15秒后繼續(xù)。消費者線程即線程池里的線程,參數(shù)設(shè)置為4,8或12(和不同指令類型的指令數(shù)據(jù)量成正比)。
改進后的方案,數(shù)據(jù)庫CPU降到10%一下,發(fā)送效率單機提升6倍,且可線性擴展任務(wù)服務(wù)器。
11、壓測TPS曲線劇烈下降或抖動
問題現(xiàn)象:50并發(fā)壓測,TPS曲線正常應(yīng)該是平緩的,波動不大,如果突然出現(xiàn)劇烈下降,并且短時間內(nèi)無法恢復(fù),則可能存在問題。
問題原因:一般是由于前置或bp的jvm進行垃圾回收,或者日志記錄磁盤滿導(dǎo)致的。
解決方法:如果不是特別劇烈的波動或者TPS曲線下降后長時間不反彈,則可以忽略該問題。否則,需要分析曲線下降的時刻,系統(tǒng)當(dāng)時正在發(fā)生的事情??梢酝ㄟ^top命令監(jiān)控當(dāng)時CPU占用比價高的線程,也可以kill -3 pid殺javacore來查看線程堆棧。
六、優(yōu)化案例
1、網(wǎng)銀系統(tǒng)基本情況
1) 壓測環(huán)境系統(tǒng)架構(gòu)
壓測環(huán)境不包括F5,只有1臺WEB、1臺前置(應(yīng)用服務(wù)器)、1臺BP(業(yè)務(wù)處理服務(wù)器)、1臺DB、1臺Redis服務(wù)器。
2)客戶請求鏈路:
客戶端壓力機-〉Web-〉PRE-〉BP-〉DB2 ;
客戶端壓力機-〉Web-〉PRE-〉BP-〉擋板服務(wù)器(模擬后端服務(wù)方)。
3)系統(tǒng)特點:
-
用戶、賬號權(quán)限校驗較多。
對公業(yè)務(wù)典型場景:經(jīng)辦、審核、查詢、下載、批量。用戶權(quán)限通過業(yè)務(wù)鏈控制。
-
接口調(diào)用較多,且由前端組合調(diào)用接口。
前后端分離,前端通過調(diào)用一個個接口來完成業(yè)務(wù)。接口粒度較細。登陸、支付轉(zhuǎn)賬經(jīng)辦需要15~19個接口完成一次交易。
-
強一致性、高可用性。
資金交易系統(tǒng),一致性需要通過數(shù)據(jù)庫來保證。
4)網(wǎng)銀登錄壓測曲線
2、數(shù)據(jù)庫消息隊列案例(指令發(fā)送隊列)
需求是:對于需要異步處理的交易指令,需要設(shè)計一個基于數(shù)據(jù)庫的消息隊列,我們稱之為指令發(fā)送隊列。該隊列既要滿足服務(wù)器的性能約束,又要滿足每日處理交易量的要求。
1)優(yōu)化前處理過程
后臺任務(wù)每次從數(shù)據(jù)庫指令表中排序并取最早的一條指令,獲取該指令的詳細交易信息,組裝成報文并調(diào)用接口發(fā)送后臺核心系統(tǒng)。
在壓測環(huán)境下,該方案如果配置一個后臺任務(wù),無法達到系統(tǒng)設(shè)計的指令處理速度;如果配置多個后臺任務(wù),則會導(dǎo)致數(shù)據(jù)庫CPU占用較高,影響其他聯(lián)機業(yè)務(wù)的開展。
2)優(yōu)化后處理過程
每臺后臺任務(wù)服務(wù)器配置一個生產(chǎn)者任務(wù),20個消費者任務(wù)。
生產(chǎn)者任務(wù)一次取100個(可配置)指令,依次分配給20個消費者任務(wù)中空閑的任務(wù),消費者任務(wù)獲取指令詳細信息,并組裝報文后發(fā)送后臺核心系統(tǒng);生產(chǎn)者任務(wù)如果發(fā)現(xiàn)無空閑消費者任務(wù),則等待15s后重新判斷。
此方案顯著降低了數(shù)據(jù)庫CPU負載,合理利用了應(yīng)用服務(wù)器的并發(fā)能力,處理效率大為提高,以上線程數(shù)和每次獲取的指令數(shù)可以配置。
指令發(fā)送線程池模型圖:
3、數(shù)據(jù)庫死鎖案例
1)死鎖問題現(xiàn)象
當(dāng)多臺任務(wù)服務(wù)器同時運行大額指令發(fā)送后臺線程,即多個生產(chǎn)者線程并行更新數(shù)據(jù)庫指令表時,數(shù)據(jù)庫快照檢測到存在數(shù)據(jù)庫死鎖,或通過db2evmon -db corpdb -evm DB2DETAILDEADLOCK>dlock.txt 生成死鎖監(jiān)控文件,快照或監(jiān)控文件中存在deadlock的情況。
DB2數(shù)據(jù)庫有自動解除死鎖功能,死鎖超時時間默認為10s,數(shù)據(jù)庫會隨機選擇一個死鎖事務(wù)kill掉。本案例由于是后臺任務(wù),所以用戶感覺不到死鎖;如果是聯(lián)機交易,一個用戶會發(fā)現(xiàn)交易失敗,另一個用戶交易成功,但是會感覺交易變慢。指令發(fā)送后臺任務(wù)模型詳見下一章節(jié)內(nèi)容。
2)數(shù)據(jù)庫死鎖發(fā)生原理
兩個不同的數(shù)據(jù)庫事務(wù)使用排它鎖鎖住了同一張表的不同行記錄,并且互相等待讀取對方鎖住的行記錄。
3)導(dǎo)致死鎖的可能原因
全表掃描、大事務(wù)、事務(wù)之間對死鎖訪問順序交叉等。
4)死鎖問題排查過程
Step1:分析數(shù)據(jù)庫快照和死鎖監(jiān)控日志,查看導(dǎo)致死鎖的SQL,定位問題SQL。
Step2:問題SQL不存在事務(wù)之間對死鎖訪問順序交叉的情況,當(dāng)時尚不清楚程序中的全表掃描、大事務(wù)可能會導(dǎo)致死鎖,因此做了以下實驗:
-
模擬問題SQL在兩個不同的數(shù)據(jù)庫客戶端執(zhí)行SQL,查看數(shù)據(jù)是否更新成功。
-
完全按照源程序的SQL邏輯執(zhí)行驗證:
UPDATE ( SELECT BP_SRVR_IP FROM ${tableName} WHERE TSK_STAT='TODO' AND ( BP_SRVR_IP IS OR BP_SRVR_IP='') AND PRTY =? AND eff_tm <= CURRENT TIMESTAMP FETCH FIRST 50 ROWS ONLY WITH RS) t SET BP_SRVR_IP=?
-
不加索引,A事務(wù)在R上加了X鎖,B事務(wù)無法在任何記錄上加X鎖,B事務(wù)會等待A事務(wù)提交后再加鎖。
-
增加索引,A事務(wù)在R記錄加了X鎖,B事務(wù)在S記錄加X鎖,互不沖突。
Step3:實驗發(fā)現(xiàn)查詢SQL增加with RS隔離級別,查詢效率會更高。當(dāng)A事務(wù)已對記錄R加X鎖,B事務(wù)掃描到R記錄時,如果是CS隔離級別,B事務(wù)會自動退出,返回空結(jié)果集;如果是RS隔離級別,B事務(wù)會等待A事務(wù)完成后,跳過R記錄,對符合條件的R+1記錄加X鎖。
Step4:PRTY字段增加索引,沒有出現(xiàn)死鎖問題;或者不加索引,維持全表掃描不變,大事務(wù)改成小事務(wù)后,也沒有出現(xiàn)死鎖問題。
5)兩點經(jīng)驗
-
需要提前熟悉DB2鎖的種類和作用,隔離級別的種類和作用,表鎖和行鎖發(fā)生的條件。比如,全表掃描時,DB2會在整個表上加表級鎖;如果是增刪改操作,會加表級排他鎖。
-
在不清楚死鎖原因時,或者不了解鎖的機制和隔離級別機制時,不同隔離級別下SQL的影響范圍,可以在數(shù)據(jù)庫客戶端工具上進行手工小實驗,驗證數(shù)據(jù)庫機制以及猜想。
七、后續(xù)提升網(wǎng)銀系統(tǒng)性能備選方法
1、增加緩存的使用
-
對于讀多寫少的數(shù)據(jù),可以加載到分布式緩存,降低數(shù)據(jù)庫壓力;
-
目前已經(jīng)將部分參數(shù)和錯誤碼數(shù)據(jù)放到分布式緩存,后續(xù)謹慎提高緩存使用率,降低數(shù)據(jù)庫壓力。
2、精簡BP日志。刪除交易訪問記錄日志表的操作。
3、合并、精簡接口數(shù)量,前端緩存數(shù)據(jù)。