Oracle 課程九之綁定變量


課程目標

  完成本課程的學習后,您應該能夠:

  •變量綁定的目的
  •父子游標
  •游標共享
  •綁定窺探
  •SQL語句處理流程
  •硬解析、軟解析、軟軟解析
  •變量綁定的應用場景
 
1。游標
  游標可以理解為SQL語句的一個句柄,也叫SQL語句的指針,游標指向一條SQL語句,oracle會話要執行一條SQL時,首先要打開游標。
1.1父子游標:
  同樣的SQL,因某些其它的差異,會產生另外的cursor。解析操作的結果是將一個父游標與子游標保存到庫緩存中。很顯然,將它們保存到共享內存的目的是為了重用它們,從而避免硬解析。
  父游標 parent cursor ---第一條運行的SQL。
  子游標 child cursor ---后續運行的SQL 。
  父游標的關鍵信息是SQL語句的文本。
  子游標的關鍵是執行計划和執行環境。
實驗:
sqlplus test/test
drop table t_cursor purge;
create table t_cursor as select * from dba_objects;
alter system flush shared_pool;
創建實驗的表
select count(*) from t_cursor;
select count(*) from T_cursor;
col sql_text format a40;
select sql_id, sql_text, executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';
alter system flush shared_pool;
select sql_id, sql_text, executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';
不共享游標的情況
select count(*) from t_cursor;
select count(*) from t_cursor;
select sql_id, sql_text, executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';
alter system flush shared_pool;
共享游標的情況
--父子游標
alter session set optimizer_mode = all_rows;
select count(*) from t_cursor;
select sql_id, sql_text,child_number,executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';
alter session set optimizer_mode = first_rows_10;
select count(*) from t_cursor;
select sql_id, sql_text,child_number,executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';

grant select any dictionary to scott;

sqlplus scott/tiger
drop table t_cursor purge;
create table t_cursor as select * from user_objects;
col sql_text format a40;
select count(*) from t_cursor;
select sql_id, sql_text,child_number, executions from v$sql
 where sql_text like '%select count(*) from%_cursor%'
   and sql_text not like '%sql_text%';
父子游標
1.2V$SQL_SHARED_CURSOR,提供游標為什么不能共享的原因:

  Desc V$SQL_SHARED_CURSOR:

  字段意思參考幫助文檔:Reference/V$SQL_SHARED_CURSOR

  Select * from V$SQL_SHARED_CURSOR;

  邏輯優化:這個階段,通過應用各種不同的轉換技巧,會生成語義上等同的新的SQL語句。

  物理優化:首先會生成與每個邏輯優化產生的SQL語句有關的執行計划。接着,根據數據字典找到的統計信息或動態取樣收集的統計信息,計算出一個與各個執行計划相關的開銷。最后,擁有最低開銷的執行計划會被選中。

1.3硬解析步驟

    1.) 對SQL語句進行語法檢查,看是否有語法錯誤。如果存在語法錯誤,則退出解析過程;
    2.) 通過數據字典(row cache),檢查SQL語句中涉及的對象和列是否存在。如果不存在,則退出解析過程;
    3.)檢查SQL語句的用戶是否對涉及到的對象是否有權限。沒有則退出解析;
    4.)通過優化器創建一個最優的執行計划。這個過程會根據數據字典中的對象的統計信息,來計算多個執行計划的cost,從而得到一個最優的執行計划。
      這一步涉及到大量的數據運算,從而會消耗大量的CPU資源;(library cache最主要的目的就是通過軟解析來減少這個步驟);

    5.)將該游標所產生的執行計划,SQL文本等裝載進library cache中的heap中。

1.4軟解析、軟軟解析

  軟解析: 所謂軟解析,就是因為相同文本的SQL語句存在於library cache中,所以本次SQL語句的解析就可以去掉硬解析中的多個步驟。從而節省大量的資源的  耗費。
  軟軟解析:所謂的軟軟解析,就是沒有任何解析過程。當設置了session_cached_cursors參數時,當某個session第三次執行相同的SQL語句時,則會把該SQL語句的游標緩存到PGA中。這樣,當該session在執行該SQL語句時,會直接從PGA中取出執行計划,從而跳過解析的所有步驟。

drop table test purge;
alter system flush shared_pool;
create table test as select * from dba_objects where 1<>1;
exec dbms_stats.gather_table_stats(user,'test');

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=50;
var oid number;
exec :oid:=20;
select * from test where object_id=:oid;
exec :oid:=30;
select * from test where object_id=:oid;
exec :oid:=40;
select * from test where object_id=:oid;
exec :oid:=50;
select * from test where object_id=:oid;
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; 
軟件解析

 

2.綁定變量

2.1變量綁定的目的

  變量綁定的目的是把硬解析變成軟解析:

  a.減少SQL硬解析的次數。
  b.減少系統資源開銷。
  c.減少latch爭用。

  綁定變量也有冬天:bind peeking

  bind peeking的由來:在執行含有綁定變量的查詢語句時,完成解析和最優化操作之后才能對綁定變量進行綁定,這意味着在實現最優化操作時無法使用綁定變    量列的統計信息。
  為了解決這個問題,數據庫使用了窺探技術,在第一次解析SQL時,按照窺探變量的值生成執行計划,以后這樣的SQL都按照這個執行。
隱藏參數_optim_peek_user_binds=true則啟用綁定變量窺探,否則CBO認為統計列是均勻的。
由綁定窺探你想到了什么?

 

drop table test purge;
create table test as select * from dba_objects;
create index ind_object_id on TEST (object_id);
exec dbms_stats.gather_table_stats(user,'test',cascade=>true);
set autotrace traceonly
select count(object_name) from test where object_id < 10;
select count(object_name) from test where object_id < 1000000;
set autotrace off

alter session set statistics_level=all;
alter system flush shared_pool;
var ccc number;
exec :ccc:=10;
select count(object_name) from test where object_id < :ccc;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

exec :ccc:=1000000;
select count(object_name) from test where object_id < :ccc;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

alter system flush shared_pool;
exec :ccc:=1000000;
select count(object_name) from test where object_id < :ccc;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

exec :ccc:=10;
select count(object_name) from test where object_id < :ccc;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
Bind peeking

 

2.2變量綁定的應用場景

  OLTP環境應該使用綁定變量:

  •操作的結果集小,雖然有些表數據量很大,但是往往操作的記錄數可能很小。比如改部門,只需要在龐大的用戶表里通過索引找到其中一行數據來做修改。即使每個用戶的謂詞條件不一樣,執行計划也是一樣的,也就是說,在執行計划幾乎唯一的情況下,使用變量來代替謂詞條件,是非常合適的。
  •用戶量非常大、執行頻繁。
---OLTP環境模擬
drop table t purge;
create table t (name varchar2(20),department_id number(10));

begin
  for i in 1 .. 10000 loop
    insert into t values ('test'||i,i);
  end loop;
  commit;
end;
/
select * from t;
------------以上為創造實驗環境--------------
---如果我們要將test1、test2兩個用戶轉部門,那么產生的sql應該是以下兩條內容
select department_id from t where name = 'test1' 
update t set department_id=1000 where name ='test1';
commit;

select department_id from t where name = 'test2' 
update t set department_id=1000 where name ='test2';
commit;
---如果使用綁定變量,那么sql應該是以下內容
select department_id from t where name = :x;
update t set department_id=1000 where name =:x;
---通過綁定變量,我們將兩次硬解析變成了一次硬解析

---接下來我們看綁定變量與不綁定變量的性能區別
alter session set tracefile_identifier = 'Look_For_Me';
alter session set events '10046 trace name context forever,level 12';

begin                        ---不綁定
for x in 1..10000 loop
execute immediate 'select * from t where name ='||x;
end loop;
end;
/

begin                        ---綁定
for x in 1..10000 loop
execute immediate 'select * from t where name =:x' using x;
end loop;
end;
/

alter session set events '10046 trace name context off';
綁定變量應用舉例

  OLAP環境不應該使用綁定變量:

  OLAP環境大多時候是報表處理,操作的結果集龐大,並且數據往往不均勻,在這種情況下,謂詞條件不同,獲取到的數據量也不一定相同,執行計划就不一定相同,這個時候,變量就不那么合適了

  在OLAP系統中,根據不同謂詞條件選擇不同的執行計划是至關重要的,這時候,解析的代價和大查詢的代價比較起來,sql解析消耗的資源是可以忽略不計的
用戶量較少、執行次數少。

2.3游標共享(cursor_sharing)

  cursor sharing用來告訴oracle什么情況下可以共享游標,即SQL重用。oracle默認cursor_sharing 是exact  指的是SQL語句必須絕對一樣的情況下才能共享游標,否則作為新的SQL語句處理。
  適合場景:使用exact 高效的前提是應用代碼中使用了綁定變量,這也是oracle推薦的。

Drop table t purge;
show parameter cursor_sharing;
create table t as select * from dba_objects;
alter system flush shared_pool;
select /*+test_exact*/count(1) from t where object_id=100;
select /*+test_exact*/count(1) from t where object_id=200;
col sql_text format a80;
select sql_text from v$sql where sql_text like '%/*+test_exact*/%'
and  sql_text not like '%sql_text%';

alter system flush shared_pool;
var x number;
exec :x:=100;
select /*+test_exact*/count(1) from t where object_id=:x;
exec :x:=200;
select /*+test_exact*/count(1) from t where object_id=:x;
select sql_text from v$sql where sql_text like '%/*+test_exact*/%'
and  sql_text not like '%sql_text%';
游標共享

  cursor sharing=force,oracle將2條類似的SQL的謂詞用一個變量代替,同時將它們看做同一條SQL語句處理
  適用場景:在無法將應用的代碼修改為綁定變量情況下,oracle提供的一種解決方法。
  注意:游標共享特性有個不太好的名聲,就是它不是很穩定,過去數年,大量與之相關的bug被發現和確認。

Drop table t purge;
show parameter cursor_sharing;
create table t as select * from dba_objects;
alter session set cursor_sharing=force;
alter system flush shared_pool;
select /*+test_force*/count(1) from t where object_id=100;
select /*+test_force*/count(1) from t where object_id=100;
select /*+test_force*/count(1) from t where object_id=200;
col sql_text format a80;
select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_force*/%'
and  sql_text not like '%sql_text%'
And  sql_text not like '%EXPLAIN PLAN%';
游標共享2

  cursor_sharing=similar ,exact和force折中的參數。當探測到謂詞會導致執行計划的改變,就會重新解析,否則不會。
  適用場景:在無法將應用的代碼修改為綁定變量情況下,oracle提供的又一種解決方法,similar的好處是最大限度地避免CBO在綁定變量的情況下做出的錯誤的判斷,但它的代價是分析的次數變大。

Drop table t purge;
show parameter cursor_sharing;
create table t as select * from dba_objects;
Create index ind_t_object_id on t(object_id) nologging;
exec dbms_stats.gather_table_stats(user,'t',cascade => true);
alter session set cursor_sharing=similar;
Set autotrace traceonly 
select /*+test_similar*/* from t where object_id<10;
select /*+test_similar*/* from t where object_id<100000;
Set autotrace off

alter system flush shared_pool;
Set autotrace traceonly
select /*+test_similar*/* from t where object_id<10;
select /*+test_similar*/* from t where object_id<100000;
Set autotrace off
col sql_text format a80;
select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_similar*/%'
and  sql_text not like '%sql_text%'
And  sql_text not like '%EXPLAIN PLAN%';

alter system flush shared_pool;
Set autotrace traceonly
select /*+test_similar*/* from t where object_name='aaa';
select /*+test_similar*/* from t where object_name='bbb';
select /*+test_similar*/* from t where object_name='ccc';
select /*+test_similar*/* from t where object_name='ddd';
Set autotrace off
select sql_id,child_number,sql_text from v$sql where sql_text like '%/*+test_similar*/%'
and  sql_text not like '%sql_text%'
And  sql_text not like '%EXPLAIN PLAN%';
游標共享3

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM