首先分享的觀念是:並非一看到 " FULL TABLE SCAN" 都代表是效能不好的,大家不能因此陷入理論上的迷思;
但也不能忽視不需要的 " FULL TABLE SCAN" 確實是效能低落的幫兇,
需更中性看待 "FULL TABLE SCAN"的存在。
資料庫存取資料的方法,可以大分類成讀入所有表格的「全表格掃描 FULL TABLE SCAN」,和使用INDEX索引只讀入必要資料的「INDEX SCAN」。
所謂的「全表格掃描 FULL TABLE SCAN」是指讀入到HWM(High Water Mark)為止的所有區塊,再將所包含的全部資料以指定的條件一件一件參照後取得必要資料的方法。所以全表格掃描是資料存取的基本方法,存取未產生INDEX的表格一定會做全表格掃描。另外,全表格掃描是將Oracle 資料區塊從表格前頭全部讀入,所以是可以一次讀入數個區塊的多重區塊。
索引掃描是以檢索速度的提升為目的所隨意產生的物件。索引掃描是透過讀入索引取得必要資料的ROWID,接下來使用這個ROWID來存取必要的表格資料方法。如果WHERE句所指定的情況下可以使用索引掃描建立的索引列。
綜合以上觀念可以了解,相對於全表格掃描,索引對象表格的資料數,按照增加的比例執行時間會變長。索引對象的表格,其件數不管是數十件或數百萬件,使用同樣索引取出一筆資料的速度,理論上幾乎相同。
所以當要你要存取的資料是整個表格或大部分的資料時,利用讀取多重區塊的全表格掃描會較有效率;相反的如果存取資料是屬於表格的某一筆或一小部分資料時,利用索引掃描才會較有效率。
為何索引並非總是最佳選擇
如果發現 Oracle 在有索引的情況下,沒有使用索引,這並不是 Oracle 的優化器出錯。在有些情況下, Oracle 確實會選擇
( Full Table Scan ) , 而非( Index Scan )。這些情況通常有︰
1. Table 未做 statistics, 或者 statistics 陳舊,導致 Oracle 判斷失誤。
2. 根據該Table 擁有的記錄數和data block數,實際上Full Table Scan要比Index Scan更快。
今天實作驗證的重點放在第2點,一般觀念上都認為索引比較快,比較難以理解什麼情況下Full Table Scan要比Index Scan快。為了講清楚這個問題,這裡先介紹一下 Oracle 在評估使用Index 的代價( cost )時兩個重要的數據︰ CF(Clustering factor) 和 FF(Filtering factor).
CF: 所謂 CF, 通俗地講,就是每讀入一個索引塊,要對應讀入多少個data block。
FF: 所謂 FF, 就是該 sql 語句所選擇的 result sets,佔總數據量的百分比。
大約的計算公式是︰ FF * (CF + 索引塊個數 ) ,由此估計出,一個查詢, 如果使用某個索引,會需要讀入的data block數。
需要讀入的data block越多,則 cost 越大, Oracle 也就越可能不選擇使用 index. (Full Table Scan需要讀入的data block數等於該表的實際data block數)其關鍵就是, CF 可能會比實際的data block數量大。
以下用實際的測試驗證上述論點 :
例如 , ci_custmast table 12/27止有 1036512筆資料, cod_cust_id的最小值是1000002 最大值是 2032791 ,看看以下 sql 語句︰
SELECT * FROM ci_custmast WHERE cod_cust_id > 1000002 (Full Table Scan)
SELECT * FROM ci_custmast WHERE cod_cust_id > 2032780 (Index Scan)
這兩句看似差不多的 sql 語句,對 Oracle 而言,卻有極大的差別。因為前者的 FF 是 100% , 而後者的 FF 可能只有 1% 。如果它 的 CF 大於實際的data block數,則 Oracle 可能會選擇完全不同的優化處理方式。
SELECT * FROM ci_custmast WHERE cod_cust_id > 1000002 使用 Full Table Scan
SELECT * FROM ci_custmast WHERE cod_cust_id > 2032780 使用 Index Scan
經由上述實作初步驗證 oracle 會根據優化處理選擇是 Full Table Scan 或 Index Scan
也初步證實 Full Table Scan 並非全然是影響DB效能的原凶
以下的另外範例是不同的SQL會造成不需要的 Full Table Scan 其 cost 會有很大差異
SELECT * FROM ci_custmast WHERE cod_cust_id >= 2000000
SELECT * FROM ci_custmast WHERE cod_cust_id LIKE '2%' Full Table Scan
還有很多在 where 子句中,加入任何形式的計算也會造成不用Index Scan改用不需要的 Full Table Scan
有興趣大家可以測試看看,這些容易忽視的小地方累積下來或許才是DB效能的殺手