Oracle的硬解析和軟解析
提到軟解析(soft prase)和硬解析(hard prase),就不能不說一下Oracle對sql的處理過程。當你發出一條sql語句交付Oracle,在執行和獲取結果前,Oracle對此sql將進行幾個步驟的處理過程: 1、語法檢查(syntax check) 檢查此sql的拼寫是否語法。 2、語義檢查(semantic check) 諸如檢查sql語句中的訪問對象是否存在及該用戶是否具備相應的權限。 3、對sql語句進行解析(prase) 利用內部算法對sql進行解析,生成解析樹(parse tree)及執行計划(execution plan)。 4、執行sql,返回結果(execute and return) 其中,軟、硬解析就發生在第三個過程里。 Oracle利用內部的hash算法來取得該sql的hash值,然后在library cache里查找是否存在該hash值; 假設存在,則將此sql與cache中的進行比較; 假設“相同”,就將利用已有的解析樹與執行計划,而省略了優化器的相關工作。這也就是軟解析的過程。 誠然,如果上面的2個假設中任有一個不成立,那么優化器都將進行創建解析樹、生成執行計划的動作。這個過程就叫硬解析。 創建解析樹、生成執行計划對於sql的執行來說是開銷昂貴的動作,所以,應當極力避免硬解析,盡量使用軟解析。 這就是在很多項目中,倡導開發設計人員對功能相同的代碼要努力保持代碼的一致性,以及要在程序中多使用綁定變量的原因。 |
當客戶端進程,將SQL語句通過監聽器發送到Oracle時, 會觸發一個Server process生成,來對該客戶進程服務。Server process得到SQL語句之后,對SQL語句進行Hash運算,然后根據Hash值到library cache中查找,如果存在,則直接將library cache中的緩存的執行計划拿來執行,最后將執行結果返回該客戶端,這種SQL解析叫做軟解析;如果不存在,則會對該SQL進行解析parse,然后執行,返回結果,這種SQL解析叫做硬解析。
1.硬解析的步驟
硬解析一般包括下面幾個過程:
1)對SQL語句進行語法檢查,看是否有語法錯誤。比如select from where 等的拼寫錯誤,如果存在語法錯誤,則推出解析過程;
2)通過數據字典(row cache),檢查SQL語句中涉及的對象和列是否存在。如果不存在,則推出解析過程。
3)檢查SQL語句的用戶是否對涉及到的對象是否有權限。如果沒有則推出解析;
4)通過優化器創建一個最優的執行計划。這個過程會根據數據字典中的對象的統計信息,來計算多個執行計划的cost,從而得到一個最優的執行計划。這一步涉及到大量的數據運算,從而會消耗大量的CPU資源;(library cache最主要的目的就是通過軟解析來減少這個步驟);
5)將該游標所產生的執行計划,SQL文本等裝載進library cache中的heap中。
2.軟解析
所謂軟解析,就是因為相同文本的SQL語句存在於library cache中,所以本次SQL語句的解析就可以去掉硬解析中的一個活多個步驟。從而節省大量的資源的耗費。
shared pool的組成:
3塊區域:free、librarycache、row cache(dictionary cache)
library cache:緩存SQL語句以及SQL語句的執行計划
dictionary cache:Oracle數據庫自身的信息(數據庫中有多少表、多少用戶、表有多少列、列名是什么、列的數據類型、每個表多大)存放在dictionary中
數據字典舉例:想知道數據庫中有沒有T1表
1、create table t1 as select * from dba_objects;
2、desc dba_tables; ---------------數據字典信息表
3、select table_name,owner from dba_tables where table_name like 'T1%’;
所有數據字典信息可在官方文檔中查找booksàreferenceàdba_tables
1、查看librarycache大小
select * from v$sgastat a where a.NAME = 'library cache';
2、free空間大小
select * from v$sgastat a where a.pool = 'shared pool' anda.NAME = 'free memory';
3、row cache空間大小
select * from v$sgastat a where a.NAME = 'row cache';
什么時候發生硬解析:
Server process 拿着SQL語句在librarycache中找,如果這條SQL語句在library cache中沒有,說明這條SQL語句和他的執行計划在library cache中沒有-------硬解析
有--------軟解析
無論hard parse還是soft parse,解析過程中用到很多數據庫自身信息(權限信息、對象信息、對象統計信息------字典信息);即對SQL語句進行解析(軟硬)的時候,都要頻繁的訪問數據字典信息----------所以row cache放在shared pool和library cache在一起
Oracle 硬解析與軟解析是我們經常遇到的問題,什么情況會產生硬解析,什么情況產生軟解析,又當如何避免硬解析?下面的描述將給出
軟硬解析的產生,以及硬解析的弊端和如何避免硬解析的產生。
一、SQL語句的執行過程
當發布一條SQL或PL/SQL命令時,Oracle會自動尋找該命令是否存在於共享池中來決定對當前的語句使用硬解析或軟解析。
通常情況下,SQL語句的執行過程如下:
a.SQL代碼的語法(語法的正確性)及語義檢查(對象的存在性與權限)。
b.將SQL代碼的文本進行哈希得到哈希值。
c.如果共享池中存在相同的哈希值,則對這個命令進一步判斷是否進行軟解析,否則到e步驟。
d.對於存在相同哈希值的新命令行,其文本將與已存在的命令行的文本逐個進行比較。這些比較包括大小寫,字符串是否一致,空格,注釋等,如果一致,則對其進行軟解析,轉到步驟f.否則到d步驟。
e.硬解析,生成執行計划。
f.執行SQL代碼,返回結果。
二、不能使用軟解析的情形
1.下面的三個查詢語句,不能使用相同的共享SQL區。盡管查詢的表對象使用了大小寫,但Oracle為其生成了不同的執行計划
select * from emp;
select * from Emp;
select * from EMP;
2.類似的情況,下面的查詢中,盡管其where子句empno的值不同,Oracle同樣為其生成了不同的執行計划
select * from emp where empno=7369
select * from emp where empno=7788
3.在判斷是否使用硬解析時,所參照的對象及schema應該是相同的,如果對象相同,而schema不同,則需要使用硬解析,生成不同的執行計划
sys@ASMDB> select owner,table_name from dba_tables where table_name like 'TB_OBJ%';
OWNER TABLE_NAME
------------------------------------------------------------
USR1 TB_OBJ --兩個對象的名字相同,當所有者不同
SCOTT TB_OBJ
usr1@ASMDB> select * from tb_obj;
scott@ASMDB> select * from tb_obj; --此時兩者都需要使用硬解析以及走不同的執行計划
三、硬解析的弊端
硬解析即整個SQL語句的執行需要完完全全的解析,生成執行計划。而硬解析,生成執行計划需要耗用CPU資源,以及SGA資源。
在此不得不提的是對庫緩存中閂的使用。閂是鎖的細化,可以理解為是一種輕量級的串行化設備。當進程申請到閂后,則這些閂用於保護共享內存的數在同一時刻不會被兩個以上的進程修改。
在硬解析時,需要申請閂的使用,而閂的數量在有限的情況下需要等待。大量的閂的使用由此造成需要使用閂的進程排隊越頻繁,性能則逾低下。
四、硬解析的演示
下面對上面的兩種情形進行演示
在兩個不同的session中完成,一個為sys帳戶的session,一個為scott賬戶的session,不同的session,其SQL命令行以不同的帳戶名開頭
如" sys@ASMDB>" 表示使用時sys帳戶的session," scott@ASMDB>"表示scott帳戶的session
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --當前的硬解析值為569
parse count (hard) 64 569
scott@ASMDB> select * from emp; www.2cto.com
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一個查詢后硬解析值為570,解析次數增加了一次
parse count (hard) 64 570
scott@ASMDB> select * from Emp;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一個查詢后硬解析值為571
parse count (hard) 64 571
scott@ASMDB> select * from EMP;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一個查詢后硬解析值為572
parse count (hard) 64 572
scott@ASMDB> select * from emp where empno=7369;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一個查詢后硬解析值為573
parse count (hard) 64 573
scott@ASMDB> select * from emp where empno=7369;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一個查詢后硬解析值為574
parse count (hard) 64 574
從上面的示例中可以看出,盡管執行的語句存在細微的差別,但Oracle還是為其進行了硬解析,生成了不同的執行計划。即便是同樣的SQL
語句,而兩條語句中空格的多少不一樣,Oracle同樣會進行硬解析。
五、編碼硬解析的改進方法
1.更改參數cursor_sharing
參數cursor_sharing決定了何種類型的SQL能夠使用相同的SQLarea
CURSOR_SHARING = { SIMILAR | EXACT | FORCE }
EXACT --只有當發布的SQL語句與緩存中的語句完全相同時才用已有的執行計划。
FORCE --如果SQL語句是字面量,則迫使Optimizer始終使用已有的執行計划,無論已有的執行計划是不是最佳的。
SIMILAR --如果SQL語句是字面量,則只有當已有的執行計划是最佳時才使用它,如果已有執行計划不是最佳則重新對這個SQL
--語句進行分析來制定最佳執行計划。
可以基於不同的級別來設定該參數,如ALTER SESSION, ALTER SYSTEM
sys@ASMDB> show parametercursor_shar --查看參數cursor_sharing
NAME TYPE VALUE
----------------------------------------------- ------------------------------
cursor_sharing string EXACT
sys@ASMDB> alter system set cursor_sharing='similar'; --將參數cursor_sharing的值更改為similar
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --當前硬解析的值為865
parse count (hard) 64 865
scott@ASMDB> select * from dept where deptno=10;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一條SQL查詢后,硬解析的值變為866
parse count (hard) 64 866
scott@ASMDB> select * from dept where deptno=20;
sys@ASMDB> select name,class,value from v$sysstat where statistic#=331;
NAME CLASS VALUE
-------------------- -------------------- --執行上一條SQL查詢后,硬解析的值沒有發生變化還是866
parse count (hard) 64 866
sys@ASMDB> select sql_text,child_number from v$sql -- 在下面的結果中可以看到SQL_TEXT列中使用了綁定變量:"SYS_B_0"
2 where sql_text like 'select * from dept where deptno%';
SQL_TEXT CHILD_NUMBER
--------------------------------------------------------------
select * from dept where deptno=:"SYS_B_0" 0
sys@ASMDB> alter system set cursor_sharing='exact'; --將cursor_sharing改回為exact
--接下來在scott的session 中執行deptno=40 和的查詢后再查看sql_text,當cursor_sharing改為exact后,每執行那個一次
--也會在v$sql中增加一條語句
sys@ASMDB> select sql_text,child_number from v$sql
2 where sql_text like 'select * from dept where deptno%';
SQL_TEXT CHILD_NUMBER
--------------------------------------------------------------
select * from dept where deptno=50 0
select * from dept where deptno=40 0
select * from dept where deptno=:"SYS_B_0" 0
注意當該參數設置為similar,會產生不利的影響
2.使用綁定變量
綁定變量要求變量名稱,數據類型以及長度是一致,否則無法使用軟解析
綁定變量(bindvariable)是指在DML語句中使用一個占位符,即使用冒號后面緊跟變量名的形式,如下
select * from emp where empno=7788 --未使用綁定變量
select * from emp where empono=:eno --:eno即為綁定變量
在第二個查詢中,變量值在查詢執行時被提供。該查詢只編譯一次,隨后會把查詢計划存儲在一個共享池(庫緩存)中,以便以后獲取
和重用這個查詢計划。
下面使用了綁定變量,但兩個變量其實質是不相同的,對這種情形,同樣使用硬解析
select * from emp where empno=:eno;
select * from emp where empno=:emp_no
使用綁定變量時要求不同的會話中使用了相同的回話環境,以及優化器的規則等。
使用綁定變量的例子(參照了TOM大師的Oracle 9i&10g編程藝術)
scott@ASMDB> create table tb_test(col int); --創建表tb_test
scott@ASMDB> create or replace procedure proc1 --創建存儲過程proc1使用綁定變量來插入新記錄
2 as
3 begin
4 for i in 110000
5 loop
6 execute immediate 'insert into tb_test values(:n)' using i;
7 end loop;
8 end;
9 /
Procedure created.
scott@ASMDB> create or replace procedure proc2 --創建存儲過程proc2,未使用綁定變量,因此每一個SQL插入語句都會硬解析
2 as
3 begin
4 for i in 110000
5 loop
6 execute immediate 'insert into tb_test values('||i||')';
7 end loop;
8 end;
9 /
Procedure created.
scott@ASMDB> exec runstats_pkg.rs_start
PL/SQL procedure successfully completed.
scott@ASMDB> exec proc1;
PL/SQL procedure successfully completed.
scott@ASMDB> exec runstats_pkg.rs_middle;
PL/SQL procedure successfully completed.
scott@ASMDB> exec proc2;
PL/SQL procedure successfully completed.
scott@ASMDB> exec runstats_pkg.rs_stop(1000);
Run1ran in 1769 hsecs
Run2ran in 12243hsecs --run2運行的時間是run1的/1769≈倍
run1 ran in 14.45% of the time
Name Run1 Run2 Diff
LATCH.SQL memory managerworka 410 2,694 2,284
LATCH.sessionallocation 532 8,912 8,380
LATCH.simulator lrulatch 33 9,371 9,338
LATCH.simulator hashlatch 51 9,398 9,347
STAT…enqueuerequests 31 10,030 9,999
STAT…enqueuereleases 29 10,030 10,001
STAT…parse count (hard) 4 10,011 10,007 --硬解析的次數,前者只有四次
STAT…calls to get snapshots 55 10,087 10,032
STAT…parse count (total) 33 10,067 10,034
STAT…consistentgets 247 10,353 10,106
STAT…consistent gets from ca 247 10,353 10,106
STAT…recursivecalls 10,474 20,885 10,411
STAT…db block gets from cach 10,408 30,371 19,963
STAT…db blockgets 10,408 30,371 19,963
LATCH.enqueues 322 21,820 21,498 --閂的隊列數比較
LATCH.enqueue hashchains 351 21,904 21,553
STAT…session logical reads 10,655 40,724 30,069
LATCH.library cachepin 40,348 72,410 32,062 --庫緩存pin
LATCH.kksstats 8 40,061 40,053
LATCH.library cachelock 318 61,294 60,976
LATCH.cache bufferschains 51,851 118,340 66,489
LATCH.row cacheobjects 351 123,512 123,161
LATCH.librarycache 40,710 234,653 193,943
LATCH.sharedpool 20,357 243,376 223,019
Run1latches total versus runs --difference and pct
Run1 Run2 Diff Pct
157,159 974,086 816,927 16.13% --proc2使用閂的數量也遠遠多於proc1,其比值是。13%
PL/SQL procedure successfully completed.
由上面的示例可知,在未使用綁定變量的情形下,不論是解析次數,閂使用的數量,隊列,分配的內存,庫緩存,行緩存遠遠高於綁定
變量的情況。因此盡可能的使用綁定變量避免硬解析產生所需的額外的系統資源。
綁定變量的優點
減少SQL語句的硬解析,從而減少因硬解析產生的額外開銷(CPU,Shared pool,latch)。其次提高編程效率,減少數據庫的訪問次數。
綁定變量的缺點
優化器就會忽略直方圖的信息,在生成執行計划的時候可能不夠優化。SQL優化相對比較困難
六、總結
1.盡可能的避免硬解析,因為硬解析需要更多的CPU資源,閂等。
2.cursor_sharing參數應權衡利弊,需要考慮使用similar與force帶來的影響。
3.盡可能的使用綁定變量來避免硬解析
我們都知道在Oracle中每條SQL語句在執行之前都需要經過解析,這里面又分為軟解析和硬解析。在Oracle中存在兩種類型的SQL語句,一類為 DDL語句(數據定義語言),他們是從來不會共享使用的,也就是每次執行都需要進行硬解析。還有一類就是DML語句(數據操縱語言),他們會根據情況選擇要么進行硬解析,要么進行軟解析。
DML:INSERT,UPDATE,DELETE,SELECT
DDL:CREATE,DROP,ALTER
一. SQL 解析過程
Oracle對此SQL將進行幾個步驟的處理過程:
1、語法檢查(syntax check): 檢查此sql的拼寫是否語法。
2、語義檢查(semantic check): 諸如檢查sql語句中的訪問對象是否存在及該用戶是否具備相應的權限。
3、對sql語句進行解析(prase): 利用內部算法對sql進行解析,生成解析樹(parse tree)及執行計划(execution plan)。
4、執行sql,返回結果(execute and return)
二. 解析過程詳解
2.1 語法檢測
判斷一條SQL語句的語法是否符合SQL的規范,比如執行:
SQL> selet * from emp;
我們就可以看出由於Select關鍵字少了一個“c”,這條語句就無法通過語法檢驗的步驟了。
2.2 語義檢查
語法正確的SQL語句在解析的第二個步驟就是判斷該SQL語句所訪問的表及列是否准確?用戶是否有權限訪問或更改相應的表或列? 比如如下語句:
SQL> select * from emp;
select * from emp
*
ERROR at line 1:
ORA-00942: table or view does not exist
由於查詢用戶沒有可供訪問的emp對象,因此該SQL語句無法通過語義檢查。
2.3 解析(Parse)
2.3.1 Parse主要分為三種:
1、Hard Parse (硬解析)
2、Soft Parse (軟解析)
3、Soft Soft Parse(好像有些資料中並沒有將這個算在其中)
Hard Parse: 就是上面提到的對提交的Sql完全重新從頭進行解析(當在Shared Pool中找不到時候將會進行此操作),總共有一下5個執行步驟:
1:語法分析
2:權限與對象檢查
3: 在共享池中檢查是否有完全相同的之前完全解析好的. 如果存在,直接跳過4和5,運行Sql, 此時算soft parse.
4:選擇執行計划
5:產生執行計划
注:創建解析樹、生成執行計划對於sql的執行來說是開銷昂貴的動作,所以,應當極力避免硬解析,盡量使用軟解析。這就是在很多項目中,倡導開發設計人員對功能相同的代碼要努力保持代碼的一致性,以及要在程序中多使用綁定變量的原因。
Soft Parse: 就如果是在Shared Pool中找到了與之完全相同的Sql解析好的結果后會跳過Hard Parse中的后面的兩個步驟。
Soft Soft Parse: 實際上是當設置了session_cursor_cache這個參數之后,Cursor被直接Cache在當前Session的PGA中的,在解析的時候只需要對其語法分析、權限對象分析之后就可以轉到PGA中查找了,如果發現完全相同的Cursor,就可以直接去取結果了,也就就是實現了 Soft Soft Parse.