原貼地址:http://www.itpub.net/forum.php?mod=viewthread&tid=1702729
在項目中使用oracle數據庫,有着兩個既是難點也是重點的兩條要求,就是數據的準確性以及數據庫的性能優化。所以在保證數據一致準確的前提下對database的優化就成爲了oracle數據庫的一個永恆的主題。
資深的Oracle數據庫DBA通常會要求提出性能問題的人對數據庫做一個statspack,貼出數據庫配置,還有的DBA要求抓出執行最慢的語句的執行計劃來進行sql分析。這些都是常用的優化手段。但是根據這些手段只能分析出哪裏出現了問題,或者哪條sql出現性能故障,卻依然手足無措,其實Oracle SQL 調整是 Oracle 性能調優中最重要的領域之一,只要通過一些簡單的 SQL 調優規則就可以大幅度地提升 SQL 語句的性能,這是一點都不奇怪的。對於存在like關鍵字通配符的模糊查尋的sql 語句往往令developer感到頭疼,也許一條簡單的sql模糊查詢在千萬行級別的表中就會執行的很慢,大大的影響了系統的性能,下面針對like的sql語句優化有幾點小的建議。
例如:select * from employee where last_name like '%c%;
這樣的查詢語句在執行的過程中很慢,但是業務中需要有模糊查詢的要求,又不得不用like
1. 分析業務需求是否前後都必須是模糊查詢如果可以改寫成通配符在後面,原因是%XXX%這樣的字段使用不到該列的索引,根據CBO規則很可能走的是FULL ACCESS TABLE,而XXX%這樣的字段卻可以走索引,會加快執行速度。如:select * from employee where last_name like 'c%'
2. 如果業務需求不能前置確定的變量XXX,卻可以寫成%XXX,雖然這樣依然不能夠走索引,但是可以通過翻轉函數索引來達到優化的目的。如:select * from employee where reverse(last_name) like reverse('%c'); 當然,在這裏值得注意的是這個反轉索引不是真正意義上的反轉索引,只不過是一個反轉函數的使用,真正意義上的反轉索引是主要防止熱塊的出現而將索引數據防止在不同的索引數據塊中,也就是在索引中使本來連續的值不連續的分佈,可以說在頻繁的插入的情況下可以減小數據塊競爭,但缺點也十分明顯,就是在範圍
scan的時候就用不上了。
3. 如果業務中必須使用類似%XXX%類似的sql查詢,也有幾個方案可供選擇。
(1)可以採用hint的強制索引,但是優化成功率比較低,根據CBO的優化器計算出的方式可能更加符合性能,但是也不失一種方法所以總結在這裏。如:Select /*+INDEX (employee pk_last_name)*/ * from employee where last_name like 'c%' ;
(2)可以採用instr或其他取得XXX字符或者字符位置的函數來代替like的使用達到同樣的業務需求。如:select count(*) from employee where instr (last_name,'c') > 0;
(3)如果該表需要被多次訪問而且不經常對該表進行添加刪改等DML語句操作可以建立物化視圖增加查詢速度。
(4)如果該表有明顯可以分區的字段,比如日期,年份,賬期等有關時間的字段,可以建立分區表,考慮從表結構的更改入手調整性能,這種優化多見於數據倉庫。
(5)利用並行sql技術,充分利用多核CPU性能來完成sql的查詢。如:SELECT /*+PARALLEL(employee,2)*/ * FROM employee WHERE last_name LIKE '%c%' ;
(6)可以採用全文索引技術來實現該字段的全文索引,但是數據精確度可能會有所損失。
(7)最後一個不是方法的方法就是把該字段數據使用其他的技術來實現,而不是依賴於oracle數據庫,如果數據比較多的話可以使用基於java的lucene全文搜索引擎技術來實現,但對於架構來講會增加很大的困難以及增加結構的複雜性,強烈建議不適用這種方法。
原貼地址:http://woodbow.net/91.html
oracle中like優化的一種方法
作者:
woodbow日期: 2012 年 12 月 5 日
在oracle中對於like操作時,如果是前後都是模糊查詢的時候(類似於col01 like ‘%xxx%’)是沒有辦法用到索引的,這裏提供一種對於這種情況的優化思路,主要的思路是把大表變小,把查詢的實體表變窄,把需要的數據放到索引裏.
常規的寫法:
SQL> create
index t01_01
on t01(object_name); |
SQL> select
* from
t01 where object_name
like '%EMP%' ; |
Plan hash value: 3295674804 |
| Id | Operation |
Name | Rows
| Bytes | Cost (%CPU)| Time
| |
| 0 | SELECT
STATEMENT | | 7091 | 775K| 522 (1)| 00:00:07 | |
|* 1 | TABLE
ACCESS FULL | T01 | 7091 | 775K| 522 (1)| 00:00:07 | |
Predicate Information (identified
by operation id): |
1 - filter( "OBJECT_NAME"
LIKE '%EMP%' ) |
14552 bytes sent via SQL*Net
to client |
608 bytes received via SQL*Net
from client |
13 SQL*Net roundtrips
to / from
client |
優化後的寫法:
2
from t01, ( select
rowid from
t01 where object_name
like '%EMP%' ) v01 |
3
where t01.rowid = v01.rowid; |
Plan hash value: 73453348 |
| Id | Operation |
Name |
Rows | Bytes | Cost (%CPU)|
Time | |
| 0 | SELECT
STATEMENT | | 7091 | 1017K| 7241 (1)| 00:01:27 | |
| 1 | NESTED LOOPS | | 7091 | 1017K| 7241 (1)| 00:01:27 | |
|* 2 | INDEX
FAST FULL
SCAN | T01_01 | 7091 | 242K| 147 (2)| 00:00:02 | |
| 3 | TABLE
ACCESS BY
USER ROWID| T01 | 1 | 112 | 1 (0)| 00:00:01 | |
Predicate Information (identified
by operation id): |
2 - filter( "OBJECT_NAME"
LIKE '%EMP%' ) |
18049 bytes sent via SQL*Net
to client |
608 bytes received via SQL*Net
from client |
13 SQL*Net roundtrips
to / from
client |
針對於上面的例子,IO從2335降到857,用這種方法在表越寬返回記錄越少時效果越好.