夢幻之旅

          DEBUG - 天道酬勤

             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            671 隨筆 :: 6 文章 :: 256 評論 :: 0 Trackbacks
          <2011年9月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          公告

          本博客中未注原創的文章均為轉載,對轉載內容可能做了些修改和增加圖片注釋,如果侵犯了您的版權,或沒有注明原作者,請諒解

          常用鏈接

          留言簿(21)

          隨筆分類(644)

          隨筆檔案(669)

          文章檔案(6)

          最新隨筆

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          數據庫優化,應該首先從大的方面考慮:網絡服務器硬件配置、操作系統配置、Oracle服務器配置、數據結構組織、然后才是具體的調整。實際上網絡、硬件等往往無法決定更換,應用程序一般也無法修改,因此應該著重從數據庫配置、數據結構上來下手,首先讓數據庫有一個良好的配置,然后再考慮具體優化某些過慢的語句。我在給我的用戶系統進行優化的過程中,總結了一些基本的,簡單易行的辦法來優化數據庫的方法.  
            一.設置合適的SGA
            
            常常有人抱怨服務器硬件很好,但是Oracle就是很慢。很可能是內存分配不合理造成的。
            
            (1)假設內存有512M,這通常是小型應用。建議Oracle的SGA大約240M,其中:共享池(SHARED_POOL_SIZE)可以設置 60M到80M,根據實際的用戶數、查詢等來定。數據塊緩沖區可以大致分配120M-150M,8i下需要設置 DB_BLOCK_BUFFERS,DB_BLOCK_BUFFER*DB_BLOCK_SIZE等于數據塊緩沖區大小。9i 下的數據緩沖區可以用db_cache_size來直接分配。
            
            (2)假設內存有1G,Oracle 的SGA可以考慮分配500M:共享池分配100M到150M,數據緩沖區分配300M到400M。
            
            (3)內存2G,SGA可以考慮分配1.2G,共享池300M到500M,剩下的給數據塊緩沖區。
            
            (4)內存2G以上:共享池300M到500M就足夠啦,再多也沒有太大幫助;(Biti_rainy有專述)數據緩沖區是盡可能的大,但是一定要注意兩個問題:一是要給操作系統和其他應用留夠內存,二是對于32位的操作系統,Oracle的SGA有1.75G的限制。有的32位操作系統上可以突破這個限制,方法還請看Biti的大作吧。
            
            二.分析表和索引,更改優化模式
            
            Oracle默認優化模式是CHOOSE,在這種情況下,如果表沒有經過分析,經常導致查詢使用全表掃描,而不使用索引。這通常導致磁盤I/O太多,而導致查詢很慢。如果沒有使用執行計劃穩定性,則應該把表和索引都分析一下,這樣可能直接會使查詢速度大幅提升。分析表命令可以用ANALYZE TABLE 分析索引可以用ANALYZE INDEX命令。對于少于100萬的表,可以考慮分析整個表,對于很大的表,可以按百分比來分析,但是百分比不能過低,否則生成的統計信息可能不準確。可以通過DBA_TABLES的LAST_ANALYZED列來查看表是否經過分析或分析時間,索引可以通過DBA_INDEXES的 LAST_ANALYZED列。
            
            下面通過例子來說明分析前后的速度對比。(表CASE_GA_AJZLZ大約有35萬數據,有主鍵)首先在SQLPLUS中打開自動查詢執行計劃功能。(第一次要執行\RDBMS\ADMIN\utlxplan.sql來創建PLAN_TABLE這個表)
            SQL> SET AUTOTRACE ON
            SQL>SET TIMING ON
            
            通過SET AUTOTRACE ON 來查看語句的執行計劃,通過SET TIMING ON 來查看語句運行時間。
            SQL> select count(*) from CASE_GA_AJZLZ;
            COUNT(*)
            ----------
            346639
            已用時間: 00: 00: 21.38
            Execution Plan
            ----------------------------------------------------------
            0 SELECT STATEMENT Optimizer=CHOOSE
            1 0 SORT (AGGREGATE)
            2 1 TABLE ACCESS (FULL) OF 'CASE_GA_AJZLZ'
            ……………………
            請注意上面分析中的TABLE ACCESS(FULL),這說明該語句執行了全表掃描。而且查詢使用了21.38秒。這時表還沒有經過分析。下面我們來對該表進行分析:
            SQL> analyze table CASE_GA_AJZLZ compute statistics;
            
            表已分析。
            
            已用時間: 00: 05: 357.63
            
            然后再來查詢:
            SQL> select count(*) from CASE_GA_AJZLZ;
            COUNT(*)
            ----------
            346639
            
            已用時間: 00: 00: 00.71
            Execution Plan
            ----------------------------------------------------------
            0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=351 Card=1)
            1 0 SORT (AGGREGATE)
            2 1 INDEX (FAST FULL SCAN) OF 'PK_AJZLZ' (UNIQUE) (Cost=351
            Card=346351)
            …………………………
            
            請注意,這次時間僅僅用了0.71秒!這要歸功于INDEX(FAST FULL SCAN)。通過分析表,查詢使用了PK_AJZLZ索引,磁盤I/O大幅減少,速度也大幅提升!下面的實用語句可以用來生成分析某個用戶的所有表和索引,假設用戶是GAXZUSR:
            SQL> set pagesize 0
            SQL> spool d:\analyze_tables.sql;
            SQL> select 'analyze table '||owner||'.'||table_name||' compute statistics;' from dba_tables where owner='GAXZUSR';
            SQL> spool off
            SQL> spool spool d:\analyze_indexes.sql;
            SQL> select 'analyze index '||owner||'.'||index_name||' compute statistics;' from dba_indexes where owner='GAXZUSR';
            SQL> spool off
            SQL> @d:\analyze_tables.sql
            SQL> @d:\analyze_indexes.sql
            
            解釋:上面的語句生成了兩個sql文件,分別分析全部的GAXZUSR的表和索引。如果需要按照百分比來分析表,可以修改一下腳本。通過上面的步驟,我們就完成了對表和索引的分析,可以測試一下速度的改進啦。建議定期運行上面的語句,尤其是數據經過大量更新。
            
            當然,也可以通過dbms_stats來分析表和索引,更方便一些。但是我仍然習慣上面的方法,因為成功與否會直接提示出來。
            
            另外,我們可以將優化模式進行修改。optimizer_mode值可以是RULE、CHOOSE、FIRST_ROWS和ALL_ROWS。對于 OLTP系統,可以改成FIRST_ROWS,來要求查詢盡快返回結果。這樣即使不用分析,在一般情況下也可以提高查詢性能。但是表和索引經過分析后有助于找到最合適的執行計劃。
            
            三.設置cursor_sharing=FORCE 或SIMILAR
            
            這種方法是8i才開始有的,oracle805不支持。通過設置該參數,可以強制共享只有文字不同的語句解釋計劃。例如下面兩條語句可以共享:
            
            SQL> SELECT * FROM MYTABLE WHERE NAME='tom'
            
            SQL> SELECT * FROM MYTABLE WHERE NAME='turner'
            
            這個方法可以大幅降低緩沖區利用率低的問題,避免語句重新解釋。通過這個功能,可以很大程度上解決硬解析帶來的性能下降的問題。個人感覺可根據系統的實際情況,決定是否將該參數改成FORCE。該參數默認是exact。不過一定要注意,修改之前,必須先給ORACLE打補丁,否則改之后oracle會占用100%的CPU,無法使用。對于ORACLE9i,可以設置成SIMILAR,這個設置綜合了FORCE和EXACT的優點。不過請慎用這個功能,這個參數也可能帶來很大的負面影響!
            
            四.將常用的小表、索引釘在數據緩存KEEP池中
            
            內存上數據讀取速度遠遠比硬盤中讀取要快,據稱,內存中數據讀的速度是硬盤的14000倍!如果資源比較豐富,把常用的小的、而且經常進行全表掃描的表給釘內存中,當然是在好不過了。可以簡單的通過ALTER TABLE tablename CACHE來實現,在ORACLE8i之后可以使用ALTER TABLE table STORAGE(BUFFER_POOL KEEP)。一般來說,可以考慮把200數據塊之內的表放在keep池中,當然要根據內存大小等因素來定。關于如何查出那些表或索引符合條件,可以使用本文提供的access.sql和access_report.sql。這兩個腳本是著名的Oracle專家 Burleson寫的,你也可以在讀懂了情況下根據實際情況調整一下腳本。對于索引,可以通過ALTER INDEX indexname STORAGE(BUFFER_POOL KEEP)來釘在KEEP池中。
            
            將表定在KEEP池中需要做一些準備工作。對于 ORACLE9i 需要設置DB_KEEP_CACHE_SIZE,對于8i,需要設置buffer_pool_keep。在8i中,還要修改 db_block_lru_latches,該參數默認是1,無法使用buffer_pool_keep。該參數應該比2*3*CPU數量少,但是要大于 1,才能設置DB_KEEP_CACHE_BUFFER。buffer_pool_keep從db_block_buffers中分配,因此也要小于 db_block_buffers。設置好這些參數后,就可以把常用對象永久釘在內存里。
            
            五.設置optimizer_max_permutations
            
            對于多表連接查詢,如果采用基于成本優化(CBO),ORACLE會計算出很多種運行方案,從中選擇出最優方案。這個參數就是設置oracle究竟從多少種方案來選擇最優。如果設置太大,那么計算最優方案過程也是時間比較長的。Oracle805和8i默認是80000,8建議改成2000。對于9i,已經默認是2000了。
            
            六.調整排序參數
            
            (1) SORT_AREA_SIZE:默認的用來排序的SORT_AREA_SIZE大小是32K,通常顯得有點小,一般可以考慮設置成1M(1048576)。這個參數不能設置過大,因為每個連接都要分配同樣的排序內存。
            
            (2) SORT_MULTIBLOCK_READ_COUNT:增大這個參數可以提高臨時表空間排序性能,該參數默認是2,可以改成32來對比一下排序查詢時間變化。注意,這個參數的最大值與平臺有關系。
            
            七.調整其它幾個關鍵的性能參數
            
            很多人認為使用oracle數據庫,系統的默認參數就是最好的,其實不是這樣,很多參數都需要調整,而且調整前后性能大不一樣。
            
            (1) log_buffer
            
            日志緩沖區大小默認設置32k太小了,建議設置成512K或者1M。
            log_buffer=524288
            
            (2) optimizer_index_caching
            
            這個參數可以設置索引的緩沖度,范圍是0到100,默認是0,可以考慮設置成90
            
            (3) optimizer_index_cost_adj
            
            這個參數是一個百分比,表明索引掃描與全表掃描的代價范圍是1到1000。默認=100表名索引掃描與全表掃描代價一樣。將這個參數設小表名索引代價要小于全表掃描,這樣就使得使用CBO進行成本計算時更傾向于使用索引掃描。建議把這個參數設置成30到50。
            
            八.改變聯機日志文件大小(一般用于oracle805)
            
            Oracle805的聯機日志文件默認只有1M大小,這實在是太小了,通過查看數據庫的日志,很可能發現“checkpoint not complete”之類的錯誤提示。這會導致系統穩定性,同樣也降低了數據庫性能。建議修改成10M。修改方法是刪除一個組、添加一個組,直到3個組都換成新的大小。說明:這個操作需要實施人員具有較多的數據庫知識,如果不太了解,最好不要試驗。
            
            九.改變數據塊大小(一般用于oracle805)
            
            Oracle805默認的塊(DB_BLOCK_SIZE)大小是2K,太小了,因為塊小,所以請求同樣的數據量的時候,讀的次數就要增多,導致性能低下。當然如果服務器性能比較好,還是升級Oracle更好,如果服務器配置比較差,建議改成8K。但是數據塊不能直接修改,唯一的辦法就是將數據導出,重新創建數據庫,然后將數據導入。說明:這個操作需要實施人員具有較多的數據庫知識,如果不太了解,最好不要試驗。
            
            十.設置合適的表存儲參數
            
            對于有很多并發寫入用戶的系統來說,如果系統沒有經過調整,經常會有數據等待現象。這是因為9i之前的表設置的默認的自由隊列freelists為1,這樣就可能造成數據等待。通過查看v$waitstat,如果發現data block 或者free list類的count次數很大,則說明等待情況嚴重,需要增加freelists。這個參數在8i、9i中可以動態修改(需要打補丁,否則會有ORA- 10620: Operation not allowed on this segment)在ORACLE805中,只能通過重新創建表來修改。
            SQL> select * from v$waitstat;
            CLASS COUNT TIME
            ------------------ ---------- ----------
            data block 11922013 342456
            sort block 0 0
            save undo block 0 0
            segment header 1 0
            free list 0 0
            
            如果測算經常有10個并發的寫用戶,可以把表的freelists改成10。例如下面的腳本可以把GAXZUSR用戶的所有表重新設置FREELISTS的語句寫在D:\FREELISTS.SQL里:
            SQL> SET PAGESIZE 0
            SQL> SPOOL D:\FREELISTS.SQL
            SQL> SELECT 'ALTER TABLE '||TABLE_NAME||' STORAGE(FREELISTS 10);' FROM DBA_TABLES WHERE OWNER=’GAXZUSR’;
            SQL>SPOOL OFF
            
            檢查D:\FREELISTS.SQL,沒有錯誤后運行修改FREELISTS:
            SQL>@D:\FREELISTS.SQL
            
            十一.重新組織表結構
            
            (1) 按照主鍵重新排序。
            
            數據庫運行了一段時間后,可能會有很多數據,而這些數據又可能是經常按照某個字段來選取區段數據。如果我們能夠把主鍵按照順序重新來組織一下表,那么用主鍵進行的查詢就會明顯快很多,主要是因為經過排序后,相似的編號都放在同一個數據塊里,ORACLE在進行主鍵范圍查找的時候,就會大大減少物理塊度讀取數量。在對表和索引分析之后,可以通過DBA_INDEXES的CLUSTERING_FACTOR列來判斷表是否需要重新排序。如果該字段的值與表的 BLOCK數量差不多,則不需要重新排序,如果和表的行數差不多,則應該考慮重新組織一下了。下面的例子示意性說明怎樣對表CASE_GA_AJZLZ按照主鍵PK_AJZLZ進行重新排序:
            
            <1> 將表CASE_GA_AJZLZ的索引、外間約束引用等找出來備用。
            SET PAGESIZE 0
            SET LINESIZE 300
            SPOOL DISABLE_CONSTRAINTS.SQL
            SELECT 'ALTER TABLE '||TABLE_NAME||' DISABLE CONSTRAINT '||CONSTRAINT_NAME||';' FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R' AND R_CONSTRAINT_NAME='PK_AJZLZ';
            SPOOL OFF
            SPOOL CREATE_CONSTRAINTS.SQL
            SELECT 'ALTER TABLE '||TABLE_NAME||' ADD CONSTRAINT '||CONSTRAINT_NAME||' FOREIGN KEY(CASEID) REFERENCES CASE_GA_AJZLZ(CASEID);' FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R' AND R_CONSTRAINT_NAME='PK_AJZLZ';
            SPOOL OFF
            SPOOL CREATE_INDEX.SQL
            SELECT 'CREATE INDEX '||INDEX_NAME||' ON '||TABLE_NAME||'('||COLUMN_NAME||') TABLESPACE INDX ;' FROM USER_IND_COLUMNS WHERE TABLE_NAME='CASE_GA_AJZLZ' AND INDEX_NAME<>'PK_AJZLZ';
            
            <2> 創建新的表CASE_GA_AJZLZ_NEW:
            SQL> CREATE TABLE CASE_GA_AJZLZ_NEW AS SELECT
            /*+INDEX(CASE_GA_AJZLZ PK_AJZLZ) */ * FROM CASE_GA_AJZLZ ;
            
            注意,上面的注釋(紅顏色部分)表名按照PK_AJZLZ排序來重新組織表。
            
            <3>禁用CASE_GA_AJZLZ的外間約束,將表CASE_GA_AJZLZ TRUNCATE,然后刪除之
            
            <4> 將表CASE_GA_AJZLZ_NEW更名為CASE_GA_AJZLZ
            
            SQL> ALTER TABLE CASE_GA_AJZLZ_NEW RENAME TO CASE_GA_AJZLZ;
            
            <5>創建CASE_GA_AJZLZ的所有索引、主鍵約束等。
            
            SQL> ALTER TABLE CASE_GA_AJZLZ ADD CONSTRAINT PK_AJZLZ PRIMARY KEY(CASEID);
            SQL> @CREATE_INDEX.SQL
            SQL> @CREATE_CONSTRAINTS.SQL
            
            (2) 將BLOB字段存儲到單獨的表空間中。
            
            基本上每個業務系統都有很多BLOB字段,而且很可能占據了整個數據庫大小的大部分。默認情況下,BLOB字段會將4000個字節的指針與表的行存在一起,這直接會導致行遷移。而且BLOB字段會與表處于同一個表空間,這也對性能有不小的影響。從設計角度來說,BLOB字段都應該單獨存儲,遺憾的是我所遇到的很多系統都沒有單獨存儲BLOB字段。如果BLOB字段占據了很大的存儲,那么將BLOB字段單獨存儲后,帶來的整體性能收益可能會非常的大。 另外BLOB字段存儲子句中有一個DISABLE STORAGE IN ROW 屬性,在將BLOB字段單獨存放時,也應該實用該屬性,這樣可以有效避免行遷移。
          posted on 2011-09-21 20:52 HUIKK 閱讀(1197) 評論(0)  編輯  收藏 所屬分類: DB-DailyMmaintenance
          主站蜘蛛池模板: 翼城县| 阳江市| 斗六市| 偃师市| 泾源县| 伊通| 山阳县| 富蕴县| 恩施市| 陇南市| 溆浦县| 梁河县| 宣城市| 师宗县| 花莲市| 乌鲁木齐县| 阿坝| 巴里| 绵阳市| 内黄县| 湖南省| 玛纳斯县| 六枝特区| 确山县| 本溪市| 三明市| 花莲市| 濮阳县| 邢台县| 梁平县| 白城市| 简阳市| 招远市| 定襄县| 马山县| 田东县| 蛟河市| 德保县| 汉沽区| 岳西县| 革吉县|