基本情況及應(yīng)用規(guī)模
xxx第三人民醫(yī)院是一所集醫(yī)療、科研、教學(xué)為一體的綜合性三級甲等醫(yī)院。醫(yī)院現(xiàn)有病床650張,日門診量1000人次左右,住院人數(shù)500人左右。有26個臨床科室、11個醫(yī)技科室,并開設(shè)有60多個專科專病門診。
醫(yī)院目前使用了醫(yī)院信息系統(tǒng)標(biāo)準(zhǔn)版、門診醫(yī)生工作站系統(tǒng)、病案管理系統(tǒng)、LIS系統(tǒng)(LIS接口程序從ZLHIS中提取病人信息和并完成計費功能),沒有使用住院醫(yī)生工作站。高峰期用戶連接數(shù)160個左右,HIS系統(tǒng)工作站150臺左右。
醫(yī)院的軟硬件環(huán)境如下:
機器名
|
HISSERVER
|
用途
|
運行ZLHIS標(biāo)準(zhǔn)版及醫(yī)囑系統(tǒng)
|
硬件供貨商
|
IBM
|
型號
|
X365、X250雙機容錯環(huán)境
|
Cluster SoftWare
|
DataWare
|
操作系統(tǒng)
|
Windows 2000 Server(SP4)
|
內(nèi)存
|
4G DRR
|
CPU 個數(shù)
|
2
|
剩余磁盤空間(in %)
|
|
ORACLE實例數(shù)量
|
1
|
IP地址
|
192.168.0.3(雙機系統(tǒng)虛擬IP)
|
網(wǎng)絡(luò)環(huán)境
|
千兆骨干、百兆到桌面
|
問題現(xiàn)象
2006年3月8日,醫(yī)院系統(tǒng)管理人員報告,系統(tǒng)在業(yè)務(wù)高峰期的9:00:~11:00時段,門診收費時有停頓的現(xiàn)象,有時一張單據(jù)保存需要1分鐘左右的時間。LIS系統(tǒng)提取病人信息的速度非常慢,等待時間以分鐘計。由于整個系統(tǒng)的運行速度較慢,已影響到醫(yī)院正常業(yè)務(wù)的開展,醫(yī)院各科室的意見比較大。服務(wù)器CPU占用率近期從原來的15%增長到現(xiàn)在的40%左右。
與市二院的對比分析
市二院與市三院在業(yè)務(wù)規(guī)模和應(yīng)用規(guī)模上比較接近,我們希望從兩家醫(yī)院的應(yīng)用環(huán)境對比分析中找到一些解決問題的線索。
對比項目
|
市二院
|
市三院
|
服務(wù)器
|
HP DL680
2G內(nèi)存/2*Xeon CPU /SCSI 360 72.8G * 2
|
IBM X365
4G內(nèi)存/2*Xeon CPU/SCSI 360 72.8G * 2
|
磁盤陣列
|
同有8210/ Raid 5 + Spare/128M Cache
|
寧波豪威/ Raid 5 + Spare/64M Cache
|
操作系統(tǒng)
|
Win 2000 Server(SP4)
|
Win 2000 Server(SP4)
|
數(shù)據(jù)庫系統(tǒng)
|
Oracle 10.1.3
|
Oracle 8.1.6
|
應(yīng)用系統(tǒng)
|
醫(yī)院信息系統(tǒng)標(biāo)準(zhǔn)版、住院醫(yī)生工作站
|
醫(yī)院信息系統(tǒng)標(biāo)準(zhǔn)版、門診醫(yī)生工作站、LIS系統(tǒng)(北京虹橋)
|
用戶連接數(shù)
|
140
|
160
|
站點數(shù)
|
140
|
150
|
數(shù)據(jù)大小
|
病人費用記錄1600萬行記錄
|
病人費用記錄1000萬行記錄
|
使用系統(tǒng)時間
|
4年
|
1.5年
|
從上表中,我們看到市二院啟用了住院醫(yī)生工作站系統(tǒng),沒有使用門診醫(yī)生工作站系統(tǒng),二家醫(yī)院在用戶連接數(shù)和硬件環(huán)境上都比較接近。市二院使用ZLHIS的時間要比市三院長,數(shù)據(jù)量也比市三院大,但市二院在使用過程中沒有反映速度慢的問題,系統(tǒng)運行速度比較理想。
通過以上的對比分析,我們發(fā)現(xiàn)以下的主要差異:
1、數(shù)據(jù)庫版本不同。市三院目前為816版本,市二院為10.1.3。
兩個版本的性能差異對ZLHIS而言,主要體現(xiàn)以下兩個方面:
1)、10.1.3使用的本地管理的表空間(LMT),而816默認(rèn)情況下使用的是字典管理的表空間(DMT)。LMT很好的解決了DMT的遞歸SQL與熱塊爭用問題。可以帶來數(shù)據(jù)寫入和空間管理性能很大的提升。
2)、10.1.3中本地管理的表空間,可以有效避免數(shù)據(jù)文件碎片,自動合并未使用的數(shù)據(jù)塊。
3)、Oracle 10g新增的回退段自動管理功能,很好地解決了8i下的回退段設(shè)置問題,在8i下如果回退段設(shè)置不當(dāng),會嚴(yán)重地影響數(shù)據(jù)庫的性能。
2、cursor_sharing參數(shù)的不同
進一步分析數(shù)據(jù)庫參數(shù),發(fā)現(xiàn)兩家醫(yī)院的cursor_sharing參數(shù)設(shè)置不相同。三院的設(shè)置為exact,也就是精確匹配。由于ZLHIS中綁定變量使用得不是很好,大量的SQL語句需要做硬分析(Hard Parse)。
二院設(shè)置為similar,這是10g新增加的選項。簡單的講,為了解決僅僅因為列值不同而導(dǎo)致SQL語句不能共享的問題,引入了CURSOR_SHARING參數(shù),使這類SQL也可以使用共享SQL,從而提高效率,降低耗費在SQL語句解析上的CPU等各種資源。但使用similar選項也可以帶來一些問題,比如把空格也解析成綁定變量 ;市二院的一日費用清單的數(shù)據(jù)源就出現(xiàn)過這種問題。最好的方式還是在編程時就避免硬編碼的SQL語句,使用綁定變量。從二院的實際應(yīng)用效果,使用simimar選項,效果比較明顯。
3、市二院沒有使用LIS系統(tǒng)。
市三院的LIS系統(tǒng)與ZLHIS進行了連接,LIS系統(tǒng)接口從ZLHIS中讀取視圖(VIEW)提取病人信息,并調(diào)用ZLHIS數(shù)據(jù)庫的存儲過程(PROCEDURE)完成計費功能。
問題診斷
首先,我想到是否因為系統(tǒng)問題導(dǎo)致性能下降,所以到達現(xiàn)場后,依次進行了如下檢查:
1)、使用Windows 2000任務(wù)管理器查看CPU及內(nèi)存使用情況,CPU利用率偏高,且波動較大,符合系統(tǒng)管理員的描述,內(nèi)存利用率較為平穩(wěn)。
2)、使用Windows 2000的事件查看器查看操作系統(tǒng)日志,沒有發(fā)現(xiàn)異常。
3)、查看Oracle的后臺進程文件(alert_orcl.log),沒有發(fā)現(xiàn)異常。初步排除是Oracle錯誤引起的性能下降
4)、檢查磁盤陣列,沒有發(fā)現(xiàn)磁盤損壞,磁盤陣列工作正常。
5)、醫(yī)院管理人員配合檢查了網(wǎng)絡(luò)環(huán)境,確認(rèn)網(wǎng)絡(luò)設(shè)備工作正常,沒有出現(xiàn)不穩(wěn)定現(xiàn)象。
經(jīng)過以上的檢查分析,排除了硬件環(huán)境和操作系統(tǒng)方面的故障引起的性能下降,接下來,主要從應(yīng)用和數(shù)據(jù)庫方面進行分析與調(diào)整。
在診斷和調(diào)整過程中,主要使用Statspack和SQL_TRACE工具。
Oracle Statspack工具從816中開始引入Oracle中,已成為DBA用來診斷數(shù)據(jù)庫性能的強有力工具。它通過收集一組數(shù)據(jù)庫運行數(shù)據(jù),記錄數(shù)據(jù)庫的運行狀況,生成Statspack報告。通過對Statspack報告的分析確定性能的瓶頸所在。目前,Statspack工具已經(jīng)在Oracle性能調(diào)整中得到了廣泛應(yīng)用。
SQL_TRACE是Oracle提供的用于進行SQL跟蹤的手段。生成的Trace文件進行格式化后,提供了包括SQL文本,執(zhí)行計劃,執(zhí)行效率等重要信息;是強有力的輔助診斷工具.在日常的數(shù)據(jù)庫問題診斷和解決中,SQL_TRACE是非常常用的方法。
本次調(diào)整使用Statspack工具收集業(yè)務(wù)高峰期的系統(tǒng)運行情況,并以此作為調(diào)整的基礎(chǔ)。Sql_Trace主要用于跟蹤分析Statspack報告中的Top SQL部分的SQL語句,還用于定位引起個別功能運行速度較慢的SQL語句。
經(jīng)過對Statspack報告的分析,我們初判斷影響性能的主要原因有:
1、“病人費用匯總”表的行級鎖定。醫(yī)保事務(wù)需要更新當(dāng)天的病人費用匯總表中的一行,獲得一個行級鎖定,當(dāng)普通業(yè)務(wù)也需要更新相同的行時,必須等待醫(yī)院業(yè)務(wù)事務(wù)完成,這種等待就造成了系統(tǒng)速度慢甚至是死機的現(xiàn)象。
2、從高峰期生成的Statspack報告中關(guān)于SQL共享使用的指標(biāo)都不理想,軟解析比率為:75.71%。原因是ZLHIS沒有很好的使用綁定變量,造成了語句大量的硬解析,耗費了大量的系統(tǒng)資源,這也是醫(yī)院CPU利用率增高的原因之一。
3、從報告的TOP SQL部分發(fā)現(xiàn)LIS接口提取病人信息的SQL邏輯讀很高,進一步使用SQL_Trace功能分析語句,發(fā)現(xiàn)這段SQL對“病人掛號記錄”和”病人信息”表都做了全表掃描,運行效率低下。
詳細的Statspack報告分析請參見附錄A和附錄B。
問題解決過程
基于上述分析,于2006年3月12日將Oracle數(shù)據(jù)庫系統(tǒng)升級到了10.2.0,升級后主要做了以下調(diào)整:
1、使用cursor_sharing=similar參數(shù),以使ZLHIS的大量硬編碼的SQL語句能夠共享使用,解決綁定變量的問題,降低SQL硬解析的系統(tǒng)消耗。(關(guān)于使用綁定變量效率提升的實驗請參見附錄C)。
2、建立基于函數(shù)to_char的索引,改善LIS提取病人信息的SQL語句的執(zhí)行效率。
3、使用定時刷新的物化視圖代替“病人費用匯總”表,并創(chuàng)建一個“病人費用匯總”視圖,然后在視圖上建立替代觸發(fā)器,屏蔽掉程序的DML語句。
4、表空間全部采用ASSM方式的本地管理表空間(LMT),減少維護表空間的遞歸SQL消耗,避免Freelist的爭用。
經(jīng)過升級調(diào)整后,觀察CPU占用率從原來的平均45%左右,降到了現(xiàn)在的20%左右。
經(jīng)過升級調(diào)整后,LIS提取病人信息的速度有了明顯提高,檢驗科使用人員比較滿意,醫(yī)院各業(yè)務(wù)窗口部門普遍反映速度有了提高。
后續(xù)問題的處理
在Oracle8i下優(yōu)化器默認(rèn)為Choose方式(optimizer_mode=choose),Oracle 10g下優(yōu)化器默認(rèn)為All_rows(CBO的一種),為保證語句的執(zhí)行計劃與在8i下的一致性,并考慮到程序有的地方強行使用了RBO,將優(yōu)化器模式修改為choose。Oracle 10g對CBO有了更好的支持,但是由于優(yōu)化器和其他環(huán)境的變化導(dǎo)致了部分SQL語句在10g下運行速度較慢。 主要體現(xiàn)在以下兩個方面:
1)、升級后部分藥品相關(guān)的模塊運行速度有了下降,對這部分SQL進行了調(diào)整,運行速度恢復(fù)正常。
2)、部分報表執(zhí)行速度較慢,接下來對系統(tǒng)中的報表SQL進行了集中優(yōu)化和調(diào)整。
調(diào)整SQL時主要使用了以下幾種方法:
1)、對相關(guān)表作分析(Analyze),收集準(zhǔn)確的統(tǒng)計信息。
2)、禁用部分不合理的索引,消除其對執(zhí)行計劃的影響,如:藥品收發(fā)記錄_IX_庫房ID。
3)、使用提示字(Hint),穩(wěn)定SQL語句的執(zhí)行計劃。
更為詳細SQL調(diào)整技巧請參見相關(guān)文檔。
總結(jié)
此次數(shù)據(jù)庫分析調(diào)整印證了“60%的性能問題都是應(yīng)用問題”這句名言。使用cursor_sharing=similar只是權(quán)宜之際,并可能會帶來一定的副作用,我們的程序在綁定變量的使用上還有很長的路要走。“病人費用匯總”這種在當(dāng)初看起來不錯的設(shè)計,由于醫(yī)保業(yè)務(wù)的發(fā)展,在今天卻成為了一個嚴(yán)重影響性能的缺陷。我們在數(shù)據(jù)庫結(jié)構(gòu)設(shè)計中不能默守陳規(guī),必須適應(yīng)不斷變化的業(yè)務(wù)需要。
有時,一條不良的SQL語句可能會導(dǎo)致整個系統(tǒng)的性能下降,影響客戶對整個系統(tǒng)的評價,最好的方式是在程序開發(fā)階段就把這些劣質(zhì)SQL扼殺在搖籃里,有時甚至為了性能,犧牲部分不實用的功能也是值得的,我們需要在性能與功能之間做出平衡。出現(xiàn)性能問題時我們需要用科學(xué)的方法分析解決問題,做出正確的判斷,而不是依據(jù)經(jīng)驗和主觀意識進行判斷。
附錄:
A、 調(diào)整前的Statspack報告分析
B、 調(diào)整后的Statspack報告對比分析
C、 升級過程及典型問題的處理
D、 LIS提取病人信息速度慢的解決過程
附錄A 調(diào)整前的Statspack報告分析
我們利用Oracle 的Statspack工具生成了上午10:15至11:00的報告,用以作為分析和調(diào)整的基礎(chǔ)。
1 數(shù)據(jù)庫壓力及緩沖區(qū)情況分析
Cache Sizes
~~~~~~~~~~~
db_block_buffers: 128000
db_block_size: 8192
log_buffer: 1048576
shared_pool_size: 314572800
Load Profile
~~~~~~~~~~~~
Per Second Per Transaction
--------------- ---------------
Redo size: 16,906.18 8,828.71
Logical reads: 33,288.68 17,3894
Block changes: 121.76 658
Physical reads: 92.41 48.26
Physical writes: 10.52 5.49
User calls: 271.47 141.77
Parses: 632 307
Hard parses: 15.38 8.03
Sorts: 26.16 166
Transactions: 1.91
Rows per Sort: 1,576.13
Pct Blocks changed / Read: 0.37
Recursive Call Pct: 48.71
Rollback / transaction Pct: 0.15
1)、Redo size: 16,906.18: 每秒鐘產(chǎn)生16,906.18字節(jié)的重做日志。每分鐘產(chǎn)生約1M左右的重做日志(16,906.18*60/1024),系統(tǒng)壓力相對還是比較大。
2)、Physical reads 92.41物理讀為92.41,很少基本可以忽略,初步判斷,高整緩沖的設(shè)置沒有太大的問題。
3)、Transactions: 1.91 每秒鐘2個事務(wù)左右,也證明系統(tǒng)壓力較大。
2實例效果分析
Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer Nowait Ratio: 100.00
Buffer Hit Ratio: 99.72
Library Hit Ratio: 921
Redo NoWait Ratio: 99.99
In-memory Sort Ratio: 99.90
Soft Parse Ratio: 75.71
Latch Hit Ratio: 99.97
1)、Buffer Hit Ratio 99.72 從這部分看高速緩存命中率為99.72,比較理想,不需要再加大高速緩存。
2)、Soft Parse Ratio 75.71 。這項值低于90%,一般證明系統(tǒng)沒有很好的使用綁定變量。ZLHIS在綁定變量的使用上還有很長的路要走。建議升級到Oracle 9i/10g 使用cursor_sharing=similar參數(shù),可以取得較好的效果。
3)、In-memory Sort Ratio: 99.90,體現(xiàn)出排序基本上都在內(nèi)存中進行,不需要調(diào)整排序區(qū)。
3等待事件分析
Top 5 Wait Events
~~~~~~~~~~~~~~~~~ Wait % Total
Event Waits Time (cs) Wt Time
-------------------------------------------- ------------ ------------ -------
db file sequential read 235,854 138,851 87.46
enqueue 45 9,025 5.68
SQL*Net more data from dblink 3,300 4,448 2.80
SQL*Net message from dblink 402 2,171 1.37
log file sequential read 810 1,062 .67
1) db file sequential read 為最主要的等待事件(87.46%),這通常是指單一的數(shù)據(jù)塊讀操作(例如,索引的讀。。該值過大說明表的連接順序很糟糕;蛘呤褂昧朔沁x擇性的索引。數(shù)據(jù)庫的主要調(diào)整方向為:優(yōu)化SQL,使用正確的索引,改善表連接效果。
2)enqueue閂鎖是底層的隊列機制,用于保護系統(tǒng)的全局區(qū)(SGA)的內(nèi)存結(jié)構(gòu)。使用本地管理的表空間(LMT)可以有效降低這部分等待事件,目前ZLHIS使用的是字典管理的表空間(DMT),會造成Fet$、Uet$系統(tǒng)表的爭用(字典管理表空間使用這兩個表記錄字典管理表空間的使用情況,LMT使用了位圖(BITMAP)管理表空間,顯著地提高了效率。
3) SQL*Net more data from dblink、SQL*Net message from dblink,為系統(tǒng)空閑事件,可不必關(guān)注,造成這兩者較高的原因可能是ZLHIS通過網(wǎng)絡(luò)向醫(yī)保前置機大量取數(shù)產(chǎn)生的。
4)enqueue 進而查看報告的另一部分
Enqueue activity for DB: ORCL Instance: orcl Snaps: 2 - 3
Enqueue Gets Waits
---------- ------------ ----------
TX 6,908 2 -------------------------------------------------------------
發(fā)現(xiàn)主要為TX類型的Enqueue等待,一般而言通常有三種情況產(chǎn)生:
(1)、唯一索引中的重復(fù)索引,需要提交(commit)或回段(rollback)事務(wù)。
(2)、位圖索引的更新,由于ZLHIS中沒有使用位圖索引,所以這應(yīng)該不是主要原因。
(3)、多個用戶同時更新同一個塊,即熱點塊爭用。典型的就是“病人費用匯總”表的塊爭用問題,可以判定這是Enqueue等待事件較高的原因。
4磁盤IO分析
Tablespace IO Summary for DB: ORCL Instance: orcl Snaps: 2 - 3
Avg Read Total Avg Wait
Tablespace Reads (ms) Writes Waits (ms)
------------------------- ----------- -------- ----------- ---------- --------
ZL9EXPENSE 134,886 5.9 7,755 10 0.0
ZL9PATIENT 88,595 5.6 1,196 0 0.0
SYSTEM 5,781 6.9 4,053 0 0.0
ZL9MEDLST 5,095 7.5 1,791 0 0.0
RBS 38 9.7 2,837 9 0.0
TEMP 1,809 0.0 967 0 0.0
ZL9CISREC 1,584 6.8 484 0 0.0
ZL9BASEITEM 787 9.7 209 2 0.0
ZLTOOLSTBS 656 8.3 5 0 0.0
USERS 10 2.0 259 0 0.0
ZLTOOLSTMP 23 0.0 87 0 0.0
ZL9DUEREC 30 4.0 5 0 0.0
ZL9HISTORY 7 1.4 5 0 0.0
DRSYS 5 0.0 5 0 0.0
INDX 5 0.0 5 0 0.0
TOOLS 5 0.0 5 0 0.0
-------------------------------------------------------------
從statspack報告的磁盤IO部分來看,發(fā)現(xiàn)ZLEXPENSE和ZLPATIENT的磁盤IO最大。ZLEXPENSE上面存儲了費用相關(guān)的表,IO大是正常的。ZL9PATIENT的IO偏大是不正常的,可以判定是由于LIS系統(tǒng)提取病人信息的的全表掃描引起大量的磁盤IO。可以看到,解決LIS系統(tǒng)提取病人信息的全表掃描,是系統(tǒng)調(diào)整的一個關(guān)鍵點。
5低效SQL分析
SQL ordered by Gets for DB: ORCL Instance: orcl Snaps: 2 - 3
Gets % of
Buffer Gets Executes per Exec Total Hash Value
-------------- ------------ ------------ ------ ------------
SQL statement
------------------------------------------------------------------------------
16,933,155 4,275 3,961.0 18.8 2331755529
UPDATE 病人費用匯總 SET 結(jié)帳金額=NVL(結(jié)帳金額,0) + :b1 WHERE 日期 = TRUNC(SYS
11,084,772 623 17,792.6 12.3 2504304975
SELECT NVL(SUM(實收金額),0) FROM 病人費用記錄 WHERE 病人ID + 0 = :b1 AND
3,738,223 102 36,649.2 4.2 467005032
Select B.登記時間,A.病人ID,A.門診號,B.NO,Nvl(A.姓名,' ') as 姓名, A.性別,A.年
3,198,259 803 3,982.9 6 4234930587
UPDATE 病人費用匯總 SET 應(yīng)收金額=NVL(應(yīng)收金額,0) + :b1 ,實收金額=NVL(實收金額,
1,966,927 510 3,856.7 2.2 4224504530
UPDATE 病人費用匯總 SET 應(yīng)收金額=NVL(應(yīng)收金額,0) + :b1 ,實收金額=NVL(實收金額,
1,557,260 328 4,747.7 1.7 1195480746
Select Distinct P.ID,P.號碼 as 號別,P.號類,P.科室ID,B.名稱 As 科室,P.項目ID
1,311,536 336 3,904 1.5 7273237
UPDATE 病人費用匯總 SET 應(yīng)收金額=NVL(應(yīng)收金額,0) + :b1 ,實收金額=NVL(實收金額,
1,304,408 92 14,178.3 1.4 1249658810
Select Rownum as KeyID,A.* From ( Select A.類別 AS 類別ID,E.ID as 診療項目ID,
1,262,369 202 6,249.4 1.4 3289008884
Select A.*,Nvl(B.使用時間,To_Date('1900-01-01','YYYY-MM-DD')) As 使用時間 From
1,080,637 177 6,105.3 1.2 137210266
Select A.*,Nvl(B.使用時間,To_Date('1900-01-01','YYYY-MM-DD')) As 使用時間 From
從SQL ordered by Gets部分可以看出,大部分的低效SQL大多數(shù)都與“病人費用匯總”有關(guān),而且多以update操作為主。在ZLHIS中很多存儲過程都要update“病人費用匯總”表,且很多update語句沒有很好用到索引。還有就是update語句需要獲得Oracle行級排它鎖,這也是造成Enqueue等待事件較高的原因。解決”病人費用匯總”行級鎖定和訪問效率是提高運行速度的又一關(guān)鍵點。
6連接用戶數(shù)分析
在32位平臺上,Oracle進程的內(nèi)存尋址空間有2G的限制,實際上Oracle.exe進程能夠使用的內(nèi)存為1.75G左右。那么SGA+PGA只能有1.75G左右。市三院在高峰期會話數(shù)有180左右,最大時達到230個session。由于使用專用服務(wù)器,每個Session大概需要2M左右的合內(nèi)存。所以整個 SGA不能大于1290M (1750M-460M)。
在實例SGA設(shè)置時,正是遵循了上述的原則。要使用更大的SGA,有兩種解決方案:
1)、使用共享服務(wù)器模式。但由于ZLHIS系統(tǒng)有很多的大事務(wù)要運行,這種模式會帶來,會話事務(wù)處理的等待。
2)、將平臺升級到64bit。這需要大量的硬件和軟件投資。
由于從Statspack的分析來看,SGA各個部分的使用效果都不錯,所以沒有調(diào)整SGA的必要。
附錄B 調(diào)整后的Statspack報告對比分析
升級后Oracle10g并進行調(diào)整后,我們在同一時間段生成Staspack報告,發(fā)現(xiàn)各項指標(biāo)有了明顯改善。報告的前半部分如下:
STATSPACK report for
Database DB Id
Instance Inst Num
Startup Time Release
RAC
~~~~~~~~ ----------------------- -------- 1112179643 orcl 1
07-3月 -06 07:3 10.2.0.1.0 NO
4
Host Name: HISSERVER Num CPUs: 4
Phys Memory (MB): 3,583
~~~~
Snapshot Snap Id Snap Time Sessions Curs/Sess Comment
~~~~~~~~ ---------- ------------------ -------- --------- -------------------
Begin Snap: 1 08-3月 -06 10:14:16 161 28.2
End Snap: 11 08-3月 -06 10:58:44 156 30.6
Elapsed: 44.47 (mins)
Cache Sizes Begin End
~~~~~~~~~~~ ---------- ----------
Buffer Cache: 800M Std Block Size: 8K
Shared Pool Size: 200M
Log Buffer: 6,860K
Load Profile Per Second Per Transaction
~~~~~~~~~~~~ --------------- ---------------
Redo size: 12,685.88 6,535.22
Logical reads: 11,708.79 6,031.87
Block changes: 81.76 42.12
Physical reads: 56.86 29.29
Physical writes: 6.28 24
User calls: 278.34 1439
Parses: 100.27 51.65
Hard parses: 12.57 6.47
Sorts: 39.76 20.49
Logons: 0.12 0.06
Executes: 141.92 711
Transactions: 1.94
% Blocks changed per Read: 0.70 Recursive Call %: 76.19
Rollback per transaction %: 0.14 Rows per Sort: 309.51
Instance Efficiency Percentages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer Nowait %: 99.96 Redo NoWait %: 100.00
Buffer Hit %: 99.51 In-memory Sort %: 100.00
Library Hit %: 94.87 Soft Parse %: 87.47
Execute to Parse %: 29.35 Latch Hit %: 99.93
Parse CPU to Parse Elapsd %: 87.81 % Non-Parse CPU: 91.11
Shared Pool Statistics Begin End
------ ------
Memory Usage %: 894 80.73
% SQL with executions>1: 796 75.00
% Memory for SQL w/exec>1: 87.20 89.54
Top 5 Timed Events Avg %Total
~~~~~~~~~~~~~~~~~~ wait Call
Event Waits Time (s) (ms) Time
----------------------------------------- ------------ ----------- ------ ------
CPU time 2,754 71.8
db file sequential read 92,563 718 8 18.7
read by other session 12,429 156 13 4.1
db file scattered read 8,398 127 15 3
latch: shared pool 1,652 27 16 .7
-------------------------------------------------------------
相比8i下的statspack主要有以下幾點改善:
1)、Soft Parse %: 87.47
Sql語句軟解析比率有了很大的提高,提高了10%左右,主要是在10g使用了cursor_sharing=similar參數(shù)。
2)、Enqueue等待事件不再是主要的等待事件,主要是得益于將“病人費用匯總”用物化視圖代替,減少了表的行級鎖定。
3)、最主要的等待事件變成了CPU time,此事件為Oracle空閑事件,空閑事件是不能避免的,在性能調(diào)整中,不必太關(guān)注此類事件。
4)、TOP 5 部分的幾個等待事件等待時間都很小。對比8i下的top5中的幾個等待事件的等待時間減少了很多。 如db file sequential read 由原來的138,851ms,減少到現(xiàn)在的718ms?梢姼纳频姆冗是很大的。
附錄C 升級過程及典型問題的處理
1、時間安排
時間
|
事項
|
說明
|
12:00
|
安裝介質(zhì)準(zhǔn)備
|
Oracle 10.2.0 RDBMS光盤
|
12:30
|
模擬升級
|
利用上周的冷備份進行模擬升級
|
15:00
|
解決模擬升級中的問題,測試
|
作好相關(guān)解決記錄
|
20:00
|
開始正式升級
|
通知相關(guān)科室,系統(tǒng)停止使用
|
20:10
|
配置Dataware雙機環(huán)境
|
清除DataWare雙機中資源中的Oracle服務(wù)
|
20:20
|
備份數(shù)據(jù)庫
|
冷備份數(shù)據(jù)庫并作EXP邏輯備份(均要求異地存檔)
|
21:00
|
冷備份數(shù)據(jù)驗證
|
異地進行
|
21:10
|
卸載Oracle 816
|
|
21:30
|
安裝Oracle 10.2.0
|
包括建立新數(shù)據(jù)庫實例
|
22:00
|
對新實例進行配置
|
主要是配置初始化參數(shù)和歸檔日志路徑
|
22:30
|
導(dǎo)入數(shù)據(jù)
|
使用先前的邏輯備份的DMP文件
|
00:30
|
數(shù)據(jù)驗證
|
|
00:50
|
數(shù)據(jù)庫連接用戶數(shù)測試
|
使用編寫的VB程序
|
01:10
|
重新配置DataWare環(huán)境
|
|
01:40
|
雙機環(huán)境及用戶測試
|
|
01:50
|
冷備份升級成功的數(shù)據(jù)庫
|
|
02:00
|
升級完成
|
|
2、升級過程難點問題的解決
1)、表空間的處理。
由于10G與8I的數(shù)據(jù)文件默認(rèn)存放路徑不一致,不能直接導(dǎo)入表空間,需要先創(chuàng)建相關(guān)的表空間。我在8i下用SQL生成SQL的方式重新創(chuàng)建表空間的腳本,對生成的后稍加修改,就是創(chuàng)建表空間的腳本。
SQL> select 'create tablespace '||a.name||' datafile '''||b.name
2 ||''' size 100m autoextend on EXTENT MANAGEMENT LOCAL AUTOALLOCATE BLOCKSIZE 8K ;' as cmd
3 from v$tablespace A,v$datafile b
4 where a.TS#=b.TS#
5 ;
CMD
……………………………………………………………………………………
create tablespace SS datafile 'D:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\SS01.DBF' size 100m autoextend on EXTENT MANAGEMENT LOCAL AUTOALLOCATE BLOCKSIZE 8K ;
……………………………….
28 rows selected
SQL>
2)、IMP – 00020錯誤的解決。
在導(dǎo)入“病人信息”表時出現(xiàn)IMP-00020錯誤。
導(dǎo)入了 0 行
. . 正在導(dǎo)入表 "病人未結(jié)費用"
導(dǎo)入了 10793 行
. . 正在導(dǎo)入表 "病人信息"
IMP-00020: 列緩沖區(qū)大小 (4) 的長列過大
. . 正在導(dǎo)入表 "病人醫(yī)囑發(fā)送"
導(dǎo)入了 263066 行
病人信息不能導(dǎo)入,查詢Oracle MetaLink此錯誤的解釋為:
由于在”病人信息”上使用了基于to_char的函數(shù)索引,先懷疑是函數(shù)的問題。按照metaLink的解釋,先試著加大導(dǎo)入時的buffer和使用commit=y、constrains=n、indexes=n參數(shù),還是出現(xiàn)同樣的錯誤。于是初步判斷是由于導(dǎo)出時,使用了direct=y造成的,是遇上了816的一個bug。于是決定使用冷備份還原,重新導(dǎo)出“病人信息”表, 重新導(dǎo)入成功,至此問題解決。
3、Oracle 10g的參數(shù)設(shè)置
Parameter Name Begin value (if different)
----------------------------- --------------------------------- --------------------------------------------------------------------------------
audit_file_dest D:\ORACLE\PRODUCT\10.2.0\ADMIN\OR
background_dump_dest D:\ORACLE\PRODUCT\10.2.0\ADMIN\OR
compatible 10.2.0.1.0
control_files F:\ORACLE\ORADATA\ORCL\CONTROL01.
core_dump_dest D:\ORACLE\PRODUCT\10.2.0\ADMIN\OR
cursor_sharing SIMILAR
db_block_size 8192
db_cache_size 838860800
db_domain DOMAIN
db_file_multiblock_read_count 16
db_name ORCL
db_recovery_file_dest d:\oracle\product\10.2.0/flash_re
db_recovery_file_dest_size 2147483648000
dispatchers (PROTOCOL=TCP) (SERVICE=ORCLXDB)
java_pool_size 25165824
job_queue_processes 10
large_pool_size 16777216
log_archive_dest_1 LOCATION=D:\oracle\product\10.2.0
log_archive_dest_2 LOCATION=F:\oracle\ORADATA\ORCL\A
log_archive_format ARC%S_%R.%T
open_cursors 300
optimizer_mode CHOOSE
pga_aggregate_target 1073741824
processes 400
remote_login_passwordfile EXCLUSIVE
sga_max_size 1325400064
shared_pool_size 209715200
spfile D:\ORACLE\PRODUCT\10.2.0\DB_1\DBS
undo_management AUTO
undo_tablespace UNDOTBS1
user_dump_dest D:\ORACLE\PRODUCT\10.2.0\ADMIN\OR
------------------------------------------------------------------------------------------------------------------------------------------------
主要參數(shù)說明:
1)、SGA使用手動管理。從實際市二院等醫(yī)院的使用情況來看,自動管理SGA的效果不是很好,可能跟ZLHIS的應(yīng)用有關(guān)。各SGA的大小參照8i下的SGA設(shè)置,因為從前面的分析來看,8i下的SGA沒有什么大的問題,運行效果較好。
2)、Cursor_sharing = similar參數(shù)的使用,使用此參數(shù)的目的在于SQL語句軟件解析的比率。此參數(shù),可以讓一些硬編碼的SQL語句,使用綁定變量,這由Oracle自動完成,下面是Trace出來一段業(yè)務(wù)SQL。
Select Distinct A.ID,A.編碼,A.名稱
From
部門表 A,部門性質(zhì)說明 B Where A.ID=B.部門ID And (B.服務(wù)對象 in(:"SYS_B_0",
:"SYS_B_1",:"SYS_B_2")) And B.工作性質(zhì) IN (:"SYS_B_3") And (A.撤檔時間 is
NULL or Trunc(A.撤檔時間)=To_Date(:"SYS_B_4",:"SYS_B_5")) Order by A.編碼
可以看到Oracle已經(jīng)對SQL語句使用了綁定變量,也就節(jié)約了解析時間,但這個參數(shù)可能會帶來一些副作用,比如把空格翻譯成綁定變量等,但從目前使用的情況來看,可以帶來一定的性能提升。最根本解決綁定變量問題的途徑只有一個:修改軟件,使用綁定變量。
下面通過一個實驗來看使用與不使用綁定變量之間的性能差別:
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as zlhis
SQL>
SQL> --創(chuàng)建兩個結(jié)構(gòu)完全一樣的表
SQL> create table test_bindvar (x number) ;
Table created
SQL> create table test_unbindvar (x number) ;
Table created
SQL> --使用綁定變量,使用動態(tài)SQL的using選項
SQL> declare
2 v_sql varchar2(400);
3 begin
4 dbms_output.put_line('開始時間:'||to_char(sysdate,'hh24:mi:ss'));
5 for i in 1..100000 loop
6 execute immediate 'insert into test_bindvar values(:x)' using i ;
7 end loop;
8 dbms_output.put_line('結(jié)束時間:'||to_char(sysdate,'hh24:mi:ss'));
9 end;
10 /
開始時間:16:17:33
結(jié)束時間:16:17:38
PL/SQL procedure successfully completed
SQL> --不使用綁定變量,直接“拼”sql。
SQL> declare
2 v_sql varchar2(400);
3 begin
4 dbms_output.put_line('開始時間:'||to_char(sysdate,'hh24:mi:ss'));
5 for i in 1..100000 loop
6 execute immediate 'insert into test_unbindvar values('||to_char(i)||')';
7 end loop;
8 dbms_output.put_line('結(jié)束時間:'||to_char(sysdate,'hh24:mi:ss'));
9 end;
10
11 /
開始時間:16:17:38
結(jié)束時間:16:19:08
PL/SQL procedure successfully completed
SQL> select count(*) from test_bindvar;
COUNT(*)
----------
100000
SQL> select count(*) from test_unbindvar;
COUNT(*)
----------
100000
SQL>
我們看到同樣插入100000行數(shù)據(jù),使用綁定變量用了5秒鐘時間,而不使用綁定變量,共用了1分30秒鐘。兩者的性能差別居然有18倍的差距!可以說目前ZLHS最大的性能問題,主要是沒有使用綁定變量。
3)、processes,默認(rèn)為150,太小,加大到400。
4、連接用戶數(shù)的測試
用VB寫了個小程序,使用ADO循環(huán)連接Oracle,測試可以連接的最大用戶數(shù)。發(fā)現(xiàn)只有200個SESSION左右。雖然醫(yī)院的高峰期的會話數(shù)只有180左右,但為了保證有擴展性,決定調(diào)整Oracle.exe進程的堆棧大小。注意必須停止Oracle服務(wù)才能調(diào)整,且當(dāng)前目錄必須為Oracle.exe的當(dāng)前目錄。
D:\oracle\product\10.2.0\db_1\BIN>orastack oracle.exe 500000
Dump of file oracle.exe
Current Reserved Memory per Thread = 1048576
Current Committed Memory per Thread = 4096
New Reserved Memory per Thread = 500000
D:\oracle\product\10.2.0\db_1\BIN>
調(diào)整后的連接數(shù)可以達到250左右。
5、對表及索引進行分析(ANALYZE)
是由于升級到10g后,默認(rèn)使用CBO優(yōu)化器,而CBO必須依賴準(zhǔn)確的統(tǒng)計信息來確定SQL語句的執(zhí)行計劃。統(tǒng)計信息錯誤,將會影響SQL語句的執(zhí)行計劃,使Oracle使用錯誤的執(zhí)行路徑,從而影響執(zhí)行速度。使用ANALYZE命令對表進行分析:
用SQL生成SQL的方式產(chǎn)生分析腳本對表進行全表分析。
select 'analyze table '||table_name||
' computed statistics for all indexes for all indexed columns ;'
from dba_tables where owner='ZLHIS'
and table_name not in ('病人費用記錄','藥品收發(fā)記錄')
病人費用記錄和藥品收發(fā)記錄由于數(shù)據(jù)量太大,對其只進行5%的采樣分析。
analyze table 病人費用記錄 estimate statistics sample 5 percent for all indexed columns for all indexes
analyze table 藥品收發(fā)記錄 estimate statistics sample 5 percent for all indexed columns for all indexes
6、<匯總打印發(fā)藥清單>速度很慢
通過分析報表數(shù)據(jù)源,發(fā)現(xiàn)<發(fā)藥時間>參數(shù)SQL對藥品收發(fā)記錄使用了全表掃描。
SQL語句:
select distinct A.審核日期,名稱
from 藥品收發(fā)記錄 A,部門表 B
where A.審核日期 between sysdate-30 and sysdate and A.對方部門id=B.id
and A.單據(jù) In (10) and A.發(fā)藥方式=3 And Mod(A.記錄狀態(tài),3)=1
order by A.審核日期 desc
是由于升級到10g后,使用了CBO優(yōu)化器,而藥品收發(fā)記錄的統(tǒng)計信息還沒有更新,導(dǎo)致Oracle使用了全表掃描,而不是使用審核日期上的建立的索引。前面做5%的采樣分析獲得的統(tǒng)計信息不準(zhǔn)確。決定對藥品收發(fā)記錄表進行全表分析。
analyze table 藥品收發(fā)記錄compute statistics for all indexed columns for indexes
7、進入<病人一日費用清單>功能模塊速度很慢
使用SQL_TRACE功能產(chǎn)生Trace文件。分析產(chǎn)生的Trace文件發(fā)現(xiàn)如下一段:
**************************************************************************************************************************
Select L.病人id,L.主頁id,I.姓名,I.住院號,LPAD(P.出院病床,:"SYS_B_0",
:"SYS_B_1") as 床號, I.性別,P.入院日期,P.出院日期,P.險類,P.病人性質(zhì),P.費別,
X.名稱 as 病區(qū)
From
部門表 X,病人信息 I,病案主頁 P,(Select Distinct 病人id,主頁id From
病人費用記錄 Where 登記時間 Between to_Date(:"SYS_B_2",:"SYS_B_3") and
to_date(:"SYS_B_4",:"SYS_B_5") And 記錄狀態(tài) IN(:"SYS_B_6",:"SYS_B_7",
:"SYS_B_8") ) L Where I.病人id=P.病人id and P.病人id=L.病人id and P.主頁id=
L.主頁id And P.當(dāng)前病區(qū)ID=X.ID And P.出院日期 is NULL And P.險類 is NULL
Order BY 病區(qū),床號
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 2 0.01 0.01 0 0 0 0
Execute 2 0.00 0.00 0 0 0 0
Fetch 35 1.96 106.43 16541 87107 0 307
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 39 1.98 106.44 16541 87107 0 307
Misses in library cache during parse: 2
Optimizer mode: CHOOSE
Parsing user id: 690
Rows Row Source Operation
------- ---------------------------------------------------
146 SORT ORDER BY (cr=42727 pr=16384 pw=0 time=104493303 us)
146 VIEW (cr=42727 pr=16384 pw=0 time=104493063 us)
146 HASH UNIQUE (cr=42727 pr=16384 pw=0 time=104492765 us)
3011 FILTER (cr=42727 pr=16384 pw=0 time=8365121 us)
3011 NESTED LOOPS (cr=42727 pr=16384 pw=0 time=8356030 us)
3011 HASH JOIN (cr=33692 pr=16380 pw=0 time=8274701 us)
207 TABLE ACCESS FULL 部門表 (cr=7 pr=0 pw=0 time=521 us)
3011 TABLE ACCESS BY INDEX ROWID 病人費用記錄 (cr=33685 pr=16380 pw=0 time=8238586 us)
122201 NESTED LOOPS (cr=1312 pr=446 pw=0 time=4889863 us)
177 TABLE ACCESS FULL 病案主頁 (cr=631 pr=0 pw=0 time=27425 us)
122023 INDEX RANGE SCAN 病人費用記錄_IX_病人ID (cr=681 pr=446 pw=0 time=1742897 us)(object id 52063)
3011 TABLE ACCESS BY INDEX ROWID 病人信息 (cr=9035 pr=4 pw=0 time=122297 us)
3011 INDEX UNIQUE SCAN 病人信息_PK (cr=6024 pr=4 pw=0 time=89468 us)(object id 52143)
發(fā)現(xiàn)這段提取病人信息的SQL使用“病人費用記錄”表來實現(xiàn)只顯示產(chǎn)生費用的病人信息。這段SQL的物理讀非常大(16541,紅色字體部分) 進一步分析下面的執(zhí)行計劃發(fā)現(xiàn),使用的”病人費用記錄_IX_病人ID”索引,由于當(dāng)前在院的病人數(shù)較多,效率很低。一日清單一般只需要查詢當(dāng)天的費用,應(yīng)該“登記時間”索引的效率。問題原因與7.1一樣,都是對象的統(tǒng)計信息不準(zhǔn)確造成的。解決辦法同樣是對表作全表分析。
analyze table 病人費用記錄compute statistics for all indexed columns for indexes
8、升級檢查、用戶測試
使用服務(wù)器管理工具對導(dǎo)入的數(shù)據(jù)進行檢查、修正。以非ZLHIS用戶進行各種業(yè)務(wù)的測試。至此升級完成。
附錄D LIS提取病人信息速度慢的解決過程
1、問題現(xiàn)象
檢驗科LIS系統(tǒng)通過接口程序提取zlhis病人信息速度很慢,以分鐘計算。
2、故障分析
可能是提取病人信息的VIEW的SQL的執(zhí)行效率低下,根據(jù)執(zhí)行計劃調(diào)整SQL語句。
3、解決過程
查看原始的視圖語句:
create or replace view patientinfo as
Select
to_char(A.門診號) as 門診號,to_char(A.住院號) as 住院號,A.姓名,
Decode(A.性別,'男','M','女','F','U') AS 性別,
A.年齡,'Y' AS 年齡單位,'0'||C.編碼 AS 科室號,C.名稱 AS 科室,
to_char(B.出院病床) AS 床號,D.編號 AS 醫(yī)生號,E.描述信息 AS 入院診斷,
'非基本醫(yī)保' AS 付款類型,decode(a.當(dāng)前科室id,null,'O','I') AS 病人類別
From 病人信息 A,病案主頁 B,部門表 C,人員表 D,診斷情況 E
Where A.病人ID=B.病人ID(+) And A.住院次數(shù)=B.主頁ID(+)
And B.出院科室ID=C.ID(+) And B.住院醫(yī)師=D.姓名(+)
And B.病人ID=E.病人ID(+) And B.主頁ID=E.主頁ID(+) And
E.診斷類型(+)=1 and a.當(dāng)前科室id is not null
union
Select
to_char(A.門診號) as 門診號,to_char(A.住院號) as 住院號,A.姓名,Decode(A.性別,'男','M','女','F','U') AS 性別,
A.年齡,'Y' AS 年齡單位,'0'||j.科室號 AS 科室號,C.名稱 AS 科室
,to_char(B.出院病床) AS 床號,D.編號 AS 醫(yī)生號,E.描述信息 AS 入院診斷,
'非基本醫(yī)保' AS 付款類型,decode(a.當(dāng)前科室id,null,'O','I') AS 病人類別
From 病人信息 A,病案主頁 B,部門表 C,人員表 D,診斷情況 E,
(select distinct (select 編碼 from 部門表 where id=執(zhí)行部門id) as 科室號,門診號 from 病人掛號記錄 ) j
Where A.病人ID=B.病人ID(+) And A.住院次數(shù)=B.主頁ID(+)
And B.出院科室ID=C.ID(+) And B.住院醫(yī)師=D.姓名(+) and a.門診號=j.門診號(+)
And B.病人ID=E.病人ID(+) And B.主頁ID=E.主頁ID(+) And E.診斷類型(+)=1 and a.當(dāng)前科室id is null
提取LIS接口程序讀取病人信息的SQL語句,查看語句的執(zhí)行計劃:
SQL語句:select * from patientinfo where 住院號 ='154426' 的執(zhí)行計劃如下:
Explain plan:
SELECT STATEMENT, GOAL = CHOOSE
VIEW Object owner=ZLHIS Object name=PATIENTINFO
SORT UNIQUE
TABLE ACCESS FULL Object owner=ZLHIS Object name=病人信息
………………..
TABLE ACCESS FULL Object owner=ZLHIS Object name=病人信息
………………
SORT JOIN
VIEW Object owner=ZLHIS
SORT UNIQUE
TABLE ACCESS FULL Object owner=ZLHIS Object name=病人掛號記錄
分析語句的執(zhí)行計劃,發(fā)現(xiàn)病人信息、病人掛號記錄表等為全表掃描。首先需要消除對“病人掛號記錄“的全表掃描。
SQL> select count(*) from 病人掛號記錄;
COUNT(*)
----------
478299
發(fā)現(xiàn)病人掛號記錄有478299條記錄,數(shù)據(jù)量還是比較大。進一步分析語句,發(fā)現(xiàn)條件中有A.門診號=J.門診號,其中A表為病人信息,J表為病人掛號記錄。查看兩個表的索引情況。
SQL> select b.table_name,b.index_name,a.column_name
2 from dba_ind_columns A,dba_indexes b
3 where a.index_owner=b.owner
4 and a.index_name=b.index_name
5 and b.owner='ZLHIS'
6 and b.table_name in ('病人掛號記錄','病人信息')
7 ;
TABLE_NAME INDEX_NAME COLUMN_NAME
------------------------------ ------------------------------ --------------------------------------------------------------------------------
病人掛號記錄 病人掛號記錄_IX_病人ID 病人ID
病人掛號記錄 病人掛號記錄_IX_操作員姓名 操作員姓名
病人掛號記錄 病人掛號記錄_IX_登記時間 登記時間
病人掛號記錄 病人掛號記錄_IX_執(zhí)行部門ID 執(zhí)行部門ID
病人掛號記錄 病人掛號記錄_IX_執(zhí)行時間 執(zhí)行時間
病人掛號記錄 病人掛號記錄_IX_執(zhí)行狀態(tài) 執(zhí)行狀態(tài)
病人掛號記錄 病人掛號記錄_PK ID
…………………………………………..
病人信息 病人信息_PK 病人ID
病人信息 病人信息_UQ_就診卡號 就診卡號
18 rows selected
SQL>
發(fā)現(xiàn)病人掛號記錄的門診號上都沒有建立索引,在表的“門診號”字段上建立索引。
SQL> create index 病人掛號記錄_IX_門診號 on 病人掛號記錄 (門診號)
tablespace ZL9PATIENT;
index created
SQL>
改造原來的(select distinct (select 編碼 from 部門表 where id=執(zhí)行部門id) as 科室號,門診號 from 病人掛號記錄 )語句,將子查詢的方式改為直接表連接。
改造后的SQL語句如下:
create or replace view patientinfo as
Select to_char(A.門診號) as 門診號,to_char(A.住院號) as 住院號,A.姓名,
Decode(A.性別,'男','M','女','F','U') AS 性別,
A.年齡,'Y' AS 年齡單位,'0'||C.編碼 AS 科室號,C.名稱 AS 科室,
to_char(B.出院病床) AS 床號,D.編號 AS 醫(yī)生號,E.描述信息 AS 入院診斷,
'非基本醫(yī)保' AS 付款類型,decode(a.當(dāng)前科室id,null,'O','I') AS 病人類別
From 病人信息 A,病案主頁 B,部門表 C,人員表 D,診斷情況 E
Where A.病人ID=B.病人ID(+) And A.住院次數(shù)=B.主頁ID(+)
And B.出院科室ID=C.ID(+) And B.住院醫(yī)師=D.姓名(+)
And B.病人ID=E.病人ID(+) And B.主頁ID=E.主頁ID(+) And
E.診斷類型(+)=1 and a.當(dāng)前科室id is not null
union
Select distinct
to_char(A.門診號) as 門診號,to_char(A.住院號) as 住院號,A.姓名,Decode(A.性別,'男','M','女','F','U') AS 性別,
A.年齡,'Y' AS 年齡單位,'0'||s.編碼 AS 科室號,C.名稱 AS 科室
,to_char(B.出院病床) AS 床號,D.編號 AS 醫(yī)生號,E.描述信息 AS 入院診斷,
'非基本醫(yī)保' AS 付款類型,decode(a.當(dāng)前科室id,null,'O','I') AS 病人類別
From 病人信息 A,病案主頁 B,部門表 C,人員表 D,診斷情況 E,
病人掛號記錄 J,部門表 S
Where a.門診號=J.門診號(+)
and j.執(zhí)行部門id=s.id (+)
and A.病人ID=B.病人ID(+) And A.住院次數(shù)=B.主頁ID(+)
And B.出院科室ID=C.ID(+) And B.住院醫(yī)師=D.姓名(+) and a.門診號=j.門診號(+)
And B.病人ID=E.病人ID(+) And B.主頁ID=E.主頁ID(+) And E.診斷類型(+)=1 and a.當(dāng)前科室id is null
改造后的SQL語句性能有了很大的很高,提取一個病人的信息由原來的30秒鐘左右,提高到現(xiàn)在的5秒鐘左右。再次查看執(zhí)行計劃:
SELECT STATEMENT, GOAL = CHOOSE
VIEW Object owner=ZLHIS Object name=PATIENTINFO
SORT UNIQUE
UNION-ALL
……………….
TABLE ACCESS FULL Object owner=ZLHIS Object name=病人信息
……………….
TABLE ACCESS FULL Object owner=ZLHIS Object name=病人信息
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病案主頁
INDEX UNIQUE SCAN Object owner=ZLHIS Object name=病案主頁_PK
……………………
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=部門表
INDEX UNIQUE SCAN Object owner=ZLHIS Object name=部門表_PK
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病人掛號記錄
INDEX RANGE SCAN Object owner=ZLHIS Object name=病人掛號記錄_IX_門診號
發(fā)現(xiàn)病人費用記錄已經(jīng)使用索引,但病人信息表還是全表掃描。下一步就是消除病人信息的全表掃描。
通過住院號進行查詢,應(yīng)該是可以用到建立在住院號上的索引,但這里沒有用到,原因是視圖中對住院號使用了to_char()函數(shù),即:to_char(A.住院號) as 住院號。經(jīng)咨詢醫(yī)院和公司的其他人員,必須使用to_char進行轉(zhuǎn)換,否則LIS的接口程序不能提取病人的信息。要徹底解決此問題,有兩種方式,1、使用基本函數(shù)的索引。2、通知三晶公司修改LIS結(jié)口。但第二種可能性很小。于是決定采用基于函數(shù)的索引。
1、修改init.ora參數(shù)
query_rewrite_enabled = TRUE
query_rewrite_integrity = TRUSTED
2、創(chuàng)建基于函數(shù)的索引
create index 病人信息_IX_char住院號 on 病人信息(to_char(住院號)) tablespace zl9patient
3、由于只有在CBO 下才能使用基于函數(shù)的索引,對”病人掛號記錄”等作分析。
analyze table 病人掛號記錄 compute statistics for all indexed columns for all indexes
4、改造SQL語句,加入提示字(hints:/*+frist_rows*/強制使用CBO。
調(diào)整后的SQL語句執(zhí)行計劃:
SELECT STATEMENT, GOAL = FIRST_ROWS Cost=87 Cardinality=74 Bytes=12876
VIEW Object owner=ZLHIS Object name=PATIENTINFO Cost=87 Cardinality=74 Bytes=12876
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病人信息 Cost=2 Cardinality=3 Bytes=129
INDEX RANGE SCAN Object owner=ZLHIS Object name=病人信息_IX_CHAR住院號 Cost=1 Cardinality=3
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病案主頁 Cost=1 ……………..
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病人信息 Cost=2 Cardinality=9 Bytes=387
INDEX RANGE SCAN Object owner=ZLHIS Object name=病人信息_IX_CHAR住院號 Cost=1 Cardinality=9
……………………..
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=病人掛號記錄 Cost=3 Cardinality=487793 Bytes=4390137
INDEX RANGE SCAN Object owner=ZLHIS Object name=病人掛號記錄_IX_門診號 Cost=2 Cardinality=487793
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=部門表 Cost=1 Cardinality=164 Bytes=3280
INDEX UNIQUE SCAN Object owner=ZLHIS Object name=部門表_PK Cardinality=164
TABLE ACCESS BY INDEX ROWID Object owner=ZLHIS Object name=人員表 Cost=1 Cardinality=491 Bytes=8347
INDEX UNIQUE SCAN Object owner=ZLHIS Object name=人員表_UQ_姓名 Cardinality=491
經(jīng)過調(diào)整,LIS系統(tǒng)提取病人信息的語句,速度得到了很大的提高,大概在1秒左右。此至問題解決。
|