數據庫優化的思路


1.針對經常查詢的條件,建立符合索引。

2.根據執行計划對sql進行優化。

看執行計划時,我們的關鍵不是看哪個操作先執行,哪個操作后執行,而是關鍵看表之間連接的順序(如得知哪個為驅動表,這需要從操作的順序進行判斷)、使用了何種類型的關聯及具體的存取路徑(如判斷是否利用了索引)

在從執行計划中判斷出哪個表為驅動表后,根據我們的知識判斷該表作為驅動表(就像上面判斷ABC表那樣)是否合適,如果不合適,對SQL語句進行更改,使優化器可以選擇正確的驅動表。

3.收集表統計信息

如果查看執行計划發現符合預期,但實際性能不行,可能需要重新收集表統計信息。

Oracle CBO需要系統定期分析統計表/索引。 只有這樣CBO才能使用正確的SQL訪問路徑,提高查詢效率。 因此在Instance Level的optimizer_mode = choose ,定期運行ANALYZE 或dbms_stats是非常重要的,尤其是當上次統計后,數據量已發生較大變化之后。
注意:統計操作是很耗資源的動作,要在系統Loading小的時候進行。

 

oracle數據庫的兩個優化器:CBO和RBO,

RBO: Rule-Based Optimization 基於規則的優化器

CBO: Cost-Based Optimization 基於代價的優化器

CBO是一種比RBO更加合理、可靠的優化器,它是從ORACLE 8中開始引入,但到ORACLE 9i 中才逐漸成熟,在ORACLE 10g中完全取代RBO

 

 

在看懂執行計划之前,先了解oracle的三個交表方式,http://keepwork.iteye.com/blog/1949520

一般來說是指:嵌套循環,hash鏈接,合並排序連接

 

1 嵌套循環連接

嵌套循環連接的內部處理的流程:

1) Oracle 優化器根據基於規則RBO或基於成本CBO的原則,選擇兩個表中的一個作為驅動表,並指定其為外部表。

2) Oracle 優化器再將另外一個表指定為內部表。

3) Oracle從外部表中讀取第一行,然后和內部表中的數據逐一進行對比,所有匹配的記錄放在結果集中。

4) Oracle讀取外部表中的第二行,再和內部表中的數據逐一進行對比,所有匹配的記錄添加到結果集中。

5) 重復上述步驟,直到外部表中的所有紀錄全部處理完。

6) 最后產生滿足要求的結果集。

通過查詢SQL語句的執行計划可以看出哪個表是外部表,哪個為內部表。

如 select a.user_name,b.dev_no

   from user_info a, dev_info b

   where a.user_id = b.user_id;

的執行計划:

SELECT STATEMENT ptimizer=CHOOSE

NESTED LOOPS

TABLE ACCESS (FULL) OF 'USER_INFO' --驅動表,外部表

TABLE ACCESS (FULL) OF 'DEV_INFO'  --內部表

使用嵌套循環連接是一種從結果集中提取第一批記錄最快速的方法。在驅動行源表(就是正在查找的記錄)較小、或者內部行源表已連接的列有惟一的索引或高度可選的非惟一索引時, 嵌套循環連接效果是比較理想的。嵌套循環連接比其他連接方法有優勢,它可以快速地從結果集中提取第一批記錄,而不用等待整個結果集完全確定下來。這樣,在理想情況下,終端用戶就可以通過查詢屏幕查看第一批記錄,而在同時讀取其他記錄。不管如何定義連接的條件或者模式,任何兩行記錄源可以使用嵌套循環連接,所以嵌套循環連接是非常靈活的。

然而,如果內部行源表(讀取的第二張表)已連接的列上不包含索引,或者索引不是高度可選時, 嵌套循環連接效率是很低的。如果驅動表的記錄非常龐大時,其他的連接方法可能更加有效。

 

 

排序合並連接(SORT MERGE JOIN)

適用於非等值連接的情況,例如,<, > ,>=,<=

不適用的連接條件有: <>    like

假設有查詢:select a.name, b.name from table_A a join table_B b on (a.id = b.id)

內部連接過程:

a) 生成 row source 1 需要的數據,按照連接操作關聯列(如示例中的a.id)對這些數據進行排序

b) 生成 row source 2 需要的數據,按照與 a) 中對應的連接操作關聯列(b.id)對數據進行排序

c) 兩邊已排序的行放在一起執行合並操作(對兩邊的數據集進行掃描並判斷是否連接)

延伸:

如果示例中的連接操作關聯列 a.id,b.id 之前就已經被排過序了的話,連接速度便可大大提高,因為排序是很費時間和資源的操作,尤其對於有大量數據的表。

故可以考慮在 a.id,b.id 上建立索引讓其能預先排好序。不過遺憾的是,由於返回的結果集中包括所有字段,所以通常的執行計划中,即使連接列存在索引,也不會進入到執行計划中,除非進行一些特定列處理(如僅僅只查詢有索引的列等)。

排序-合並連接的表無驅動順序,誰在前面都可以;

 

下面我們分步驟介紹Hash Join算法步驟:

 

   i.        Hash Join連接對象依然是兩個數據表,首先選擇出其中一個“小表”。這里的小表,就是參與連接操作的數據集合數據量小。對連接列字段的所有數據值,進行Hash函數操作。Hash函數是計算機科學中經常使用到的一種處理函數,利用Hash值的快速搜索算法已經被認為是成熟的檢索手段。Hash函數處理過的數據特征是“相同數據值的Hash函數值一定相同,不同數據值的Hash函數值可能相同”;

 ii.        經過Hash處理過的小表連接列,連同數據一起存放到Oracle PGA空間中。PGA中存在一塊空間為hash_area,專門存放此類數據。並且,依據不同的Hash函數值,進行划分Bucket操作。每個Bucket中包括所有相同hash函數值的小表數據。同時建立Hash鍵值對應位圖。

iii.        之后對進行Hash連接大表數據連接列依次讀取,並且將每個Hash值進行Bucket匹配,定位到適當的Bucket上(應用Hash檢索算法);

iv.        在定位到的Bucket中,進行小規模的精確匹配。因為此時的范圍已經縮小,進行匹配的成功率精確度高。同時,匹配操作是在內存中進行,速度較Merge Sort Join時要快很多;

 

哈希連接(HASH JOIN)是一種兩個表在做表連接時主要依靠哈希運算來得到連接結果集的表連接方法。

對於哈希連接的優缺點及適用場景如下:

a,哈希連接不一定會排序,或者說大多數情況下都不需要排序

b,哈希連接的驅動表所對應的連接列的選擇性盡可能好。

c,哈希只能用於CBO,而且只能用於等值連接的條件。(即使是哈希反連接,ORACLE實際上也是將其換成等值連接)。

c,哈希連接很適用小表和大表之間做連接且連接結果集的記錄數較多的情形,特別是小表的選擇性非常好的情況下,這個時候哈希連接的執行時間就可以近似看做和全表掃描個個大表的費用時間相當。

e,當兩個哈希連接的時候,如果在施加了目標SQL中指定的謂詞條件后得到的數據量較小的那個結果集所對應的HASH TABLE能夠完全被容納在內存中(PGA的工作區),此時的哈希連接的執行效率非常高。

 

oracle表之間的連接之哈希連接(Hash Join),其特點如下:

1,驅動表和被驅動表都是最多只被訪問一次。

2,哈希連接的表有驅動順序。

3,哈希表連接的表無需要排序,但是他在做連接之前做哈希運算的時候,會用到HASH_AREA_SIZE來創建哈希表。

4,哈希連接不適用於的連接條件是:不等於<>,大於>,小於<,小於等於<=,大於等於>=,like。

5,哈希連接索引列在表連接中無特殊要求,與單表情況無異。

 

 

排序 - - 合並連接(Sort Merge Join, SMJ):
  a) 對於非等值連接,這種連接方式的效率是比較高的。
  b) 如果在關聯的列上都有索引,效果更好。
  c) 對於將2個較大的row source做連接,該連接方法比NL連接要好一些。
  d) 但是如果sort merge返回的row source過大,則又會導致使用過多的rowid在表中查詢數據時,數據庫性能下降,因為過多的I/O.

  嵌套循環(Nested Loops, NL):
  a) 如果driving row source(外部表)比較小,並且在inner row source(內部表)上有唯一索引,或有高選擇性非唯一索引時,使用這種方法可以得到較好的效率。
  b) NESTED LOOPS有其它連接方法沒有的的一個優點是:可以先返回已經連接的行,而不必等待所有的連接操作處理完才返回數據,這可以實現快速的響應時間。

  哈希連接(Hash Join, HJ):
  a) 這種方法是在oracle7后來引入的,使用了比較先進的連接理論,一般來說,其效率應該好於其它2種連接,但是這種連接只能用在CBO優化器中,而且需要設置合適的hash_area_size參數,才能取得較好的性能。
  b) 在2個較大的row source之間連接時會取得相對較好的效率,在一個row source較小時則能取得更好的效率。
  c) 只能用於等值連接中

如何看懂執行計划可以參考:

http://blog.51cto.com/xiao1ang/1900950

 

 

執行計划是如何訪問數據的:

Full Table Scan (FTS)    --全表掃描
Index Lookup (unique & non-unique)    --索引掃描(唯一和非唯一)

索引掃描其實分為兩步:

Ⅰ:掃描索引得到對應的ROWID

Ⅱ:通過ROWID定位到具體的行讀取數據


Rowid    --物理行id  最快的方法

 

 

表訪問方式:

1.Full Table Scan (FTS) 全表掃描

In a FTS operation, the whole table is read up to the high water mark (HWM). The HWM marks the last block in the table that has ever had data written to it. If you have deleted all the rows then you will still read up to the HWM. Truncate resets the HWM back to the start of the table. FTS uses multiblock i/o to read the blocks from disk.   --全表掃描模式下會讀數據到表的高水位線(HWM即表示表曾經擴展的最后一個數據塊),讀取速度依賴於Oracle初始化參數db_block_multiblock_read_count(我覺得應該這樣翻譯:FTS掃描會使表使用上升到高水位(HWM),HWM標識了表最后寫入數據的塊,如果你用DELETE刪除了所有的數據表仍然處於高水位(HWM),只有用TRUNCATE才能使表回歸,FTS使用多IO從磁盤讀取數據塊).

Query Plan

------------------------------------

SELECT STATEMENT [CHOOSE] Cost=1

**INDEX UNIQUE SCAN EMP_I1   --如果索引里就找到了所要的數據,就不會再去訪問表

2.Index Lookup 索引掃描

There are 5 methods of index lookup:

index unique scan   --索引唯一掃描

針對唯一性索引(UNIQUE INDEX)的掃描,每次至多只返回一條記錄;

表中某字段存在 UNIQUE、PRIMARY KEY 約束時,Oracle常實現唯一性掃描;

 

 

index range scan   --索引局部掃描

使用一個索引存取多行數據;

發生索引范圍掃描的三種情況:

  • 在唯一索引列上使用了范圍操作符(如:>   <   <>   >=   <=   between)
  • 在組合索引上,只使用部分列進行查詢(查詢時必須包含前導列,否則會走全表掃描)
  • 對非唯一索引列上進行的任何查詢

 

 

index full scan   --索引全局掃描

進行全索引掃描時,查詢出的數據都必須從索引中可以直接得到(注意全索引掃描只有在CBO模式下才有效)

 

index fast full scan   --索引快速全局掃描,不帶order by情況下常發生

掃描索引中的所有的數據塊,與 INDEX FULL SCAN 類似,但是一個顯著的區別是它不對查詢出的數據進行排序(即數據不是以排序順序被返回)

 

index skip scan   --索引跳躍掃描,where條件列是非索引的前導列情況下常發生

Oracle 9i后提供,有時候復合索引的前導列(索引包含的第一列)沒有在查詢語句中出現,oralce也會使用該復合索引,這時候就使用的INDEX SKIP SCAN;

什么時候會觸發 INDEX SKIP SCAN 呢?

前提條件:表有一個復合索引,且在查詢時有除了前導列(索引中第一列)外的其他列作為條件,並且優化器模式為CBO時

當Oracle發現前導列的唯一值個數很少時,會將每個唯一值都作為常規掃描的入口,在此基礎上做一次查找,最后合並這些查詢;

例如:

假設表emp有ename(雇員名稱)、job(職位名)、sex(性別)三個字段,並且建立了如 create index idx_emp on emp (sex, ename, job) 的復合索引;

因為性別只有 '男' 和 '女' 兩個值,所以為了提高索引的利用率,Oracle可將這個復合索引拆成 ('男', ename, job),('女', ename, job) 這兩個復合索引;

當查詢 select * from emp where job = 'Programmer' 時,該查詢發出后:

Oracle先進入sex為'男'的入口,這時候使用到了 ('男', ename, job) 這條復合索引,查找 job = 'Programmer' 的條目;

再進入sex為'女'的入口,這時候使用到了 ('女', ename, job) 這條復合索引,查找 job = 'Programmer' 的條目;

最后合並查詢到的來自兩個入口的結果集。

 

3.Rowid 物理ID掃描

This is the quickest access method available.Oracle retrieves the specified block and extracts the rows it is interested in. --Rowid掃描是最快的訪問數據方式

 

 

七、運算符

1.sort    --排序,很消耗資源

There are a number of different operations that promote sorts:

(1)order by clauses (2)group by (3)sort merge join –-這三個會產生排序運算

 

2.filter    --過濾,如not in、min函數等容易產生

Has a number of different meanings, used to indicate partition elimination, may also indicate an actual filter step where one row source is filtering, another, functions such as min may introduce filter steps into query plans.

 

3.view    --視圖,大都由內聯視圖產生(可能深入到視圖基表)

When a view cannot be merged into the main query you will often see a projection view operation. This indicates that the 'view' will be selected from directly as opposed to being broken down into joins on the base tables. A number of constructs make a view non mergeable. Inline views are also non mergeable.

eg: SQL> explain plan for

select ename,tot from emp,(select empno,sum(empno) tot from big_emp group by empno) tmp

where emp.empno = tmp.empno;

Query Plan

------------------------

SELECT STATEMENT [CHOOSE]

**HASH JOIN

**TABLE ACCESS FULL EMP [ANALYZED]

**VIEW

****SORT GROUP BY

******INDEX FULL SCAN BE_IX

 

4.partition view     --分區視圖

Partition views are a legacy technology that were superceded by the partitioning option. This section of the article is provided as reference for such legacy systems.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM