分析評價Oracle數據庫性能主要有數據庫吞吐量、數據庫用戶響應時間兩項指標。數據庫用戶響應時間又可以分為系統服務時間和用戶等待時間兩項,即: 數據庫用戶響應時間=系統服務時間+用戶等待時間 因此,獲得滿意的用戶響應時間有兩個途徑:一是減少系統服務時間,即提高數據庫的吞吐量;二是減少用戶等待時間,即減少用戶訪問同一數據庫資源的沖突率。
數據庫性能優化包括如下幾個部分: 調整數據結構的設計 這一部分在開發信息系統之前完成,程序員需要考慮是否使用Oracle數據庫的分區功能,對於經常訪問的數據庫表是否需要建立索引等。 調整應用程序結構設計 這一部分也是在開發信息系統之前完成的。程序員在這一步需要考慮應用程序使用什么樣的體系結構,是使用傳統的Client/Server兩層體系結構,還是使用Browser/Web/Database的三層體系結構。不同的應用程序體系結構要求的數據庫資源是不同的。 調整數據庫SQL語句 應用程序的執行最終將歸結為數據庫中的SQL語句執行,因此SQL語句的執行效率最終決定了Oracle數據庫的性能。 Oracle公司推薦使用Oracle語句優化器(Oracle Optimizer)和行鎖管理器(Row-Level Manager)來調整優化SQL語句。 調整服務器內存分配 內存分配是在信息系統運行過程中優化配置的。數據庫管理員根據數據庫的運行狀況不僅可以調整數據庫系統全局區(SGA區)的數據緩沖區、日志緩沖區和共享池的大小,而且還可以 調整程序全局區(PGA區)的大小。 調整硬盤I/O 這一步是在信息系統開發之前完成的。數據庫管理員可以將組成同一個表空間的數據文件放在不同的硬盤上,做到硬盤之間I/O 負載均衡。 調整操作系統參數 例如:運行在Unix操作系統上的 Oracle數據庫,可以調整Unix數據緩沖區的大小、每個進程所能使用的內存大小等參數。 實際上,上述數據庫優化措施之間是相互聯系的。Oracle 數據庫性能惡化的表現基本上都是用戶響應時間比較長,需要用戶長時間的等待。而性能惡化的原因卻是多種多樣的,有時是多個因素共同造成了性能惡化的結果,這就需要數據庫管理員有比較全面的計算機知識,能夠敏感地察覺到影響數據庫性能的主要原因所在。另外,良好的數據庫管理工具對於優化數據庫性能也是很重要的。
Oracle數據庫常用的數據庫性能優化工具有:
Oracle數據庫在線數據字典 Oracle在線數據字典能夠反映出Oracle的動態運行情況,對於調整數據庫性能是很有幫助的。 操作系統工具 例如使用Unix操作系統的Vmstat、 Iostat等命令可以查看到系統級內存和硬盤I/O的使用情況,這些工具能夠幫助管理員弄清楚系統瓶頸出現在什么地方。 SQL語言跟蹤工具可以記錄SQL語句的執行情況,管理員可以使用虛擬表來調整實例,並使用SQL語句跟蹤文件調整應用程序性能。SQL語言跟蹤工具將結果輸出成一個操作系統的文件,管理員可以使用TKPROF工具查看這些文件。 Oracle Enterprise Manager(OEM) 這是一個圖形的用戶管理界面,用戶可以使用它方便地進行數據庫管理而不必記住復雜的Oracle數據庫管理的命令。 Explain Plan——SQL語言優化命令 使用這個命令可以幫助程序員寫出高效的SQL語言。 系統性能評估
信息系統的類型不同,需要關注的數據庫參數也是不同的。數據庫管理員需要根據自己的信息系統類型來着重考慮不同的數據庫參數。
在線事務處理信息系統(OLTP) 這種類型的信息系統一般需要有大量的Insert、Update操作,典型的系統包括民航機票發售系統、銀行儲蓄系統等。OLTP系統需要保證數據庫的並發性、可靠性和最終用戶的速度,這類系統使用的Oracle數據庫需主要考慮以下參數: 數據庫回滾段是否足夠? 是否需要建立Oracle數據庫索引、聚集、散列? 系統全局區(SGA)大小是否足夠? SQL語句是否高效? 數據倉庫系統(Data Warehousing) 這種信息系統的主要任務是從Oracle的海量數據中進行查詢,以得到數據之間的某些規律。數據庫管理員需要為這種類型的Oracle數據庫着重考慮下述參數: 是否采用B*索引或者Bitmap索引? 是否采用並行SQL查詢以提高查詢效率? 是否采用PL/SQL函數編寫存儲過程? 有必要的話,需要建立並行數據庫以提高數據庫的查詢效率。 參數的調整
CPU參數
CPU是服務器的一項重要資源,服務器良好的工作狀態表現為在工作高峰時CPU的使用率高於90%。如果空閑時間CPU使用率就在90%以上,說明服務器缺乏CPU資源;如果工作高峰時CPU使用率仍然很低,則說明服務器CPU 資源還比較充足。 使用操作命令可以看到CPU的使用情況,一般Unix操作系統的服務器,可以使用sar-u命令查看CPU的使用率;NT操作系統的服務器,可以使用NT的性能管理器來查看CPU的使用率。 數據庫管理員可以通過查看v$sysstat數據字典中的 “CPU used by this session”統計項得知Oracle數據庫使用的CPU時間;查看“OS User level CPU time”統計項得知操作系統用戶狀態下的CPU時間;查看“OS System call CPU time” 統計項得知操作系統系統狀態下的CPU時間,操作系統總的CPU時間就是用戶狀態和系統狀態時間之和。如果Oracle數據庫使用的CPU時間占操作系統總CPU時間的90%以上,就說明服務器CPU基本上被Oracle數據庫使用着,這是合理的,反之,則說明服務器CPU被其他程序占用過多,Oracle數據庫無法得到更多的CPU時間。 內存參數
----內存參數的調整主要是指Oracle數據庫的系統全局區(SGA)的調整。SGA主要由3部分構成:共享池、數據緩沖區、日志緩沖區。
----共享池由兩部分構成:共享SQL區和數據字典緩沖區。共享SQL區是存放用戶SQL命令的區域,數據字典緩沖區則存放數據庫運行的動態信息。
性能調整案例
一.通過top命令看CPU的利用率:
#top
以下是TOP的結果: last pid: 11225; load averages: 7.95, 6.63, 6.25 17:19:35 273 processes: 259 sleeping, 3 running, 5 zombie, 3 stopped, 3 on cpu CPU states: 10.0% idle, 75.0% user, 15.0% kernel, 0.0% iowait, 0.0% swap Memory: 8192M real, 4839M free, 2147M swap in use, 12G swap free
PID USERNAME THR PRI NICE SIZE RES STATE TIME CPU COMMAND 10929 oracle 1 59 0 1048M 1022M cpu/6 2:52 21.59% oracle 11224 oracle 1 59 0 1047M 1018M run 0:03 4.22% oracle 8800 oracle 1 59 0 1048M 1022M run 1:39 3.99% oracle 4354 oracle 1 59 0 1049M 1023M cpu/4 0:28 3.46% oracle 3537 oracle 1 59 0 1048M 1022M sleep 1:01 1.93% oracle 29499 oracle 1 59 0 1048M 1022M sleep 30.0H 1.84% oracle 11185 oracle 1 59 0 1047M 1020M sleep 0:01 0.74% oracle 11225 wacos 1 44 0 2832K 1928K cpu/0 0:00 0.65% top 9326 oracle 1 59 0 1047M 1020M sleep 0:58 0.50% oracle 410 root 14 59 0 7048K 6896K run 76.3H 0.42% picld 21363 oracle 1 59 0 1047M 1019M sleep 574:35 0.36% oracle 10782 oracle 11 59 0 1052M 1024M sleep 749:05 0.28% oracle 13415 oracle 1 59 0 1047M 1019M sleep 6:07 0.27% oracle 5679 oracle 11 59 0 1052M 1026M sleep 79:23 0.19% oracle 5477 oracle 258 59 0 1056M 1021M sleep 57:32 0.14% oracle
二.通過分析找出了消耗CPU最高進程對應的SQL語句:
SQL>set line 240
SQL>set verify off
SQL>column sid format 999
SQL>column pid format 999
SQL>column S_# format 999
SQL>column username format A9 heading "ORA User"
SQL>column program format a29
SQL>column SQL format a60
SQL>COLUMN OSname format a9 Heading "OS User"
SQL>SELECT P.pid pid,S.sid sid,P.spid spid,S.username username,
S.osuser osname,P.serial# S_#,P.terminal,P.program program,
P.background,S.status,RTRIM(SUBSTR(a.sql_text, 1, 80)) SQL
FROM v$process P, v$session S,v$sqlarea A WHERE P.addr = s.paddr AND S.sql_address = a.address (+) AND P.spid LIKE '%&1%';
Enter value for 1:10929
最終找到的SQL語句如下:
SELECT NVL(SUM(RURALCHARGE),0.00) AS Fee FROM LOCALUSAGE WHERE ServiceID=219987 and starttime >= to_date('2003/12/30 13:24:20','YYYY/MM/DD HH24:MI:SS');
三.查詢localusage表上的分區索引:
通過以下查詢得到localusage表上的分區索引有兩個:
SQL> select INDEX_NAME from user_part_indexes where TABLE_NAME='LOCALUSAGE';
INDEX_NAME ------------------------------ I_LOCALUSAGE_SID UI_LOCALUSAGE_ST_SEQ
通過執行計划分析出這條語句使用的是UI_LOCALUSAGE_ST_SEQ索引,沒有使用I_LOCALUSAGE_SID索引,我統計了一下時間,用UI_LOCALUSAGE_ST_SEQ這個索引的效率很差的,返回時間用了2分鍾36秒,而使用I_LOCALUSAGE_SID這個索引的話,返回時間為1秒多。而ORACLE缺省使用的是UI_LOCALUSAGE_ST_SEQ索引,因此占用了大量的CPU資源,導致CPU利用率下降。
以下是用autotrace的分析過程:
SQL>connect wacos/oss
SQL>set autotrace on
SQL> set timing on SQL> SELECT NVL(SUM(RURALCHARGE),0.00) AS Fee FROM LOCALUSAGE WHERE ServiceID=219987 and starttime >= to_date('2003/12/30 13:24:20','YYYY/MM/DD HH24:MI:SS');
FEE ---------- 107.25
Elapsed: 00:02:36.19 (返回時間2分36秒)
Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=1 Bytes=35) 1 0 SORT (AGGREGATE) 2 1 PARTITION RANGE (ALL) 3 2 TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'LOCALUSAGE' (C ost=10 Card=10035 Bytes=351225)
4 3 INDEX (RANGE SCAN) OF 'UI_LOCALUSAGE_ST_SEQ' (UNIQUE ) (Cost=2 Card=10035)
Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 11000821 consistent gets 349601 physical reads 0 redo size 292 bytes sent via SQL*Net to client 359 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 1 rows processed
用HINT強制ORACLE使用I_LOCALUSAGE_SID索引,然后查看執行計划:
SQL>connect wacos/oss
SQL>set autotrace on
SQL>set timing on
SQL> SELECT /*+ INDEX(LOCALUSAGE I_LOCALUSAGE_SID)*/ NVL(SUM(RURALCHARGE),0.00) AS Fee FROM LOCALUSAGE WHERE ServiceID=219987 and starttime >= to_date('2003/12/30 13:24:20','YYYY/MM/DD HH24:MI:SS');
FEE ---------- 107.25
Elapsed: 00:00:01.15 (返回時間1秒)
Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=15 Card=1 Bytes=35) 1 0 SORT (AGGREGATE) 2 1 PARTITION RANGE (ALL) 3 2 TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'LOCALUSAGE' (Cost=15 Card=10035 Bytes=351225)
4 3 INDEX (RANGE SCAN) OF 'I_LOCALUSAGE_SID' (NON-UNIQUE) (Cost=14 Card=10035)
Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 307 consistent gets 232 physical reads 0 redo size 292 bytes sent via SQL*Net to client 359 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
建議研發人員調整該語句,使這條語句缺省使用I_LOCALUSAGE_SID索引,或在語句中使用HINT來強制使用I_LOCALUSAGE_SID索引。
案例二
一. 引言 本文描述了在Linux 環境下對一個大型Web應用系統的Oracle數據庫進行 優化調整的過程.分為數據庫分析,數據庫調整,數據庫監控三部分.在每部分 中,對具體的操作都有比較詳細的分析說明. 二. 數據庫分析 2.1 系統概述 系統是一個針對特定客戶的大型web系統.存在幾個數據增長頻度很高的表, 並發事務不多. 2.2 詳細分析 系統的瓶頸目前體現在部分查詢的效率比較低,歸檔日志增幅過快兩方面. 隨着以后用戶數的增多,並發操作的效率也是一個潛在性的問題.因此 需要對數據庫服務器進行全面的分析, 包括環境配置,網絡配置, 數據庫 初始化參數配置, SQL語句的分析等部分. 2.2.1 環境配置 硬件配置: RAM: 4G Disk: 500G 軟件配置: OS: Redhat Linux Database: Oracle 817 Enterprise Edition
分析結果: 硬件配置較高,能滿足大型OLTP的應用. Oracle 在Unix/Linux 環境下比WINNT環境下性能更好. 2.2.2 數據庫初始化參數 對於每個Oracle數據庫實例,有一個對應的初始化參數配置文件 initsid.ora. 參數文件中某些參數對數據庫性能影響極大.下面列出的 是一些核心參數: DB_BLOCK_BUFFERS SHARED_POOL_SIZE LARGE_POOL_SIZE JAVA_POOL_SIZE LOG_BUFFER DB_FILE_MULTIBLOCK_READ_COUNT SORT_AREA_SIZE SHARED_POOL_RESERVED_SIZE 分析結果: 大部分參數的值都是數據庫建立時的缺省值,需要調整. 2.2.3 數據庫邏輯結構 根據OFA (Optimal Flexible Architecture) 的規則, 將數據庫對象按照對象類型及操作類型進行區分.也就是根據具體的業務特征規划表空間.
分析結果: 表空間組織不太合理,大部分數據建立在SYSTEM表空間 ; 表中的索引沒有專屬的表空間, 不利於管理; 用戶的配額管理和權限管理需要調整.
2.2.4 數據庫物理結構 數據庫的物理結構主要體現在磁盤分布的規划上,以確保磁盤I/O 對數據庫性能的影響最小化.
分析結果: 結合邏輯結構的分析,部分數據文件需要與其他數據文件分開存放,以確保I/O競爭最小化,包括: 業務表空間與索引表空間分離
回滾段空間與業務表空間分離
SYSTEM 表空間與其他表空間分離 同時還確定系統的總體目標是基於恢復的,對於產品數據庫, 要啟用歸檔重做日志模式, 對於測試數據庫,通過邏輯備份 或冷備份來備份數據. 2.2.5 表結構分析 分析結果: 表結構基本設計合理, 部分表的字段類型需要調整 ; 部分表沒有做過STATISTICS 或者 STATISTICS 比較陳舊, 出現了ORACLE 不使用索引,而使用全表掃描的情況 ; 調整FK的使用. 2.2.6 SQL語句分析 分析結果: 系統中沒有過於復雜的查詢, 但是有一個代價較高的查詢 ; 批量轉移數據可以有更好更塊的方式 ; 可以更多地利用數據庫的特性,將處理語句編寫成過程或函數, 在服務器上運行,減少網絡流量 ; 沒有充分使用綁定變量,共享池的使用效率不高 ; 短時間內的大量DML操作會導致 回滾段激增和重做日志切換頻 繁, 需要優化 . 2.2.7 網絡環境 分析結果: 可將數據庫移植到一台單獨的服務器上,以發揮更好的性能.
2.3 總結 從上面的分析得出結論: 數據庫的性能還沒有充分發揮,很多方面都可以通 過調整來提高性能. 三. 數據庫調整
原則: 根據數據分析中提到的部分逐個進行調整,調整后進行程序檢測. 3.1 參數調整 說明: 服務器內存4G, 考慮到服務器同時還運行其他任務, 從中分配 10%給SGA.可根據實際情況重新分配. 一般來說SGA的范圍 是物理內存的 20%-40% , 最大不能超過全部物理內存的50% . 操作: 計算方式: SGA=((db_block_buffers * block size)+ (shared_pool_size+large_pool_size+java_pool_size+ log_buffers) +1MB =0.1 * 4G=0.4G =410M 3.1.1 共享池 約為SGA的一半即200M 調整前: shared_pool_size=31457280(31.45M) 調整后: shared_pool_size=209715200(200M) 說明: 這個值不能太大, 可根據實際業務中SQL語句的復雜度來衡量. 如果業務中沒有過多的動態 SQL或者沒有復雜的SQL,可以減小 它, 否則會使SHARE POOL中碎片增多, 並且頻繁引發OS級別 的內存調度,嚴重影響系統的正常運行. 監控: 定期運行statspack 或utlstat包,根據獲得的監控數據來調整. 監控結果: 通過 statspack包產生的report文件的分析,共享池的值設置 偏大,可以調小. 3.1.2 緩沖區高速緩存塊 設置為SGA的一半即200M
緩沖區高速緩存容量=緩沖區高速緩存塊數 (DB_BLOCK_BUFFERS) * 每個塊的大小 (DB_BLOCK_SIZE) DB_BLOCK_SIZE=8K, 這個參數是安裝數據庫時的缺省設置.
調整前: db_block_buffers = 2048 調整后: db_block_buffers = 25600(200M /8K)
說明: 緩沖區高速緩存對性能影響極大,因為用戶進程所存取的所 有數據都是經過緩沖區高速緩存來存取.調整完畢后,定期 檢查它的命中率.如果命中率小於90%, 則需要增大這個參數. 3.1.3 日志緩沖區 (LogBuffer) 不進行調整,保持缺省的參數. 說明: 通過對alert 文件的監控發現,發現有不能分配重做日志文件 空間的信息,增加重做日志文件大小/日志緩沖區大小可以 解決這個問題.
3.1.4 大池 (Large_pool_size) 大存儲池的分配堆,它可被多線程服務器用作會話內存,用作並行 執行的消息緩沖區以及用作 RMAN 備份和恢復的磁盤 I/O緩沖區. 調整前: large_pool_size = 614400 調整后: large_pool_size = 1048576 (根據計算公式得出) 說明: 考慮到數據增長的頻度, 數據量達到一定程度時需要使用RAMN 進行備份恢復.因此可適當調整. 3.1.5 JAVA 池 不進行調整,保持缺省的參數. 3.1.6 DB_FILE_MULTIBLOCK_READ_COUNT 調整前: 8 (系統缺省) 調整后: 16(適用於中型系統,即事務量不是很大的OLTP系統) 說明: 這個參數對SQL查詢策略有影響.如果參數設置的比較大,有 可能造成Oracle 使用全表掃描而不使用索引.
3.1.7 排序緩沖區(SORT_AREA_SIZE) 調整前: 65536 (64K) 調整后: 1048576 (1M) 說明: Oracle 基於SORT_AREA_SIZE的設置值,為大型排序操作 分配內存,如果內存不夠用,則使用臨時段空間.因此應盡 可能將排序操作放在內存中進行.系統在數據交換的操作中 用到了不少排序, 因此將這個參數適當調大一些. 監控: 定期運行statspack,根據監控數據來調整.
3.1.8 SHARED_POOL_RESERVED_SIZE 默認值: SHARED_POOL_SIZE 值的 5% 調整后: 10485760(200M * 5%=10M) 說明: 共享池使用久了,會出現碎片,為了盡量少的執行刷新共 享池的操作,需要預留一定的內存空間.否則將加重CPU 重新調度的負擔,同時也影響其他進程. 3.2 數據庫邏輯結構調整 目標: 新建數據表空間USERDATA ; 整理 SYSTEM表空間, 只保留基本的數據字典 ; 轉移特定用戶, 將目前常用用戶的缺省表空間設置為數據表空間 ; 轉移索引到專用的索引表空間等. 3.2.1 新建表空間 USERDATA 考慮到UNIX 件系統 2G的限制(可能引發管理問題), 將每個數據 文件設置成2G ; 通過分析全庫邏輯備份文件的大小,初始大小設成6G .
3.2.2 特定用戶的轉移 現有系統中對表的訪問大多是使用數據庫內置的用戶scott, 從安全性和用戶管理方面考慮,有以下兩種方案: . 保留現有用戶, 調整配額策略和分配對應的權限 ; . 設置新用戶,重新分配權限 . 下面的改進采用方案1. STEP 1: 回收角色和權限 Revoke resource from scott STEP 2: 取消該用戶在SYSTEM表空間上的磁盤配額 Alter user scott quota 0 on system STEP 3: 將該用戶的缺省表空間改為 USERATA Alter user scott default tablespace USERDATA STEP 5: 調整磁盤配額, 重新授權 Alter user scott quota unlimited on USERDATA grant create procedure to scott grant create trigger to scott grant create type to scott
說明: 最好不要將系統權限 unlimited tablespace賦予普通用戶.同時, 數據庫的缺省用戶DBSNMP有這個權限,從安全性角度 考慮,需要回收這個權限.
3.2.3 數據轉移 有三種方案可以選擇: 1. 使用SQL語句轉移表 alter table TableName move tablespace TBSNAME
說明: 因為表上索引的物理存儲位置已經改變, 需要重建索引.
2. 使用基於對象的導出/導入策略.
目的: 將scott 用戶所擁有的全部對象(表, 索引,約束, 觸發器等) 到 USERDATA表空間 STEP 1: 導出
exp userid=system/manager parfile=exp_scott.par file=exp_scott.dmp log=exp_scott.log owner="(scott)" 參數文件 exp_scott.par: BUFFER=4096000 COMPRESS=Y GRANTS=Y INDEXES=N ROWS=Y CONSTRAINTS=N DIRECT=Y 說明: 如果不導出索引,約束,並且使用直接路徑,加大緩存能 大大提高導出的速度.使用COMPRESS=Y 選項壓縮原 表空間; 導出過程前事先估算文件的大小, 如果超過2G,作分割. STEP 2: 導入 imp userid=system/manager parfile=imp_scott.par file=exp_scott.dmp log=imp_scott.log fromuser="(scott)" touser="(scott)" 參數文件: BUFFER=4096000 COMMIT=Y GRANTS=Y INDEXES=N IGNORE=Y ROWS=Y 說明: 在數據量不是很大時,導入的速度還可以接受,但是隨着數據 量的增加,導入的速度就會越來越慢.這時可以考慮使用RMAN或者使用冷/熱備份方式來備份數據. 3. 使用CTAS語句 CREATE TABLE TableName TABLESPACE USERDATA As select * from …
說明: 在歸檔重做日志模式下,使用NOLOGGING參數可以大幅 減小重做日志. 數據轉移完畢后,刪除舊表,新建索引. 3.2.4 索引的轉移 目的: 分配索引專屬的表空間,可以減小磁盤 I/O競爭, 使得數據庫邏輯 結構更清晰. 實現: 通過 CREATE INDEX 語句中的TABLESPACE 參數可以指定索 引使用的表空間 ; 創建表的主鍵時會同時創建唯一性索引,使用 using index tablespace 子句指定表空間.
例如: Create index IX_T1_SepID ON T1(SepID) tablespace INDX storage (initial 40K next 40K pctincrease 1) Alter table T1 add constraint PK_T1 primary key (ID1,ID2) using index tablespace INDX storage (initial 40K next 40K pctincrease 1)
3.2.5 整理SYSTEM 表空間 目的: SYSTEM 表空間只放置數據字典 實現: 將數據和索引轉移后,清理臨時表, 最后合並自由盤區. alter tablespace SYSTEM default storage(pctincrease 1) alter tablespace SYSTEM coalesce 3.3 Performance Tuning 3.3.1 Execution Plan 當SQL語句出現性能瓶頸時,首先檢查查詢計划.如果查詢計划 不是最優計划時,從這些方面逐個檢查調整. . 表或索引是否做過統計 . 上次統計的時間 . 從上次統計到現在, 活躍表的增長頻度 . 是否使用了索引 . 索引訪問是不是最佳訪問策略 先對兩個活躍表 T1, T2作全表統計 ANALYZE TABLE T1 COMPUTE STATISTICS ANALYZE TABLE T2 COMPUTE STATISTICS 對其他表有選擇性地作索引統計 ANALYZE TABLE T3 COMPUTE STATISTICS FOR ALL INDEXES 對指定的列按照指定的SIZE進行統計: ANALYZE TABLE T4 COMPUTE STATISTICS FOR COLUMNS COLX SIZE XXX 執行完畢后,檢查 USER_INDEXES, USER_TAB_COLUMNS 視圖 獲得相關信息. 然后作一些簡單的測試SQL, 表T1上 col1+col2 為主關鍵字, col3 列有非唯一索引. Select count(*) from T1 Select col1,col2,col3 from T1 where col1=1 and col2 =… and T1 .SepID= : iLowVal ' || ' and ' || sColumn || ' <= : iHighVal ' || sOtherCon ; -- dbms_output.put_line(' The entire sql is:' || strSQL); Execute immediate strSQL into iCountRet using iLowVal, iHighVal; -- Test --dbms_output.put_line(' Result: count is ' || icountRet); EXCEPTION WHEN OTHERS THEN iCountRet := -1; End; / 2. 索引使用分析: 上面存儲過程中有一個參數sOtherCon, 供用戶輸入其他的查 詢條件,假設輸入條件是 col5=0.
這條SQL語句的瓶頸主要出在對col5列的訪問上,因為該列 只有兩個值(1,0), 且沒有建索引.嘗試建一個復合索引(該列不 能單獨建位圖索引,位圖索引通常用於OLAP系統) create index ix_bind_T1 on T1(Sep_ID,col5) tablespace indx storage (initial 64K NEXT 64K PCTINCREASE 1); 再做一個統計: analyze table T1 compute statistics for columns Sep_ID Size 8; 最后重新執行查詢. 查詢計划: 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=6) 1 0 SORT (AGGREGATE) 2 1 INDEX (RANGE SCAN) OF 'IX_BIND_T1' (NON-UNIQUE) (Cost=2 Card=83672 Bytes=502032)
統計信息: 0 recursive calls 0 db block gets 392 consistent gets 391 physical reads 0 redo size 293 bytes sent via SQL*Net to client 421 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 1 rows processed 可以看出: 改進后的索引查詢效率比較高. 對於復雜的SQL,也按照這樣的步驟進行,對大查詢中的各個子查詢作查詢計划,找出其中的性能瓶頸.有一點非常重要:要從整個系統的角度來考慮索引,合理地設置和使用索引. 3.3.2.2 其他DML 系統中除了查詢語句,還有其他的DML語句,包括新增,修改和刪除.對於OLTP系統來說,優化這些操作對於改善系統的性能至關重要. 新增操作: 大量數據導入時,優化效率可以從這些方面入手:
對於批量導入操作, 考慮這些因素: 1) 優先選擇系統不繁忙的時間段進行 ; 2) 准備一個大的回滾段 ; 3) 禁用目標表上的索引, 約束等 ; 4) 對目標表的操作不創建重做日志 ; 5) 使用 Insert /*+ Append */ 直接導入方式; 如果運行在歸檔日志模式下,上面提到的第3,4點更加重要,因為導入期間會產生大量的歸檔重做日志,很有可能造成磁盤空間不足. 在操作完成后要馬上進行數據庫的備份. 使用IMP命令時,考慮這些因素: 1) 加大緩存; 2) 將目標表上的索引,約束暫時去掉, 導入完成后 再重新創建; 3) 批量提交. 刪除操作: 對於刪除操作,考慮這些因素: 1) 如果有可能,使用一個獨立的私有回滾段,大小是 最大數據量的1.5 –2 倍左右 ; 2) 批量提交 ; 3) 區分維護性的刪除操作和必需的刪除操作, 如果 是定期的維護數據,建一個JOB在數據庫不繁忙 的時候(夜間)自動完成; 如果是應用中必要的刪除 操作,要保證操作盡可能快速完成. 4) 估算要刪除的數據量大小,如果接近於全表, 可以 用SQL命令將表中的剩余數據導出,然后執行 Truncate table 命令將表清空,最后再將數據重新 導入.
四. 數據庫監控
4.1 STATSPACK 4.1.1 概述 STATSPACK 是Oracle 提供的性能監控工具之一,可以提供一段時間內數據庫運行的綜合指標,從中可以檢測出目前數據庫最大的性能瓶頸. 4.1.2 TOP WAIT EVENTS TOP WAIT EVENTS 是STATSPACK 產生的REPORT中核心的部分, 它列出了系統最多的等待事件.看下面一個例子: Event Waits Wait Time (CS) % Total Wt Time db file scattered read 36,159 694 43.38 db file sequential read 9,900 296 18.50 Log file parallel write 1,620 255 15.94 control file parallel write 1,069 198 12.38 Log buffer space 50 73 4.56 分析: db file scattered read: 過多的使用全表掃描而不是使用索引 ;
db file sequential read: 過多的使用了單塊讀操作; log file parallel write: 與磁盤I/O操作有關 ; control file parallel write: log buffer space: 需要調整log buffer 大小. 4.1.2.1 db file scattered read 1. 檢查SQL語句(將snaplevel 設成5可以獲得SQL語句監控的相關信息),看SQL 語句是否使用了太多的物理讀.找到REPORT 中相關的部分: Statistic Total Per Second Per Trans physical reads 475,420 150.4 844.4 physical reads direct 132 0.0 0.2 physical writes 4,381 1.4 7.8 physical writes direct 132 0.0 0.2 physical writes non checkpoint 2,748 0.9 4.9
從上面的數據可以得出結論: 由於physical reads 的值比較大, 肯定存在沒有很好優化的SQL語句. 從REPORT中找到這些SQL語句: Physical Reads Executions Reads per Exec % Total Hash Value 73,537 4 18,384.3 99.6 1570063194 select count(*) from T1
41,098 6 6,849.7 8.6 1127261530 DELETE /*+NESTED_TABLE_SET_REFS+*/ FROM T2_TMP 40,432 6 6,738.7 8.5 3300685046 Delete from T2_TMP 40,363 6 6,727.2 8.5 2027128933 INSERT INTO T2 SELECT * FROM T2_TMP 40,306 6 6,717.7 8.5 2047057492
delete from T2 where (col1,col2) in (select col1,col2 from T2_TMP) … 分析: 可以看出,這是一個比較典型的數據導入批操作.先根據條件刪除重復的數據,然后將將數據從臨時表轉移到目標表,最后清空臨時表. Delete from 子句 會占用很多回滾段空間,產生大量重做日志, 改用Truncate table 語句效率會好很多. INSERT INTO 操作的代價也比較高,因為目標表上有索引,新增數據的時候需要維護索引,也會產生大量的重做日志.如果用直接導入方式,並且將索引置成不產生日志的模式,速度會很多,並且重做日志會大大減小. ALTER INDEX IX_DEST_TABLE NOLOGGING; INSERT /*+ APPEND */ INTO … ; 在ARCHIVELOG 模式下,如果一段時間操作不寫重做日志,那么這些操作就是不可恢復的,因此如果使用上面的操作,必須在操作完成后備份數據庫,另外應保證這些操作很快完成. 對於其他查詢語句, 分析其查詢計划,對SQL語句進行優化調整. 2. 檢查系統的I/O子系統是否存在瓶頸,查看REPORT中的關於TABLESPACE IO STATS 和 FILE IO STATS的相關信息. 下面是這兩部分的具體指標列: Read: Reads Av Av Av Reads/s Rd(ms) Blks/Rd Write: Writes Av Buffer Av Buf Writes/s Waits Wt (ms) 需要關注的信息主要是平均讀取時間,如果這個值偏大, 說明 磁盤本身的I/O性能存在瓶頸. 4.1.3 Buffer Hit Ratio & Shared pool 除了 TOP WAIT EVENTS 外,REPORT中關於 Buffer(Library) Hit 和 Shared pool的統計信息也應該關注. 一般情況下,如果庫緩沖區命中率長期低於90%,可考慮增加參數 DB_BLOCK_BUFFERS的大小,確保庫緩沖區命中率大於95% ; 對於 Shared pool,查看Parse 的有關指標(Total Parse, Hard Parse).尤其 是OLTP系統,如果系統中存在過多的Hard Parse, Parse Time就會大大 增加,同時對LATCH的需求也會增大,這樣將嚴重制約系統的可伸縮性.比較有效的方式是使用綁定變量.
Oracle數據庫的優化 摘要本文提出了一種優化Oracle數據庫的方法。Oracle中SQL語句的執行過程可分為解析(Parse)、執行(Execute)和提取結果(Fetch)三步,此方法就是通過對SQL語句在Oracle數據庫中優化執行的三個過程來提高Oracle數據庫的性能。 關鍵詞 數據庫 掃描 多表聯結 子查詢
1 如何優化Parse 1. 1 SQL語句的Parse處理步驟: 1) 計算語句值 2) 共享池中有無與此語句值相同的語句? 3) 共享池中有與此語句字符完全匹配的語句? 4) 准備要運行的SQL語句 5) 為新語句在共享池中創建空間 6) 將語句存放在共享池中 7) 修改共享池圖,標明語句的值和在共享池中的位置 8) 執行准備好的SQL語句 最理想的是,語句只執行1、2、3和8步來進行處理。不經過2、3步來測試被傳給Oracle的語句要使用1~8步進行處理。只經過1、2、3、8的SQL語句要比經過1~8步的語句更為有效。 1.2 在共享池中重用SQL語句 當SQL語句被傳遞給Oracle處理時,其秘訣是重復使用已經在共享池中的語句,而不是讓Oracle在接受語句時去准備新的語句。前面表明,如果Oracle接受了一個與共享池中的語句相一致的語句,就重用共享池中的語句。 Oracle提供在數據庫中存儲代碼的能力。當應用系統開始運行時,從數據庫中讀取代碼(可用PL/SQL語句編制)並像其它語句那樣傳遞到共享池中去處理。從數據庫中取出的代碼是編譯過的並駐留在共享池中。 可以利用數據庫中存儲的程序代碼設計應用系統,檢查所有的事務處理以及主要的通用的過程,研究現有的應用系統並把主要的處理程序轉換為數據庫中存儲的程序代碼。在Oracle中存儲代碼可以通過過程、程序包、函數、觸發器等來實現。 2 如何優化Execute和Fetch 2.1 避免無計划的全表掃描 全表掃描連續從表讀取所有數據,而不管數據是否與查詢有關。避免不必要的全表掃描有兩個充足理由:1)全表掃描沒有選擇性 2) 過全表掃描讀取的數據很快從SGA的緩沖區移走(如果正在掃描的表不是“高速存儲”的表) 在基於規則優化的情況下,如果下列任何條件在SGA語句出現,就要對一個表進行全表掃描。 1) 該表無索引 2) 對返回的行無任何限定條件(如無Where語句) 3) 對數據表與任何索引主列相對應的行無限定條件。例如,在City-State-Zip列上創建了三列復合索引,那么僅對State列有限定條件的查詢不能使用這個索引,因為State不是索引的主列。 4) 對索引主列的行有限定條件,但條件或者是NULL或者是不相等。例如,City列上存在索引,在所有下列情況下都不會使用索引。 Where city is null Where city is not null Where city!=’liaoning’ 5) 對索引主列的行有限定條件,但條件在表達式里使用。例如,如果在City列上索引,那么限定條件 Where City=’liaoning’ 可以使用索引。然而,如果限定條件是 Where UPPER(City)=’liaoning’ 那么不會使用City列上的索引,因為City列在UPPER函數里。如果將City列與文本字符串聯結在一起,也不會使用索引。例如,如果限定條件是 Where City||’x’ like ‘liaoning%’ 那么不會使用City列上的索引。 6) 對索引主列的行有限定條件,但條件使用Like操作以及值以‘%’開始或者值是一個賦值變量。例如,在所有下列情況下都不會使用索引: Where City like ‘%aonin%’ Where City like :City_Bind_Variable 如果表小、索引列無選擇性,基於開銷的優化器可能決定使用全表掃描。 2.2只使用選擇性索引 索引的選擇性是指索引列里不同值的數目與表中記錄數的比。如果表有1000個記錄,表索引列有950個不同值,那么這個索引的選擇性就是950/1000 或者0.95。最好的可能性選擇是1.0。依據非空值列的唯一索引,通常其選擇性為1.0。 如果使用基於開銷的最優化,優化器不應該使用選擇性不好的索引。 索引的選擇性是指索引列里不同值的數目與表中記錄數的比。如果表有1000個記錄,表索引列有950個不同值,那么這個索引的選擇性就是950/1000 或者0.95。最好的可能性選擇是1.0。依據非空值列的唯一索引,通常其選擇性為1.0。 索引的選擇性是指索引列里不同值的數目與表中記錄數的比。如果表有1000個記錄,表索引列有950個不同值,那么這個索引的選擇性就是950/1000 或者0.95。最好的可能性選擇是1.0。依據非空值列的唯一索引,通常其選擇性為1.0。 2.3管理多表聯結 Oracle提供了3個聯結操作:NESTED LOOPS、HASH JOIN和MERGE JOIN。MERGE JOIN是一組操作,在所有行被處理完之前,它不返任何記錄給下一操作。NESTED LOOPS和HASH是行操作,因此會很快將第一批記錄返回給下一個操作。 在每個聯結選項里,必須執行一些步驟以獲取最好的聯結性能。如果沒有適當地優化聯結操作,那么聯結所需的時間也許隨着表的增長而呈指數級地增長。 2.4管理包含視圖的SQL語句 如果查詢包含視圖,優化器有兩種執行查詢的方法:首先解決視圖然后執行查詢,或者把視圖文本集成到查詢里去。如果首先執行視圖,那么首先完成全部的結果集,然后用其余的查詢條件做過濾器。 首先解決視圖會導致查詢性能下降的問題,這取決於所涉及表的相對大小。如果視圖被集成到查詢里,那么查詢的條件也可以應用於視圖里,並且可以使用一個小一些的結果集。然而在一些情況下,也許可以通過視圖分離組操作提高查詢性能。 如果一個視圖包含集合的操作(如Group by、SUM、COUNT或者DISTINCT),那么視圖不能被集成到查詢里去。 不使用組或者沒有集合操作的視圖的SQL語法可以被集成到大的查詢里去。 2.5優化子查詢 當使用自查詢時,也許會碰到幾個獨特的問題。涉及子查詢的查詢潛在問題如下: ? 也許在執行完查詢的剩余部分前執行子查詢(與執行分組功能的視圖相似)。 ? 子查詢也許要求特定的提示,但這些提示不直接與調用該子查詢的查詢有關 ? 可以作為單個查詢執行的子查詢也許被代替寫成幾個不同的子查詢。 ? 也許在使用not in子句或者not exists子句時,不能在最有效的方式下進行子查詢的存在查詢。 1)當執行子查詢時 如果一個查詢包含子查詢,那么優化器有兩種完成查詢的方法:首先完成子查詢,然后完成查詢(“視圖的方法”),或者將子查詢集成到查詢里去(“聯結”的方法)。如果首先解決子查詢,那么整個子查詢的結果集將首先被計算,並且用查詢條件的剩余部分做過濾器。如果沒有使用子查詢去進行存在檢查,那么“聯結”方法將通常要比“視圖”方法完成得好。 如果一個子查詢包括集合操作,如group by,SUM或者DISTINCT,那么不能集成子查詢到查詢的其余部分里去。非集成的子查詢限制了提供給優化器的選項。 2)如何組合子查詢 一個查詢可以包含多個子查詢,使用的子查詢越多,集成或者重寫它們到大的聯結里就越困難。既然有多個子查詢使集成困難,就應該盡可能地組合多個子查詢。 3)怎樣進行存在檢查 有時子查詢不返回行(記錄),但可以進行數據正確性檢查。在相關表里的記錄或者存在或者不存在的邏輯檢查,稱為存在檢查。可以使用exists和not exists子句提高存在檢查的性能。 2.6管理對非常巨大的表的訪問 隨着表增長到比SGA的數據塊高速緩沖區存儲器的空間顯著大時,需要從另一個角度優化對這個表的查詢。 1)問題 當表和它的索引小的時候,在SGA里可以有高度的數據共享。多用戶讀表或索引范圍掃描可以反復使用同一個塊。隨着表的增長,表的索引也在增長。隨着表和它的索引增長到比SGA里提供的空間大時,范圍掃描需要的下一行將在SGA里找到的可能性變小,數據庫的命中率將減小。最后,每一個邏輯讀將要求一個單獨的物理讀。對使用非常大的表的優化方法着眼於特別的索引技術和有關索引的選擇。 2)管理數據接近 在訪問非常大的表期間,如果傾向於繼續使用索引,那么應該關注數據接近,即邏輯相關記錄的物理關系。為了使數據最大限度地接近,應該連續往表里插入記錄。記錄按通常在表的范圍掃描里使用的列排序。 3)避免沒有幫助的索引掃描 如果要對大表使用索引掃描,那么不能假定索引掃描將比全表掃描執行得更好。不緊跟表訪問的索引唯一掃描或范圍掃描執行得比較好,但緊跟通過RowID的表訪問的索引范圍掃描也許執行得差。隨着表增長到比數據塊高速緩沖存儲器大得多,最終,索引掃描和全表掃描間的平衡點打破。 4)創建充分索引的表 如果表中的數據相當穩定,充分索引一個表是很有用的。創建一個復合索引,它包括所有在查詢期間通常選擇的列。在查詢期間,查詢要求的所有數據可以通過索引訪問提供,不需要任何表訪問。 5)並行選項 可以把一個數據庫任務,比如Select語句,分為多個單元的工作,由多個Oracle進程同時執行。這種能夠允許數據庫的單個查詢活動由多個協調的進程透明地進行處理的能力,稱為並行查詢選項(PQO)。 並行選項調用多個進程來利用空閑的系統資源,以減少完成任務所需要的時間。並行選項並不減少處理過程所要求的資源數量,而是把處理的任務分散給多個 CPU。為了從並行選項中得到最大的好處,應該使CPU和磁盤上的I/O不要滿負荷使用。因為並行的目的是使更多的CPU和磁盤同時參與處理數據庫的命令,一個缺乏CPU和I/O資源的服務程序是不能從並行選項中得到好處的。 2.7使用UNION ALL而不是UNION 最常用的集操作是UNION操作,UNION操作使多個記錄集聯結成為單個集。UNION操作的數學定義是返回記錄的單個集並且沒有重復的行,所以在合並結果集里,Oracle只返回不同的記錄。 當UNION操作用作SQL語句的一部分時,唯一性要求強迫Oracle移走重復的記錄。Oracle的移走重復記錄的功能是SORT UNIQUE操作,它與使用DISTINCT子句時執行的操作類似。 UNION ALL操作允許重復。UNION ALL不要求SORT UNIQUE操作,從而節省了開銷。UNION ALL是一個行操作,所以當其變為有效就返回給用戶。而UNION包括SORT UNIQUE集操作,在全部記錄的集的排序結束前,不返回任何記錄給用戶。 當UNION ALL操作產生巨大的結果集時,不需要任何排序便返回記錄給應用的事實意味着第一行檢索的響應時間更快,並且在許多情況下,可以不用臨時段完成操作。 在有些情況下,UNION ALL和UNION不返回同樣的結果。如果在應用環境中,結果集並不包含任何重復的記錄,則可以把UNION轉換成UNION ALL。 2.8避免在SQL里使用PL/SQL功能調用 對於增加PL/SQL的使用,許多用戶試圖利PL/SQL功能的優勢產生可重復使用的代碼。其中一個強迫重復使用PL/SQL功能的方法是在SQL語句里使用。例如,可以創建一個將國際貨幣轉換為US$的函數。這個函數稱為US$。示例如下: select transaction_type, US$ (amount, currency) from international_transaction where US$ (amount, currency) > 1000; 執行前面的SQL語句沒有所期望的那樣好。在測試時,它的性能大約比下面得出相同結果的SQL語句慢大約幾十倍。 select transaction_type, amount*exchange_rate US$ from exchange_rate er, international_transaction it where er.currency = it.currency and amount*exchange_rate > 1000; 響應時間不同的原因是混合PL/SQL和SQL時,Oracle使用的機制不同。在SQL查詢里嵌入PL/SQL功能時,在執行時,Oracle將調用分成兩部分:用帶有賦值變量的SQL語句代替功能調用以及對每一個函數調用的無名PL/SQL塊。 select transaction_type, :a1 from international_transaction where :a1 > 1000 和 BEGIN :a1 := US$ (:amount, :currency); END 對在international_transaction表里的每一行,將執行在前面示例里顯示的無名塊兩次。無名塊調用導致查詢響應時間的劇增。應該避免在SQL語句里使用PL/SQL功能調用。