1. 程式人生 > >oracle 全表掃描和索引掃描

oracle 全表掃描和索引掃描

1) 全表掃描(Full Table Scans, FTS)
        為實現全表掃描,Oracle讀取表中所有的行,並檢查每一行是否滿足語句的WHERE限制條件。Oracle順序地讀取分配給表的每個資料塊,直到讀到表的最高水線處(high water mark, HWM,標識表的最後一個數據塊)。一個多塊讀操作可以使一次I/O能讀取多塊資料塊(db_block_multiblock_read_count引數設定),而不是隻讀取一個數據塊,這極大的減少了I/O總次數,提高了系統的吞吐量,所以利用多塊讀的方法可以十分高效地實現全表掃描,而且只有在全表掃描的情況下才能使用多塊讀操作。在這種訪問模式下,每個資料塊只被讀一次。由於HWM標識最後一塊被讀入的資料,而delete操作不影響HWM值,所以一個表的所有資料被delete後,其全表掃描的時間不會有改善,一般我們需要使用truncate命令來使HWM值歸為0。幸運的是oracle 10G後,可以人工收縮HWM的值。

由FTS模式讀入的資料被放到快取記憶體的Least Recently Used (LRU)列表的尾部,這樣可以使其快速交換出記憶體,從而不使記憶體重要的資料被交換出記憶體。

使用FTS的前提條件:在較大的表上不建議使用全表掃描,除非取出資料的比較多,超過總量的5% -- 10%,或你想使用並行查詢功能時

SQL> set autotrace traceonly
SQL> select * from t00_cust_no_mapping_his;
已選擇46116行。
執行計劃
----------------------------------------------------------
Plan hash value: 973990892
---------------------------------------------------------------------------------------------
| Id  | Operation         | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                         | 46116 |  5764K|   225   (1)| 00:00:03 |
|   1 |  TABLE ACCESS FUL
L| T00_CUST_NO_MAPPING_HIS | 46116 | 5764K| 225 (1)| 00:00:03 | ---------------------------------------------------------------------------------------------

2) 通過ROWID的表存取(Table Access by ROWID或rowid lookup)

        行的ROWID指出了該行所在的資料檔案、資料塊以及行在該塊中的位置,所以通過ROWID來存取資料可以快速定位到目標資料上,是Oracle存取單行資料的最快方法。為了通過ROWID存取表,Oracle 首先要獲取被選擇行的ROWID,或者從語句的WHERE子句中得到,或者通過表的一個或多個索引的引掃描得到。Oracle然後以得到的ROWID為依據定位每個被選擇的行。

這種存取方法不會用到多塊讀操作,一次I/O只能讀取一個數據塊。我們會經常在執行計劃中看到該存取方法,如通過索引查詢資料。所以oracle中rowid掃描有兩層含義:一種是使用者在sql語句輸入的rowid的值直接去訪問對於的資料行記錄;另一種是先去訪問相關索引,然後根據訪問索引後得到的rowid再回表去訪問對應的資料行記錄

SQL> select * from temp where b='2';

執行計劃
----------------------------------------------------------
Plan hash value: 1733176997
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     2 |    40 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEMP  |     2 |    40 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_B |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("B"=2)
Note
-----
   - dynamic sampling used for this statement (level=2)

對於oracle堆表而言,我們可以通過oracle內建的rowid偽列得到對應行記錄所在的rowid值(rowid只是一個偽列),可以通過DBMS_ROWID包中相關方法檢視,可以將rowid翻譯成實際的實體地址值

案例:

 select employee_id, first_name ,rowid,dbms_rowid.rowid_relative_fno(rowid) ||'_'||dbms_rowid.rowid_block_number(rowid)||'_'||dbms_rowid.rowid_row_number(rowid) location from employees;


通過rowid直接訪問;

select employee_id, first_name from employees where rowid='AAAEATAAEAAAADNAAA';

例如:employee_id=100 的行記錄對於的rowid是AAAEATAAEAAAADNAAA,對rowid翻譯後可以看出該行記錄儲存的實際實體地址是:位於4號檔案的第205個數據塊的第0行記錄(資料塊裡資料行記錄的記錄號是從0開始)

3)索引掃描(Index Scan或index lookup)
        我們先通過index查詢到資料對應的rowid值(對於非唯一索引可能返回多個rowid值),然後根據rowid直接從表中得到具體的資料,這種查詢方式稱為索引掃描或索引查詢(index lookup)。一個rowid唯一的表示一行資料,該行對應的資料塊是通過一次i/o得到的,在此情況下該次i/o只會讀取一個數據庫塊。

在索引中,除了儲存每個索引的值外,索引還儲存具有此值的行對應的ROWID值。索引掃描可以由2步組成:(1) 掃描索引得到對應的rowid值。 (2) 通過找到的rowid從表中讀出具體的資料。每步都是單獨的一次I/O,但是對於索引,由於經常使用,絕大多數都已經CACHE到記憶體中,所以第1步的I/O經常是邏輯I/O,即資料可以從記憶體中得到。但是對於第2步來說,如果表比較大,則其資料不可能全在記憶體中,所以其I/O很有可能是物理I/O,這是一個機械操作,相對邏輯I/O來說,是極其費時間的。所以如果多大表進行索引掃描,取出的資料如果大於總量的5% -- 10%,使用索引掃描會效率下降很多。

案例:

假設一張表含有10萬行資料--------100000行
我們要讀取其中20%(2萬)行資料----20000行
表中每行資料大小80位元組----------80bytes
資料庫中的資料塊大小8K----------8000bytes
所以有以下結果:
每個資料塊包含100行資料---------100行
這張表一共有1000個數據塊--------1000塊

通過索引讀取20000行資料 = 約20000個table access by rowid = 需要處理20000個塊來執行這個查詢

所以:如果按照索引讀取全部的資料的20%相當於將整張表平均讀取了20次!!這種情況下直接讀取整張表的效率會更高

由於索引掃描後要利用索引中的指標去逐一訪問記錄,假設每個記錄都使用索引訪問,則讀取磁碟的次數是查詢包含的記錄數T,而如果表掃描則讀取磁碟的次數是儲存記錄的塊數B,如果T>B 的話索引就沒有優勢了。對於大多數資料庫來說,這個比例是10%(oracle,postgresql等),即先對結果數量估算,如果小於這個比例用索引,大於的話即直接表掃描。