自適應遊標共享(ACS)與sql計劃管理(SPM)的相互影響

eric0435發表於2013-10-24

討論自適應遊標共享和sql計劃管理的相互影響,要記住它們是負責執行不同任務的.ACS自適應遊標共享控制在特定執行時間一個子遊標是否被共享.對於每個執行的查詢,自適應遊標會考慮當前的繫結變數值並決定一個存在的子游標是否能被共享或者最佳化器將給一個機會對於當前的繫結變數值找到更好的執行計劃.SPM(sql計劃管理)控制著哪個執行計劃會被最佳化器選中.如果一個子遊標是ind-aware,那麼決定是否共享是不會理睬這個查詢是不是由sql計劃管理所控制.但是一旦查詢和它的當前繫結變數被髮送給最佳化器sql計劃管理會約束最佳化器選擇執行計劃,而不會考慮這個查詢現在是否正在由自適應遊標進行最佳化.

讓我們來看一下例子,有許多方法將執行計劃載入到sql計劃管理中,但是為了簡單起見,測試時將手動從遊標快取中載入執行計劃將使用下面的語句來建立一個名叫employees_jy的表,下面的語句是向employees_jy表中插入多行記錄,在job列上資料有大量的傾斜,且在表上只建立一個索引.

SQL> drop table employees_jy purge;
 
Table dropped
SQL> create table employees_jy as select * from hr.employees;
 
Table created
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
104 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
208 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
416 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
832 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
1664 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_VP','AD_PRES');
 
3328 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_PRES');
 
6658 rows inserted
SQL> insert into employees_jy
  2  select * from employees_jy where job_id not in ('AD_PRES');
 
13316 rows inserted
 
SQL> commit;
 
Commit complete
 

SQL> create index EMP_DEPARTMENT_JY_IX on employees_jy (department_id);
 
Index created


SQL> begin
  2   dbms_stats.gather_table_stats(null, 'employees_jy');
  3  end;
  4  /
  
 
PL/SQL procedure successfully completed

sys@JINGYONG> select job_id,count(*) from employees_jy group by job_id
  2  order by 2;

JOB_ID       COUNT(*)
---------- ----------
AD_PRES             1
AD_VP               8
AD_ASST           256
AC_ACCOUNT        256
AC_MGR            256
PU_MAN            256
PR_REP            256
MK_REP            256
MK_MAN            256
HR_REP            256
FI_MGR            256
SA_MAN           1280
IT_PROG          1280
PU_CLERK         1280
FI_ACCOUNT       1280
ST_MAN           1280
SH_CLERK         5120
ST_CLERK         5120
SA_REP           7680

已選擇19行。

下面將執行一個簡單的查詢將這個employees_jy與hr.departments表使用department_id進行連線
使用job_id對錶employees_jy進行過濾併產生聚集結果.

select /*+ bind_aware */ d.department_name,avg(e.salary) 
from employees_jy e,hr.departments d
where e.job_id=:job
and e.department_id=d.department_id
group by d.departmentd_name;

我們為了加快在遊標快取中得到bind-aware遊標,對上面的查詢語句使用了bind_aware提示.

如果我們對job_id使用三種不同的繫結變數值,AD_PRES,SA_MAN和SA_REP來執行上面的查詢,那麼最佳化器會選擇三種不同的執行計劃.

sys@JINGYONG> select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='AD_PRES'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
        24000 Executive

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical'));

Plan hash value: 912418101

----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |              |       |       |    79 (100)|          |
|   1 |  HASH GROUP BY                |              |     3 |   165 |    79   (3)| 00:00:01 |
|   2 |   NESTED LOOPS                |              |       |       |            |          |
|   3 |    NESTED LOOPS               |              |     3 |   165 |    78   (2)| 00:00:01 |
|   4 |     VIEW                      | VW_GBC_5     |     3 |   117 |    77   (2)| 00:00:01 |
|   5 |      HASH GROUP BY            |              |     3 |    99 |    77   (2)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL       | EMPLOYEES_JY |     3 |    99 |    76   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN         | DEPT_ID_PK   |     1 |       |     0   (0)|          |
|   8 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS  |     1 |    16 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------



sys@JINGYONG> select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='SA_MAN'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
        12200 Sales

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical')
);



Plan hash value: 2162091158

------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |    80 (100)|          |
|   1 |  HASH GROUP BY      |              |    27 |  1323 |    80   (3)| 00:00:01 |
|*  2 |   HASH JOIN         |              |  1505 |  73745|    79   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| EMPLOYEES_JY |    27 |  50127|     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| DEPARTMENTS  |  1519 |    432|    76   (0)| 00:00:01 |
------------------------------------------------------------------------------------

sys@JINGYONG> select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='SA_REP'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
   8396.55172 Sales

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical')
);


Plan hash value: 4206419095


------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |    81 (100)|          |
|   1 |  HASH GROUP BY      |              |    27 |  1323 |    81   (3)| 00:00:01 |
|*  2 |   HASH JOIN         |              |  9050 |   433K|    80   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| DEPARTMENTS  |    27 |   432 |     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| EMPLOYEES_JY |  9136 |   294K|    76   (0)| 00:00:01 |
------------------------------------------------------------------------------------

下面我們載入兩個執行計劃到sql計劃管理中,再來使用繫結變數值AD_PRES,SA_REP來執行查詢,這裡有兩個子游標有不同的執行計劃.

SQL> select child_number,plan_hash_value
  2  from v$sql
  3  where sql_id='48ndug79z68zn'
  4  ;
 
CHILD_NUMBER PLAN_HASH_VALUE
------------ ---------------
           0      912418101
           1      4206419095


sys@JINGYONG> var loaded number
sys@JINGYONG> exec :loaded:=dbms_spm.load_plans_from_cursor_cache('48ndug79z68zn');

PL/SQL 過程已成功完成。

sys@JINGYONG> print loaded

    LOADED
----------
         2

現在如果我們同樣使用上面三個繫結變數值來執行查詢,sql計劃管理將會約束最佳化器從sql計劃基線中的兩個可接受
的執行計劃中選擇,我們還是使用相同的執行順序來看一下最佳化器會選擇哪一個.

 select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='AD_PRES'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
        24000 Executive

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical')
);


Plan hash value: 912418101

----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |              |       |       |    79 (100)|          |
|   1 |  HASH GROUP BY                |              |     3 |   165 |    79   (3)| 00:00:01 |
|   2 |   NESTED LOOPS                |              |       |       |            |          |
|   3 |    NESTED LOOPS               |              |     3 |   165 |    78   (2)| 00:00:01 |
|   4 |     VIEW                      | VW_GBC_5     |     3 |   117 |    77   (2)| 00:00:01 |
|   5 |      HASH GROUP BY            |              |     3 |    99 |    77   (2)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL       | EMPLOYEES_JY |     3 |    99 |    76   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN         | DEPT_ID_PK   |     1 |       |     0   (0)|          |
|   8 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS  |     1 |    16 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------


Note
-----
   - dynamic sampling used for this statement (level=2)
   - SQL plan baseline SQL_PLAN_5rjzd2w0wwnak39ef2806 used for this statement

對於這個繫結變數值,選擇了正確的執行計劃沒有因為sql計劃基線而混淆.這是因為這個執行計劃被載入到
sql計劃基線中且是可接受的.所以最佳化器允許選擇它.

 select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='SA_MAN'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
        12200 Sales

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical')
);

Plan hash value: 4206419095


------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |    81 (100)|          |
|   1 |  HASH GROUP BY      |              |    27 |  1323 |    81   (3)| 00:00:01 |
|*  2 |   HASH JOIN         |              |  9050 |   433K|    80   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| DEPARTMENTS  |    27 |   432 |     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| EMPLOYEES_JY |  9136 |   294K|    76   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)
   - SQL plan baseline SQL_PLAN_15f1skdhjq6mx641797f3 used for this statement

對於這個繫結變數值,最佳化器選擇了一個在sql計劃基數中不存在的執行計劃,所以我們選擇了一個可以接受的最好的執行計劃來執行這個查詢,最佳化器提出將基於成本的執行計劃新增到sql計劃基線中,但它將不會被考慮直到它已經被改進之前.

SQL> select sql_handle,plan_name,accepted
  2  from dba_sql_plan_baselines
  3  where sql_handle='SYS_SQL_5bc7ed1701ce5152';
 
SQL_HANDLE                     PLAN_NAME                      ACCEPTED
------------------------------ ------------------------------ --------
SYS_SQL_5bc7ed1701ce5152       SQL_PLAN_5rjzd2w0wwnak39ef2806 YES
SYS_SQL_5bc7ed1701ce5152       SQL_PLAN_15f1skdhjq6mx641797f3 YES
SYS_SQL_5bc7ed1701ce5152       SQL_PLAN_5rjzd2w0wwnakecea1efa NO 

SQL> select * from table(dbms_xplan.display_sql_plan_baseline('SYS_SQL_5bc7ed1701ce5152','SQL_PLAN_5rjzd2w0wwnakecea1efa','basic'));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
SQL handle: SYS_SQL_5bc7ed1701ce5152
SQL text: select /*+ bind_aware */ avg(e.salary),d.department_name from
          employees_jy e,hr.departments d where e.job_id=:job and
          e.department_id=d.department_id group by d.department_name
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_5rjzd2w0wwnakecea1efa         Plan id: 2162091158
Enabled: YES     Fixed: NO      Accepted: NO      Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------
Plan hash value: 2162091158


------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |    80 (100)|          |
|   1 |  HASH GROUP BY      |              |    27 |  1323 |    80   (3)| 00:00:01 |
|*  2 |   HASH JOIN         |              |  1505 |  73745|    79   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| EMPLOYEES_JY |    27 |  50127|     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| DEPARTMENTS  |  1519 |    432|    76   (0)| 00:00:01 |
------------------------------------------------------------------------------------

sys@JINGYONG> select /*+ bind_aware */ avg(e.salary),d.department_name
  2  from employees_jy e,hr.departments d
  3  where e.job_id='SA_REP'
  4  and e.department_id=d.department_id
  5  group by d.department_name;

AVG(E.SALARY) DEPARTMENT_NAME
------------- ------------------------------
   8396.55172 Sales

sys@JINGYONG> select * from table(dbms_xplan.display_cursor(null,null,'typical')
);


Plan hash value: 4206419095


------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |    81 (100)|          |
|   1 |  HASH GROUP BY      |              |    27 |  1323 |    81   (3)| 00:00:01 |
|*  2 |   HASH JOIN         |              |  9050 |   433K|    80   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| DEPARTMENTS  |    27 |   432 |     3   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| EMPLOYEES_JY |  9136 |   294K|    76   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)
   - SQL plan baseline SQL_PLAN_15f1skdhjq6mx641797f3 used for this statement

和我們所期待的一樣,和原來得到的執行計劃一樣,因為這個執行計劃被載入到sql計劃基線中了.因為第二個與第三個查詢使用了相同的執行計劃,而在遊標快取中只有一個能被共享.因此現在這個遊標將會匹配與SA_MAN或SA_REP(或者在它們兩者之間)有相似選擇性的繫結變數.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-774991/,如需轉載,請註明出處,否則將追究法律責任。

相關文章