一.編寫初衷描述 |
【博客地址】http://www.cnblogs.com/grl214 sql
在應有系統開發初期,因爲數據庫數據較少,對於sql語句各類寫法的編寫體現不出sql的性能優劣,隨着數據的不斷增長,出現海量數據,劣質sql與優質sql在執行效率甚至存在百倍差距,可見sql優化的重要性數據庫
二.Sql語句性能優化 |
執行計劃是一條查詢語句在Oracle中執行過程或者訪問路徑的描述.緩存
1.執行計劃經常使用的列字段解釋性能優化
基數:返回的結果集行數oracle
字節:執行該步驟後返回的字節數函數
耗費(cust),CPU耗費:Oracle估計的該步驟的執行成本,用於說明SQL執行的代價,理論上越小越好.性能
根據縮進來判斷,縮進最多的最早執行(縮進相同時,最上面的最早執行)大數據
Oracle會讀取表中的全部行,並檢查是否知足where語句中條件;優化
使用建議:數據量太大的表不建議全表掃描ui
ROWID的解釋:oracle會自動加在表的每一行的最後一列僞列,表中並不會物理存儲ROWID的值,一旦一行數據插入後,則其對應的ROWID在該行的生命週期內是惟一的,即便發生行遷移,該行的ROWID值也不變。
在索引塊中即存儲每一個索引的鍵值,也存儲具備該鍵值所對的ROWID.
索引的掃描分兩步:首先是找到索引所對的ROWID,其次經過ROWID讀取改行數據
索引掃描又分五種:
(a).INDEX UNIQUE SCAN(索引惟一掃描):
針對惟一性索引(UNIQUE INDEX)的掃描,每次至多隻返回一條記錄,主要針對該字段爲主鍵或者惟一;
(b). INDEX RANGE SCAN(索引範圍掃描)
使用一個索引存取多行數據;
發生索引範圍掃描的三種狀況:
(c). INDEX FULL SCAN(索引全掃描)
(d). INDEX FAST FULL SCAN(索引快速掃描)
(e). INDEX SKIP SCAN(索引跳躍掃描):
Oracle 9i後提供,有時候複合索引的前導列(索引包含的第一列)沒有在查詢語句中出現,oralce也會使用該複合索引,這時候就使用的INDEX SKIP SCAN;
當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' 的條目;
最後合併查詢到的來自兩個入口的結果集。
1.在共享池中查找SQL語句
2.檢查語法
3.檢查語義和相關的權限
4.合併(MERGE)視圖定義和子查詢
5.肯定執行計劃
綁定(BIND):
1.在語句中查找綁定變量
2.賦值(或從新賦值
執行(EXECUTE):
1.應用執行計劃
2.執行必要的I/O和排序操做
提取(FETCH):
1.從查詢結果中返回記錄
2.必要時進行排序
3.使用ARRAY FETCH機制
共享遊標:好處
1.減小解析
2.動態內存調整
3.提升內存使用率
Oracle將執行過程當中的sql語句放在內存的共享池中,能夠被全部的數據庫用戶共享到,當執行一條sql語句時,若是它和以前的sql執行語句徹底相同時,oracle會快速獲取被解析的語句以及最好的執行路勁。
這塊系統屬於全局的區域,可是oracle只對簡單的表提供高速緩存,若是是多表的鏈接查詢,數據庫管理員必須在啓動參數文件中爲該區域設置合適的參數,增長共享的可能性。
1.執行語句必須與共享池語句徹底同樣,包括(大小寫,空格,換行等).
2.兩條語句所指的對象必須徹底相同。
3.兩個SQL語句綁定變量的名字必須相同。
例子:字符級的比較
SELECT * FROM UR_USER_INFO
Select * from ur_user_info
例子:相同的綁定變量名
select pay_fee,pay_method from bal_payment_info where pay_sn= : pay_sn;
select pay_fee,pay_method from bal_payment_info where pay_sn= : pay_no;
綁定變量不同,不能共享。
當一個Oracle實例接收一條sql後
一、Create a Cursor 建立遊標
二、Parse the Statement 分析語句
三、Describe Results of a Query 描述查詢的結果集
四、Define Output of a Query 定義查詢的輸出數據
五、Bind Any Variables 綁定變量
六、Parallelize the Statement 並行執行語句
七、Run the Statement 運行語句
八、Fetch Rows of a Query 取查詢出來的行
九、Close the Cursor 關閉遊標
例如:
select *from ur_user_info where contract_no = 32013484095139
下面這個語句每執行一次就須要在SHARE POOL 硬解析一
次,一百萬用戶就是一百萬次,消耗CPU和內存,若是業務
量大,極可能致使宕庫……
若是綁定變量,則只須要硬解析一次,重複調用便可
例如:
select *from ur_user_info where contract_no = 32013484095139
select *from ur_user_info where contract_no = 12013481213149
使用綁定變量
select *from ur_user_info where contract_no =:contract_no
a、不要使用數據庫級的變量綁定參數cursor_sharing來強
制綁定,不管其值爲 force 仍是similar
b、有些帶> < 的語句綁定變量後可能致使優化器沒法正確
使用索引
(1).SQL優化的通常性原則設計方面:
(1).儘可能依賴oracle的優化器,併爲其提供條件;
(2).合適的索引,索引的雙重效應,列的選擇性;
(1).利用索引,避免大表FULL TABLE SCAN;
(2).合理使用臨時表;
(3).避免寫過於複雜的sql,不必定非要一個sql解決問題;
(4).在不影響業務的前提下減少事務的粒度;
任何sql語句只要在where語句後面添加is null或者is not null,那麼oracl優化器將再也不使用索引。
列舉兩個例子說明該問題:
查詢ur_user_info表中phone_no帶10的服務號碼
例子1:Select *from ur_user_info where phone_no like ‘%10%’;
例子2:Select *from ur_user_info where phone_no like ‘10%’;
因爲例1中通配符(%)在搜尋詞首出現,因此oracle系統不使用phone_no的索引,通配符會下降查詢的效率,但當通配符再也不首出現,又能使用索引,如例2所示。
三.ORACLE語句優化規則 |
例如:TAB1 1000條記錄, TAB2 1條記錄
選擇記錄最少的做爲基表
Select count(*) from tab1,tab2;
若是有3個或者3個以上的表則選擇交叉表做爲基表
oracle的解析按照從上而下解析,所以表之間的鏈接必須寫在where條件以前:
例如:
低效率:
select .. from
emp e
where sal > 50000 and job = 'manager'
and 25 < (select count(*) from emp where mgr=e.empno);
高效率:
select .. from
emp e
where 25 < (select count(*) from emp where mgr=e.empno)
and sal > 50000
and job = 'manager';
Sql在執行帶通配符的語句時,若是‘%’在首位,那麼在字段上創建的主鍵或者索引將會失效!
應該避免相似語句的出現
Select name from user_info where name=’%A’;
當刪除表時,使用delete執行操做,回滾端用來存放可恢復的信息,當沒有提交事務的時候,執行回滾事務,數據會恢復到執行delete操做以前,而當用truncate是,回滾端則不會存放可恢復的信息,減小資源的調用。
避免使用 HAVING 子句, HAVING 只會在檢索出全部記錄以後纔對結果集進行過濾. 這個處理須要排序,總計等操做. 若是能經過 WHERE 子句限制記錄的數目,那就能減小這方面的開銷.
低效:
Select tab_name from tables where tab_name = ( select
tab_name from tab_columns where version = 604) and db_ver=
( select db_ver from tab_columns where version = 604)
高效:
select tab_name from tables where (tab_name,db_ver) =
( select tab_name,db_ver) from tab_columns where version =604)
低效:
Select.. from location where loc_id = 10 or loc_id = 20 or loc_id = 30
高效:
Select..from location where loc_in in (10,20,30);
最高效的刪除重複記錄的方法
Delete from ur_user_info a
Where a.rowid>(select min(b.rowid)
From ur_user_info b
Where b. uid=a. uid);
帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啓動SQL引擎執行耗費資源的排序(SORT)功能. DISTINCT須要一次排序操做, 而其餘的至少須要執行兩次排序.
例如,一個UNION查詢,其中每一個查詢都帶有GROUP BY子句, GROUP BY會觸發嵌入排序(NESTED SORT) ; 這樣, 每一個查詢須要執行一次排序, 而後在執行UNION時, 又一個惟一排序(SORT UNIQUE)操做被執行並且它只能在前面的嵌入排序結束後才能開始執行. 嵌入的排序的深度會大大影響查詢的效率.
若是表中有兩個以上(包括兩個)索引,其中有一個惟一性索引,而其餘是非惟一性.在這種狀況下,ORACLE將使用惟一性索引而徹底忽略非惟一性索引.
舉例:
select ename from emp where empno = 2326 and deptno = 20 ;這裏,只有empno上的索引是惟一性的,因此empno索引將用來檢索記錄.
table access by rowid on emp index unique scan on emp_no_idx;
若是索引是創建在多個列上, 只有在它的第一個列(leading column)被where子句引用時,優化器纔會選擇使用該索引. 當僅引用索引的第二個列時,優化器使用了全表掃描而忽略了索引。
低效:
select ..
from dept
where sal * 12 > 25000;
高效:
select ..
from dept
where sal > 25000/12;
當比較不一樣數據類型的數據時, ORACLE自動對列進行簡單的類型轉換.
假設EMP_TYPE是一個字符類型的索引列.
select user_no,user_name,address
from user_files
where user_no = 109204421
這個語句被ORACLE轉換爲:
select user_no,user_name,address
from user_files
where to_number(user_no) = 109204421由於內部發生的類型轉換, 這個索引將不會被用到!
如用 :
where a.order_no = b.order_no
不用 :
where to_number (substr(a.order_no, instr(b.order_no, '.') - 1)
= to_number (substr(a.order_no, instr(b.order_no, '.') - 1)
例如:
select count(*) sum(sal)
from emp
where dept_no = 0020
and ename like 'smith%';
select count(*) sum(sal)
from emp
where dept_no = 0030
and ename like 'smith%';
你能夠用DECODE函數高效地獲得相同結果
select count(decode(dept_no, 0020, 'x', null)) d0020_count,
count(decode(dept_no, 0030, 'x', null)) d0030_count,
sum(decode(dept_no, 0020, sal, null)) d0020_sal,
sum(decode(dept_no, 0030, sal, null)) d0030_sal
from emp
where ename like 'smith%';
低效
select tab_name
from tables
where tab_name = ( select tab_name
from tab_columns
where version = 604)
and db_ver= ( select db_ver
from tab_columns
where version = 604)
高效
select tab_name
from tables
where (tab_name,db_ver)
= ( select tab_name,db_ver)
from tab_columns
where version = 604)
(a).ORDER BY語句決定了Oracle如何將返回的查詢結果排序。Order by語句對要排序的列沒有什麼特別的限制,也能夠將函數加入列中(象聯接或者附加等)。任何在Order by語句的非索引項或者有計算表達式都將下降查詢速度。
(b). order by語句以找出非索引項或者表達式,它們會下降性能。解決這個問題的辦法就是重寫order by語句以使用索引,也能夠爲所使用的列創建另一個索引,同時應絕對避免在order by子句中使用表達式。
索引是表的一個概念部分,用來提升檢索數據的效率,ORACLE使用了一個複雜的自平衡B-tree結構. 一般,經過索引查詢數據比全表掃描要快. 當ORACLE找出執行查詢和Update語句的最佳路徑時, ORACLE優化器將使用索引. 一樣在聯結多個表時使用索引也能夠提升效率. 另外一個使用索引的好處是,它提供了主鍵(primary key)的惟一性驗證。一般, 在大型表中使用索引特別有效. 固然,你也會發現, 在掃描小表時,使用索引一樣能提升效率. 雖然使用索引能獲得查詢效率的提升,可是咱們也必須注意到它的代價. 索引須要空間來存儲,也須要按期維護, 每當有記錄在表中增減或索引列被修改時, 索引自己也會被修改. 這意味着每條記錄的INSERT , DELETE , UPDATE將爲此多付出4 , 5 次的磁盤I/O . 由於索引須要額外的存儲空間和處理,那些沒必要要的索引反而會使查詢反應時間變慢.。按期的重構索引是有必要的。
WHERE子句中,若是索引列是函數的一部分.優化器將不使用索引而使用全表掃描.
低效:
SELECT … FROM DEPT WHERE SAL * 12 > 25000;
高效:
SELECT … FROM DEPT WHERE SAL > 25000/12;
若是DEPTNO上有一個索引。
高效:
SELECT *
FROM EMP
WHERE DEPTNO >=4
低效:
SELECT *
FROM EMP
WHERE DEPTNO >3
例子:
select * from employee where salary <> 3000;
對這個查詢,能夠改寫爲不使用NOT:
select * from employee where salary<3000 or salary>3000;
雖然這兩種查詢的結果同樣,可是第二種查詢方案會比第一種查詢方案更快些。第二種查詢容許Oracle對salary列使用索引,而第一種查詢則不能使用索引。
好比有的表PHONE_NO字段是CHAR型,並且建立有索引,
但在WHERE條件中忘記了加引號,就不會用到索引。
WHERE PHONE_NO=‘13920202022’
WHERE PHONE_NO=13920202022
四.優化總結 |
a.建立表的時候。應儘可能創建主鍵,儘可能根據實際須要調整數據表的PCTFREE和PCTUSED參數;大數據表刪除,用truncate table代替delete。
b. 合理使用索引,在OLTP應用中一張表的索引不要太多。數據重複量大的列不要創建二叉樹索引,能夠採用位圖索引;組合索引的列順序儘可能與查詢條件列順序保持一致;對於數據操做頻繁的表,索引須要按期重建,以減小失效的索引和碎片。
c.查詢儘可能用肯定的列名,少用*號。
select count(key)from tab where key> 0性能優於select count(*)from tab;
d. 儘可能少嵌套子查詢,這種查詢會消耗大量的CPU資源;對於有比較多or運算的查詢,建議分紅多個查詢,用union all聯結起來;多表查詢的查詢語句中,選擇最有效率的表名順序。Oracle解析器對錶解析從右到左,因此記錄少的表放在右邊。
e.儘可能多用commit語句提交事務,能夠及時釋放資源、解鎖、釋放日誌空間、減小管理花費;在頻繁的、性能要求比較高的數據操做中,儘可能避免遠程訪問,如數據庫鏈等,訪問頻繁的表能夠常駐內存:alter table...cache;
f.在Oracle中動態執行SQL,儘可能用execute方式,不用dbms_sql包。
《Oracle SQL 語句優化》 2010 做者:Black_Snail
《基於Oracle的SQL優化典型案例分析》2013做者:dbsnake @dbsnake