26 Oracle深度學習筆記——SQL基線
26.Oracle深度學習筆記——SQL基線
歡迎轉載,轉載請標明出處:http://blog.csdn.net/notbaron/article/details/50830903
1. SQL計劃基線
SQL計劃基線可以理解是儲存提綱的一個改進版本,不僅和儲存提綱有許多相同的特性,而且也和儲存提綱一樣被設計用來提供穩定的執行計劃,以防執行環境和物件統計資訊的改變對執行計劃產生影響。此外,和儲存提綱類似,也可以在不修改語句的情況下調優應用程式。
維持執行計劃的穩定性是SQL計劃基線唯一被提及的用途.
SQL計劃基線是一個與SQL語句相關聯的物件,它被設計用來影響查詢優化器產生執行計劃時的決定。具體地講,SQL計劃基線主要是一個提示的集合。基本上,SQL計劃基線就是用來迫使查詢優化器為一條給定的SQL語句產生個特定的、穩定的執行計劃。
有多種方法可用來捕獲SQL計劃基線。基本上,它們都是由資料庫引擎自動建立或資料庫管理員手動建立。
2. 基線自動捕獲
當動態初始化引數optimizer_use_sql_plan_baselines設定為true的時候,查詢優化器將自動建立一個新的SQL計劃基線。這個初始化引數被預設設定為FALSE,可以在系統級和會話級修改它。當自動捕獲開啟後,查詢優化器為每條重複執行過(就是至少執行過兩次)的SQL 語句儲存一個新的SQL 計劃基線。為此.它會將每條SQL 語句的簽名插入一個日誌中,以便於管理。
這意味著當一條SQL語句第一次執行的時候,僅把它的簽名插入日誌。然後,當第二次執行相同的語句的時候,如果不存在與此語句相對應的SQL計劃基線,就新建一個並存儲起來。如果與SQL語句相對應的SQL計劃基線已經存在,查詢優化器仍然會對比當前的執行計劃和基於此SQL計劃基線的執行計劃。如果它們不匹配,那麼這個描述當前執行計劃的新的SQL計劃基線將被儲存。然而就像你在前面見到的,不能直接使用當前的執行計劃。查詢優化器被強制使用在SQL 計劃基線的輔助下產生的執行計劃。
計劃基線用來指導優化器始終選擇某一個執行計劃。通過計劃基線,可以將執行計劃儲存在資料庫的表中並進行管理。計劃基線由一個或多個已經被接受到的SQL查詢執行計劃組成。執行一個查詢,且該查詢已經存在計劃基線,優化器就會優先考慮計劃基線中的執行計劃。
3. 測試
用SQL_ID建立基線
[email protected]> create table t as select * fromall_objects;
Table created.
[email protected]>create index t_idx on t(object_name);
Index created.
收集統計資訊:
[email protected]> execdbms_stats.gather_table_stats(user,'t',cascade=>true);
PL/SQL procedure successfully completed.
[email protected]> select/*test_01*/object_id,object_type from t where object_name ='DUAL';
OBJECT_ID OBJECT_TYPE
---------- -----------------------
142 TABLE
143 SYNONYM
查詢所執行SQL的ID
[email protected]> select sql_id,sql_text from v$sqlwhere sql_text like 'select /*test_01*/%';
SQL_ID
-------------
SQL_TEXT
----------------------------------------------------------------------------------------------------
farns9fxz7dum
select /*test_01*/object_id,object_typefrom t where object_name ='DUAL'
SQL計劃基線的管理,需要擁有administersql management object許可權。
檢視已有基線:
SQL>select sql_handle,plan_name fromdba_sql_plan_baselines where sql_text like 'select /*test_01*/%';
no rows selected
使用dbms_spm.load_plans_from_cursor_cache載入sql_id對應的SQL的執行計劃。
declare
x pls_integer;
begin
x := dbms_spm.load_plans_from_cursor_cache(sql_id => 'farns9fxz7dum');
end;
/
再檢視建立的基線如下:
[email protected]> select sql_handle,plan_name fromdba_sql_plan_baselines where sql_text like 'select /*test_01*/%';
SQL_HANDLE
----------------------------------------------------------------------------------------------------
PLAN_NAME
----------------------------------------------------------------------------------------------------
SQL_718c094fd7a20a4d
SQL_PLAN_733099zbu42kda0b930be
如此就為需要建立計劃基線的SQL建立了計劃基線
用sql文字來建立計劃基線
如下
[email protected]>select object_id from t whereobject_name = 'DUAL';
OBJECT_ID
----------
142
143
declare
xpls_integer;
begin
x :=dbms_spm.load_plans_from_cursor_cache(
attribute_name => 'SQL_TEXT',
attribute_value => 'select object_id from t%');
dbms_output.put_line(x);
end;
/
PL/SQL procedure successfully completed.
檢視如下:
[email protected]> select sql_handle,plan_name fromdba_sql_plan_baselines where sql_text like 'select object_id from t%';
SQL_HANDLE
----------------------------------------------------------------------------------------------------
PLAN_NAME
----------------------------------------------------------------------------------------------------
SQL_b7598beb2a522d9d
SQL_PLAN_bfqcbxcp54bcxa0b930be
顯示一條SQL的執行計劃
我們可以使用dbms_xplan.display_sql_plan_baseline來完成,如下:
[email protected]> select sql_handle
from dba_sql_plan_baselines
where plan_name = 'SQL_PLAN_bfqcbxcp54bcxa0b930be'
/
SQL_HANDLE
----------------------------------------------------------------------------------------------------
SQL_b7598beb2a522d9d
[email protected]> select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=> 'SQL_b7598beb2a522d9d'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SQL_b7598beb2a522d9d
SQL text: select object_id from t whereobject_name = 'DUAL'
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_bfqcbxcp54bcxa0b930be Planid: 2696491198
Enabled: YES Fixed: NO Accepted:YES Origin: MANUAL-LOAD
Plan rows: From dictionary
--------------------------------------------------------------------------------
Plan hash value: 767293772
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
---------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 4 (100)| |
| 1| TABLE ACCESS BY INDEX ROWID BATCHED|T | 2 | 60 | 4 (0)| 00:00:01 |
|* 2| INDEX RANGE SCAN | T_IDX | 2 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- access("OBJECT_NAME"='DUAL')
26 rows selected.
也可以輸入plan_name,或是兩者都輸入。還有一個引數為format,來用決定所顯示計劃的詳細資訊,其值可以為baice,typical或all。
4. 建立一個AWR基線
通過如下命令來實現。
begin
dbms_workload_repository.create_baseline(
start_snap_id=> xxxxx,
end_snap_id=> yyyy,
baseline_name=> 'temp_baseline_name');
end;
/
快照點,我們可以使用dba_hist_snapshot檢視查詢
如下:
[email protected]> select snap_id fromdba_hist_snapshot;
SNAP_ID
----------
78
79
92
93
82
95
76
77
86
83
84
85
88
89
94
73
80
81
87
90
91
74
75
23 rows selected.
然後執行如下,建立AWR基線:
begin
dbms_workload_repository.create_baseline(
start_snap_id =>91,
end_snap_id => 92,
baseline_name => 'temp_baseline_name');
end;
/
PL/SQL procedure successfully completed.
建立一個SQL調優集。
begin
dbms_sqltune.create_sqlset(
sqlset_name=> 'temp_sqlset',
description=> 'sql tune set from awr');
end;
/
PL/SQL procedure successfully completed.
從awr基線中找到佔較高資源的查詢來填充SQL調優集。
declare
base_cur dbms_sqltune.sqlset_cursor;
begin
open base_cur for
select value(x)
from table(dbms_sqltune.select_workload_repository(
'temp_baseline_name',null,null,'elapsed_time',
null,null,null,15))x;
dbms_sqltune.load_sqlset(
sqlset_name=> 'temp_sqlset',
populate_cursor=> base_cur);
end;
/
PL/SQL procedure successfully completed.
為SQL調優集中的每一個查詢SQL建立計劃基線。
declare
x pls_integer;
begin
x := dbms_spm.load_plans_from_sqlset(
sqlset_name=> 'temp_sqlset');
end;
/
PL/SQL procedure successfully completed.
這樣就完成了調優集中查詢SQL的計劃基線的建立。
5. SQL基線綜合性使用
很多的時候,優化器只選擇一種執行計劃,我們嘗試使用hint改變這個執行計劃,且執行效率有很較大的提升。但是通常是不能夠改變產品系統裡面的SQL程式碼的。在這種情況下,會用到SQL 計劃基線,在不改變SQL的情況下讓優化器選擇我們加個hint後的執行計劃。使用SQL計劃基線是一個不錯的選擇。
測試如下:
準備表
如下:
[email protected]> drop table t purge;
Table dropped.
[email protected]> create table t as select * fromall_objects;
Table created.
[email protected]> create index t_idx on t(object_name);
Index created.
[email protected]> execdbms_stats.gather_table_stats(user,'t',cascade=>true);
PL/SQL procedure successfully completed.
variable name varchar2(30)
exec :name := 'DUAL'
PL/SQL procedure successfully completed.
執行SQL
[email protected]> select count(*) from t whereobject_name = :name;
COUNT(*)
----------
2
常看剛執行SQL的SQL_ID
[email protected]> Select sql_id, child_number from v$sql where sql_text like 'select count(*) from t where object_name = :name';
SQL_ID CHILD_NUMBER
------------- ------------
astw879f24195 0
顯示執行計劃:
[email protected]> select * fromtable(dbms_xplan.display_cursor(sql_id=>'astw879f24195'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID astw879f24195,child number 0
-------------------------------------
select count(*) from t where object_name =:name
Plan hash value: 293504097
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 3 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | | |
|* 2| INDEX RANGE SCAN| T_IDX | 2 | 50 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- access("OBJECT_NAME"=:NAME)
19 rows selected.
帶HINT執行
如下:
[email protected]> select /*+full(t)*/ count(*) from twhere object_name = :name;
COUNT(*)
----------
2
檢視執行計劃的SQL_ID如下:
[email protected]> select sql_id, child_number from v$sqlwhere sql_text like 'select /*+full(t)*/ count(*) from t where object_name =:name';
SQL_ID CHILD_NUMBER
------------- ------------
9rsq8360s27zu 0
檢視執行計劃:
[email protected]> select * fromtable(dbms_xplan.display_cursor(sql_id=>'9rsq8360s27zu'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID 9rsq8360s27zu,child number 0
-------------------------------------
select /*+full(t)*/ count(*) from t whereobject_name = :name
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 422 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | | |
|* 2| TABLE ACCESS FULL| T | 2 | 50 | 422 (1)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- filter("OBJECT_NAME"=:NAME)
19 rows selected.
然後改變第一個執行SQL(未帶HINT)的執行計劃,讓其走使用hint後的SQL——“9rsq8360s27zu”的執行計劃,也就是讓它走全表掃描
建立兩條SQL計劃基線
[email protected]> Select sql_id,hash_value,child_numberfrom v$sql where sql_text like 'select count(*) from twhere object_name = :name';
SQL_ID HASH_VALUE CHILD_NUMBER
------------- ---------- ------------
astw879f24195 1545733413 0
[email protected]> select sql_id, hash_value,child_numberfrom v$sql where sql_text like 'select /*+full(t)*/ count(*) from t whereobject_name = :name';
SQL_ID HASH_VALUE CHILD_NUMBER
------------- ---------- ------------
9rsq8360s27zu 2172723194 0
建立SQL基線如下:
declare
xpls_integer;
begin
x :=dbms_spm.load_plans_from_cursor_cache(
sql_id => 'astw879f24195',
plan_hash_value => '293504097'
);
dbms_output.put_line(x);
end;
/
第二條:
declare
xpls_integer;
begin
x :=dbms_spm.load_plans_from_cursor_cache(
sql_id => '9rsq8360s27zu',
plan_hash_value => '2966233522'
);
dbms_output.put_line(x);
end;
/
檢視基線:
SQL>select plan_name, enabled, fixed, sql_handle,sql_text from dba_sql_plan_baselines where 1 = 1 and sql_textlike '%count(*) from t where object_name = :name%';
PLAN_NAME
----------------------------------------------------------------------------------------------------
ENA FIX
--- ---
SQL_HANDLE
----------------------------------------------------------------------------------------------------
SQL_TEXT
--------------------------------------------------------------------------------
SQL_PLAN_7x3fcur5rkfgm3fdbb376
YES NO
SQL_7e8dccd5cb7939f3
select /*+full(t)*/ count(*) from t whereobject_name = :name
SQL_PLAN_4d8jvstgfrn4vded8ae2f
YES NO
SQL_46a23bc65eebd09b
select count(*) from t where object_name =:name
檢視一下每個計劃基線裡的執行計劃
SQL> select * fromtable(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_7e8dccd5cb7939f3'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SQL_7e8dccd5cb7939f3
SQL text: select /*+full(t)*/ count(*) fromt where object_name = :name
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_7x3fcur5rkfgm3fdbb376 Plan id: 1071362934
Enabled: YES Fixed: NO Accepted:YES Origin: MANUAL-LOAD
Plan rows: From dictionary
--------------------------------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 422 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | 0 (0)| |
|* 2| TABLE ACCESS FULL| T | 2 | 50 | 422 (1)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- filter("OBJECT_NAME"=:NAME)
26 rows selected.
idle> select * fromtable(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_46a23bc65eebd09b'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SQL_46a23bc65eebd09b
SQL text: select count(*) from t where object_name= :name
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_4d8jvstgfrn4vded8ae2f Plan id: 3738742319
Enabled: YES Fixed: NO Accepted:YES Origin: MANUAL-LOAD
Plan rows: From dictionary
--------------------------------------------------------------------------------
Plan hash value: 293504097
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 3 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | 0 (0)| |
|* 2| INDEX RANGE SCAN| T_IDX | 2 | 50 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- access("OBJECT_NAME"=:NAME)
26 rows selected.
改變的SQL資訊
把加hint後生成的SQL ID與 Plan Hash Value(全表掃描)載入到走索引生成的SQL計劃基線當中。
declare
xpls_integer;
begin
x :=dbms_spm.load_plans_from_cursor_cache(
sql_id => '9rsq8360s27zu',
plan_hash_value => '2966233522',
sql_handle => 'SQL_46a23bc65eebd09b'
);
dbms_output.put_line(x);
end;
/
PL/SQL procedure successfully completed.
其中SQL_46a23bc65eebd09b 是走索引的SQL HANDLE。
而sql_id => '9rsq8360s27zu',
plan_hash_value => '2966233522',
是走全表掃描的相關索引。
執行之後原先走索引的SQL會走全表。
這樣的話
SQL Handle為“SQL_46a23bc65eebd09b”的計劃基線有兩份,因為剛才載入又生成了一份。需要把原來走索引的那個Pan Baseline刪除或禁用掉,保留全表掃描的那一個。
檢視如下:
[email protected]> select * fromtable(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_46a23bc65eebd09b'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SQL_46a23bc65eebd09b
SQL text: select count(*) from t whereobject_name = :name
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_4d8jvstgfrn4v3fdbb376 Plan id: 1071362934
Enabled: YES Fixed: NO Accepted:YES Origin: MANUAL-LOAD
Plan rows: From dictionary
--------------------------------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 422 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | 0 (0)| |
|* 2| TABLE ACCESS FULL| T | 2 | 50 | 422 (1)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- filter("OBJECT_NAME"=:NAME)
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_4d8jvstgfrn4vded8ae2f Plan id: 3738742319
Enabled: YES Fixed: NO Accepted:YES Origin: MANUAL-LOAD
Plan rows: From dictionary
--------------------------------------------------------------------------------
Plan hash value: 293504097
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | | 3 (100)| |
| 1| SORT AGGREGATE | | 1| 25 | 0 (0)| |
|* 2| INDEX RANGE SCAN| T_IDX | 2 | 50 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operationid):
---------------------------------------------------
2- access("OBJECT_NAME"=:NAME)
47 rows selected.
刪除原先的基線
程式如下:
declare
xpls_integer;
begin
x :=dbms_spm.drop_sql_plan_baseline(
plan_name => 'SQL_PLAN_4d8jvstgfrn4vded8ae2f',
sql_handle => 'SQL_46a23bc65eebd09b'
);
end;
/
執行檢視
[email protected]> select count(*) from t whereobject_name = :name;
COUNT(*)
----------
2
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0| SELECT STATEMENT | | 1 | 25 | 422 (1)| 00:00:01 |
| 1| SORT AGGREGATE | | 1| 25 | | |
|* 2| TABLE ACCESS FULL| T | 2 | 50 | 422 (1)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
2- filter("OBJECT_NAME"=:NAME)
Note
-----
-SQL plan baseline "SQL_PLAN_4d8jvstgfrn4v3fdbb376" used for thisstatement
Statistics
----------------------------------------------------------
7 recursive calls
0 dbblock gets
1519 consistent gets
0 physical reads
0 redosize
542 bytes sent via SQL*Net to client
551 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rowsprocessed
走的是SQL基線了。
6. 基線維護
檢視基線
一個查詢已經有計劃基線。然後新加了一個這個查詢可以使用的索引。
優化器確實這個查詢現在有成本更低的新執行計劃可用。並將這個新執行以未被接受的狀態加入到計劃歷史中。
通過SQL調優顧問給出的建議或者通過查詢dba_sql_plan_baselines檢視發現了新的計劃。
[email protected]> select plan_name,sql_handle,enabled,accepted,optimizer_costfrom dba_sql_plan_baselines where sql_text like 'select object_id from t%';
PLAN_NAME
----------------------------------------------------------------------------------------------------
SQL_HANDLE
----------------------------------------------------------------------------------------------------
ENA ACC OPTIMIZER_COST
--- --- --------------
SQL_PLAN_bfqcbxcp54bcxa0b930be
SQL_b7598beb2a522d9d
YES YES 4
接受基線中的計劃
找到未被接受的執行計劃,即accepted為NO的SQL執行計劃,然後我們用dbms_spm.evolve_sql_plan_baseline函式來將一個計劃歷史移到計劃基線中,可用如下類似的指令碼:
declare
val clob;
begin
val := dbms_spm.evolve_sql_plan_baseline(
sql_handle=> 'SQL_b7598beb2a522d9d');
dbms_output.put_line(val);
end;
/
禁用基線計劃
計劃基線進行一個禁用。我們可以使用如下的方法。
declare
x pls_integer;
begin
x := dbms_spm.alter_sql_plan_baseline(
plan_name=> 'SQL_PLAN_bfqcbxcp54bcxa0b930be',
attribute_name =>'ENABLED',
attribute_value=> 'NO'
);
dbms_output.put_line(x);
end;
/
PL/SQL proceduresuccessfully completed.
然後檢視如下:
[email protected]> select plan_name,sql_handle,enabled,accepted,optimizer_costfrom dba_sql_plan_baselines where sql_text like 'select object_id from t%';
PLAN_NAME
----------------------------------------------------------------------------------------------------
SQL_HANDLE
----------------------------------------------------------------------------------------------------
ENA ACCOPTIMIZER_COST
--- -----------------
SQL_PLAN_bfqcbxcp54bcxa0b930be
SQL_b7598beb2a522d9d
NO YES 4
刪除計劃基線
如果,某個計劃基線不再使用,我們可以對它進行一個刪除。我們可以使用如下的方法。
declare
x pls_integer;
begin
x := dbms_spm.drop_sql_plan_baseline(
plan_name=> 'SQL_PLAN_bfqcbxcp54bcxa0b930be'
);
end;
/
PL/SQL proceduresuccessfully completed.
檢視如下,被刪除了:
[email protected]> select plan_name,sql_handle,enabled,accepted,optimizer_costfrom dba_sql_plan_baselines where sql_text like 'select object_id from t%';
no rows selected
自動增加計劃基線
修改引數optimizer_capture_sql_plan_baselines的值即可。
再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed