Oracle中每條sql在執行前都要解析,解析分為硬解析、軟解析、軟軟解析。
Oracle會緩存DML語句,相同的DML語句會進行軟解析。但不會緩存DDL語句,所以DDL每次都做硬解析。硬解析是一個很耗時的操作,所以應用程序內部很少執行執行DDL。DDL一般在部署前執行。
sql語句執行步驟:
1.語法檢查(syntax check)
2.語義檢查(symantic check): 對象是否存在,是否有權限。
3.sql解析(parse): 利用內部算法對sql進行解析,生成解析樹及執行計划。
4.執行sql,返回結果(execute and return)
首先了解一下sql解析時用到的內存結構——shared pool:
shared pool是一塊內存池,里邊又被分成了很多小的區塊,每個塊有他們的作用
1.free (空閑)
2.library cache (庫緩存,緩存sql語句以及sql所對應的執行計划)
3.row cache (字典緩存——庫里有多少表,多少用戶,多少個列,列的名字,列的數據類型,每個表多大等等都屬於數據庫自身信息。)
一個sql 語句,進入到數據庫后,server process 會拿着sql語句到shared pool中的library cache 里邊去找,看sql語句以前是否有執行過。也就是在library cache 里面看有沒有這條sql語句以及sql語句所對應的執行計划。(此過程是通過對傳遞進來的SQL語句使用HASH函數運算得出HASH值,與共享池中現有語句的HASH值進行比較看是否一一對應。現有數據庫中SQL語句的HASH值我們可以通過訪問v$sql、v$sqlarea、v$sqltext等數據字典中的HASH_VALUE列查詢得出。)
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.
如果SQL語句的HASH值一致,那么ORACLE事實上還需要對SQL語句的語義進行再次檢測,以決定是否一致。那么為什么Oracle需要再次對語句文本進行檢測呢?不是SQL語句的HASH值已經對應上了?事實上就算是SQL語句的HASH值已經對應上了,並不能說明這兩條SQL語句就已經可以共享了。
例如:假如用戶SYS有自己的一張表EMP,他要執行查詢語句:select * from emp; 用戶SYSTEM也有一張EMP表,同樣要查詢select * from emp;這樣他們兩條語句在文本上是一模一樣的,他們的HASH值也會一樣,但是由於涉及到查詢的相關表不一樣,他們事實上是無法共享的.
下面我們來看實驗:
數據庫版本:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
create table test as select * from user_objects where 1<>1;
begin
dbms_stats.gather_table_stats('TOUGH','TEST');
end;
alter system flush shared_pool;
- 硬解析
select * from test where object_id=20;
select * from test where object_id=30;
- 軟解析
select * from test where object_id=40;
select * from test where object_id=40;select * from test where object_id=50;
select * from test where object_id=50;
select * from test where object_id=50;
select * from test where object_id=50;
- 軟軟解析
begin
for i in 1 .. 4 loop
execute immediate 'select * from test where object_id=:i'
using i;
end loop;
end;
查看解析情況:
select sql_text, s.parse_calls, loads, executions
from v$sql s
where sql_text like 'select * from test where object_id%'
order by 1, 2, 3, 4;
SQL_TEXT | PARSE_CALLS | LOADS | EXECUTIONS |
select * from test where object_id=20 | 1 | 1 | 1 |
select * from test where object_id=30 | 1 | 1 | 1 |
select * from test where object_id=40 | 2 | 1 | 2 |
select * from test where object_id=50 | 4 | 1 | 4 |
select * from test where object_id=:i | 1 | 1 | 4 |
object_id=20 -> 因為沒有緩存此條sql,所以硬解析
object_id=30 -> 因為沒有緩存此條sql,所以硬解析
object_id=40 -> 因為第一次執行已經緩存此條sql,所以軟解析次數為2,硬解析次數為1
object_id=50 -> 因為第一次執行已經緩存此條sql,所以軟解析次數為4,硬解析次數為1
object_id=:i -> 用了動態綁定變量,盡管執行了4次,但只做了一次硬解析和一次軟解析
字段解釋:
PARSE_CALLS - 解析的次數
LOADS - 硬解析的次數
EXECUTIONS - 執行的次數