自适应SQL计划管理(AdaptiveSQLPlanManagement)在Oracle12c中的应用
- 手机
- 2025-08-25 03:15:02

在Oracle Database 12c Release 1 (12.1)版本中,引入了对SQL计划管理(SPM)功能的增强,特别是关于SQL计划基线的自动进化机制。这一改进允许数据库更加智能地管理和优化SQL查询的执行计划,确保即使数据分布发生变化,也能维持高效的查询性能。
ASPM的核心理念在于其能够根据实时运行时统计信息来评估不同执行计划的表现,并据此决定是否需要采用新的执行计划。这意味着,即使是在数据库重启之后,优化器也能利用之前收集到的信息做出更加明智的选择。
一、SQL计划管理的基础最初在Oracle 11g中引入的SQL计划管理,旨在通过固定一组已验证为高效的执行计划来稳定查询性能。这种方法被称为“保守的计划选择策略”,因为它倾向于使用经过验证的执行计划,而不是每次都让优化器重新评估所有可能的执行路径。到了Oracle 12c,虽然这个核心理念没有改变,但引入了一些重要的更新以进一步提升自动化程度和灵活性。
二、SYS_AUTO_SPM_EVOLVE_TASK介绍 2.1 自动化SQL计划基线的进化在Oracle数据库12C中,现有基线的演变被自动化为称为SYS_AUTO_SPM_EVOVES_TASK的顾问任务,该任务是由自动化数据库维护任务下现有的“ SQL Tuning Advisor”客户端触发的。
CONN sys@pdb1 AS SYSDBA COLUMN client_name FORMAT A35 COLUMN task_name FORMAT a30 SELECT client_name, task_name FROM dba_autotask_task; CLIENT_NAME TASK_NAME ----------------------------------- ------------------------------ sql tuning advisor AUTO_SQL_TUNING_PROG auto optimizer stats collection gather_stats_prog auto space advisor auto_space_advisor_prog SQL> 2.2 手动干预与任务管理在Oracle 12c中,一个名为SYS_AUTO_SPM_EVOLVE_TASK的新任务被引入,用于自动进化现有的SQL计划基线。这个任务作为数据库维护任务的一部分,由现有的"sql tuning advisor"客户端触发。这意味着,一旦发现有更优的执行计划,系统可以自动对其进行评估,并在确认其优势后将其纳入基线之中。
CONN sys@pdb1 AS SYSDBA COLUMN parameter_name FORMAT A25 COLUMN parameter_value FORMAT a15 SELECT parameter_name, parameter_value FROM dba_advisor_parameters WHERE task_name = 'SYS_AUTO_SPM_EVOLVE_TASK' AND parameter_value != 'UNUSED' ORDER BY parameter_name; PARAMETER_NAME PARAMETER_VALUE ------------------------- --------------- ACCEPT_PLANS TRUE DAYS_TO_EXPIRE UNLIMITED DEFAULT_EXECUTION_TYPE SPM EVOLVE EXECUTION_DAYS_TO_EXPIRE 30 JOURNALING INFORMATION MODE COMPREHENSIVE TARGET_OBJECTS 1 TIME_LIMIT 3600 _SPM_VERIFY TRUE SQL>尽管自动化的进化过程极大简化了管理负担,但在某些情况下,手动干预仍然是必要的。例如,如果想要阻止新的计划自动被接受,可以调整ACCEPT_PLANS参数为FALSE:
BEGIN DBMS_SPM.set_evolve_task_parameter( task_name => 'SYS_AUTO_SPM_EVOLVE_TASK', parameter => 'ACCEPT_PLANS', value => 'FALSE'); END; /DBMS_SPM 包有一个名为 REPORT_AUTO_EVOLVE_TASK 的函数,用于显示自动进化任务所采取的行动的信息。如果没有指定参数,则会为最新的任务运行生成文本报告。
SET LONG 1000000 PAGESIZE 1000 LONGCHUNKSIZE 100 LINESIZE 100 SELECT DBMS_SPM.report_auto_evolve_task FROM dual; REPORT_AUTO_EVOLVE_TASK ---------------------------------------------------------------------------------------------------- GENERAL INFORMATION SECTION --------------------------------------------------------------------------------------------- Task Information: --------------------------------------------- Task Name : SYS_AUTO_SPM_EVOLVE_TASK Task Owner : SYS Description : Automatic SPM Evolve Task Execution Name : EXEC_461 Execution Type : SPM EVOLVE Scope : COMPREHENSIVE Status : COMPLETED Started : 02/19/2025 15:53:33 Finished : 02/19/2025 15:53:35 Last Updated : 02/19/2025 15:53:35 Global Time Limit : 3600 Per-Plan Time Limit : UNUSED Number of Errors : 0 --------------------------------------------------------------------------------------------- SUMMARY SECTION --------------------------------------------------------------------------------------------- Number of plans processed : 0 Number of findings : 0 Number of recommendations : 0 Number of errors : 0 --------------------------------------------------------------------------------------------- SQL> 三、手工演进SPB(SQL Plan Baselines)在之前的版本中,使用EVOLVE_SQL_PLAN_BASELINE函数来实现SQL计划基线的演变。在12c中,这已被基于任务的方法所取代,通常涉及以下步骤。
CREATE_EVOLVE_TASK EXECUTE_EVOLVE_TASK REPORT_EVOLVE_TASK IMPLEMENT_EVOLVE_TASK此外,以下函数可以与进化任务进行交互:
CANCEL_EVOLVE_TASK RESUME_EVOLVE_TASK RESET_EVOLVE_TASK为了展示这一点,我们需要创建一个SQL计划基线:
3.1 创建并填充测试表。 CONN test/test@pdb1 DROP TABLE spm_test_tab PURGE; CREATE TABLE spm_test_tab ( id NUMBER, description VARCHAR2(50) ); INSERT /*+ APPEND */ INTO spm_test_tab SELECT level, 'Description for ' || level FROM dual CONNECT BY level <= 10000; COMMIT;使用未索引的列查询表,这会导致对整个表进行扫描。
SET AUTOTRACE TRACE SELECT description FROM spm_test_tab WHERE id = 99; Execution Plan ---------------------------------------------------------- Plan hash value: 1107868462 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 14 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| SPM_TEST_TAB | 1 | 25 | 14 (0)| 00:00:01 | ----------------------------------------------------------------------------------通过查询V$SQL视图来识别SQL语句的SQL_ID。
CONN sys@pdb1 AS SYSDBA SELECT sql_id FROM v$sql WHERE plan_hash_value = 1107868462 AND sql_text NOT LIKE 'EXPLAIN%'; SQL_ID ------------- gat6z1bc6nc2d SQL> 3.2 使用此SQL_ID手动加载SQL计划基线。 SET SERVEROUTPUT ON DECLARE l_plans_loaded PLS_INTEGER; BEGIN l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache( sql_id => 'gat6z1bc6nc2d'); DBMS_OUTPUT.put_line('Plans Loaded: ' || l_plans_loaded); END; / Plans Loaded: 1 PL/SQL procedure successfully completed. SQL>DBA_SQL_PLAN_BASELINES 视图提供了有关 SQL 计划基线的信息。我们可以看到有一个与我们的基准关联的计划,该计划已启用并接受。
COLUMN sql_handle FORMAT A20 COLUMN plan_name FORMAT A30 SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines WHERE sql_text LIKE '%spm_test_tab%' AND sql_text NOT LIKE '%dba_sql_plan_baselines%'; SQL_HANDLE PLAN_NAME ENA ACC -------------------- ------------------------------ --- --- SQL_7b76323ad90440b9 SQL_PLAN_7qxjk7bch8h5tb65c37c8 YES YES 3.3刷新共享池以强制进行另一个硬解析然后在ID列上创建索引,然后重复查询以查看对执行计划的影响。
CONN sys@pdb1 AS SYSDBA ALTER SYSTEM FLUSH SHARED_POOL; CONN test/test@pdb1 CREATE INDEX spm_test_tab_idx ON spm_test_tab(id); EXEC DBMS_STATS.gather_table_stats(USER, 'SPM_TEST_TAB', cascade=>TRUE); SET AUTOTRACE TRACE SELECT description FROM spm_test_tab WHERE id = 99; Execution Plan ---------------------------------------------------------- Plan hash value: 1107868462 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 14 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| SPM_TEST_TAB | 1 | 25 | 14 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"=99) Note ----- - SQL plan baseline "SQL_PLAN_7qxjk7bch8h5tb65c37c8" used for this statement请注意,查询没有使用新创建的索引,即使我们强制执行硬解析。注释解释了SQL计划基线被使用的原因。查看DBA_SQL_PLAN_BASELINES视图我们可以看到原因。
CONN sys@pdb1 AS SYSDBA COLUMN sql_handle FORMAT A20 COLUMN plan_name FORMAT A30 SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines WHERE sql_handle = 'SQL_7b76323ad90440b9'; SQL_HANDLE PLAN_NAME ENA ACC -------------------- ------------------------------ --- --- SQL_7b76323ad90440b9 SQL_PLAN_7qxjk7bch8h5t3652c362 YES NO SQL_7b76323ad90440b9 SQL_PLAN_7qxjk7bch8h5tb65c37c8 YES YES SQL>SQL 计划基准线现在包含第二个计划,但尚未被接受。
如果您在DBA_SQL_PLAN_BASELINES视图中没有看到新行,请返回并重新运行"spm_test_tab"中的查询,直到您这样做。有时服务器需要几次尝试才能注意到对其他计划的需求。
3.4 创建新的进化任务要使用新计划,我们需要等待维护窗口或手动发展SQL计划基线。为此基线创建一个新的进化任务。
SET SERVEROUTPUT ON DECLARE l_return VARCHAR2(32767); BEGIN l_return := DBMS_SPM.create_evolve_task(sql_handle => 'SQL_7b76323ad90440b9'); DBMS_OUTPUT.put_line('Task Name: ' || l_return); END; / Task Name: TASK_21 PL/SQL procedure successfully completed. SQL>执行进化任务。
SET SERVEROUTPUT ON DECLARE l_return VARCHAR2(32767); BEGIN l_return := DBMS_SPM.execute_evolve_task(task_name => 'TASK_21'); DBMS_OUTPUT.put_line('Execution Name: ' || l_return); END; / Execution Name: EXEC_471 PL/SQL procedure successfully completed. SQL>进化任务结果报告。
SET LONG 1000000 PAGESIZE 1000 LONGCHUNKSIZE 100 LINESIZE 100 SELECT DBMS_SPM.report_evolve_task(task_name => 'TASK_21', execution_name => 'EXEC_471') AS output FROM dual; OUTPUT ---------------------------------------------------------------------------------------------------- GENERAL INFORMATION SECTION --------------------------------------------------------------------------------------------- Task Information: --------------------------------------------- Task Name : TASK_21 Task Owner : SYS Execution Name : EXEC_471 Execution Type : SPM EVOLVE Scope : COMPREHENSIVE Status : COMPLETED Started : 02/19/2025 16:38:15 Finished : 02/19/2025 16:38:16 Last Updated : 02/19/2025 16:38:16 Global Time Limit : 2147483646 Per-Plan Time Limit : UNUSED Number of Errors : 0 --------------------------------------------------------------------------------------------- SUMMARY SECTION --------------------------------------------------------------------------------------------- Number of plans processed : 2 Number of findings : 2 Number of recommendations : 2 Number of errors : 0 --------------------------------------------------------------------------------------------- DETAILS SECTION --------------------------------------------------------------------------------------------- Object ID : 2 Test Plan Name : SQL_PLAN_7qxjk7bch8h5t3652c362 Base Plan Name : SQL_PLAN_7qxjk7bch8h5tb65c37c8 SQL Handle : SQL_7b76323ad90440b9 Parsing Schema : SYS Test Plan Creator : SYS SQL Text : SELECT description FROM spm_test_tab WHERE id = 99 Execution Statistics: ----------------------------- Base Plan Test Plan ---------------------------- ---------------------------- Elapsed Time (s): .00001 .000001 CPU Time (s): .000016 .000001 Buffer Gets: 4 0 Optimizer Cost: 13 2 Disk Reads: 0 0 Direct Writes: 0 0 Rows Processed: 0 0 Executions: 10 10 FINDINGS SECTION --------------------------------------------------------------------------------------------- Findings (1): ----------------------------- 1. The plan was verified in 0.01700 seconds. It passed the benefit criterion because its verified performance was 14.66841 times better than that of the baseline plan. Recommendation: ----------------------------- Consider accepting the plan. Execute dbms_spm.accept_sql_plan_baseline(task_name => 'TASK_21', object_id => 2, task_owner => 'SYS'); EXPLAIN PLANS SECTION --------------------------------------------------------------------------------------------- Baseline Plan ----------------------------- Plan Id : 101 Plan Hash Value : 3059496904 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 13 | 00:00:01 | | * 1 | TABLE ACCESS FULL | SPM_TEST_TAB | 1 | 25 | 13 | 00:00:01 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter("ID"=99) Test Plan ----------------------------- Plan Id : 102 Plan Hash Value : 911393634 --------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 2 | 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED | SPM_TEST_TAB | 1 | 25 | 2 | 00:00:01 | | * 2 | INDEX RANGE SCAN | SPM_TEST_TAB_IDX | 1 | | 1 | 00:00:01 | --------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 2 - access("ID"=99) --------------------------------------------------------------------------------------------- Object ID : 3 Test Plan Name : SQL_PLAN_7qxjk7bch8h5t3652c362 Base Plan Name : SQL_PLAN_7qxjk7bch8h5tb65c37c8 SQL Handle : SQL_7b76323ad90440b9 Parsing Schema : SYS Test Plan Creator : SYS SQL Text : SELECT description FROM spm_test_tab WHERE id = 99 Execution Statistics: ----------------------------- Base Plan Test Plan ---------------------------- ---------------------------- Elapsed Time (s): .000009 .000001 CPU Time (s): 0 0 Buffer Gets: 4 0 Optimizer Cost: 13 2 Disk Reads: 0 0 Direct Writes: 0 0 Rows Processed: 0 0 Executions: 10 10 FINDINGS SECTION --------------------------------------------------------------------------------------------- Findings (1): ----------------------------- 1. The plan was verified in 0.00600 seconds. It passed the benefit criterion because its verified performance was 14.66667 times better than that of the baseline plan. Recommendation: ----------------------------- Consider accepting the plan. Execute dbms_spm.accept_sql_plan_baseline(task_name => 'TASK_21', object_id => 3, task_owner => 'SYS'); EXPLAIN PLANS SECTION --------------------------------------------------------------------------------------------- Baseline Plan ----------------------------- Plan Id : 103 Plan Hash Value : 3059496904 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 13 | 00:00:01 | | * 1 | TABLE ACCESS FULL | SPM_TEST_TAB | 1 | 25 | 13 | 00:00:01 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter("ID"=99) Test Plan ----------------------------- Plan Id : 104 Plan Hash Value : 911393634 --------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 2 | 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED | SPM_TEST_TAB | 1 | 25 | 2 | 00:00:01 | | * 2 | INDEX RANGE SCAN | SPM_TEST_TAB_IDX | 1 | | 1 | 00:00:01 | --------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 2 - access("ID"=99) --------------------------------------------------------------------------------------------- 3.5 接受新的进化任务如果演进任务已完成并已报告建议,建议使用 ACCEPT_SQL_PLAN_BASELINE,但您应该真正使用 IMPLEMENT_EVOLVE_TASK。
SET SERVEROUTPUT ON DECLARE l_return NUMBER; BEGIN l_return := DBMS_SPM.implement_evolve_task(task_name => 'TASK_21'); DBMS_OUTPUT.put_line('Plans Accepted: ' || l_return); END; / Plans Accepted: 2 PL/SQL procedure successfully completed. SQL>DBA_SQL_PLAN_BASELINES 视图显示第二个计划已被接受。
CONN sys/pdb1 AS SYSDBA SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines WHERE sql_handle = 'SQL_7b76323ad90440b9'; SQL_HANDLE PLAN_NAME ENA ACC -------------------- ------------------------------ --- --- SQL_7b76323ad90440b9 SQL_PLAN_7qxjk7bch8h5t3652c362 YES YES SQL_7b76323ad90440b9 SQL_PLAN_7qxjk7bch8h5tb65c37c8 YES YES SQL>重复之前的测试表明,现在可以使用更高效的计划。
CONN test/test@pdb1 SET AUTOTRACE TRACE LINESIZE 130 SELECT description FROM spm_test_tab WHERE id = 99; Execution Plan ---------------------------------------------------------- Plan hash value: 2338891031 -------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| SPM_TEST_TAB | 1 | 25 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | SPM_TEST_TAB_IDX | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=99) Note ----- - SQL plan baseline "SQL_PLAN_7qxjk7bch8h5t3652c362" used for this statement 3.6 删除计划如果您想删除计划,使用 DROP_SQL_PLAN_BASELINE 函数将其丢弃。
CONN sys@pdb1 AS SYSDBA SET SERVEROUTPUT ON DECLARE l_plans_dropped PLS_INTEGER; BEGIN l_plans_dropped := DBMS_SPM.drop_sql_plan_baseline (sql_handle => 'SQL_7b76323ad90440b9'); DBMS_OUTPUT.put_line('Plans Dropped: ' || l_plans_dropped); END; / Plans Dropped: 2 PL/SQL procedure successfully completed. SQL>查看执行计划
SET AUTOTRACE TRACE LINESIZE 130 SELECT description FROM spm_test_tab WHERE id = 99; Execution Plan ---------------------------------------------------------- Plan hash value: 2338891031 -------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| SPM_TEST_TAB | 1 | 25 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | SPM_TEST_TAB_IDX | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=99)通过这种方式,管理员能够细致地控制哪些计划被采纳以及何时采纳,从而确保数据库性能始终处于最优状态。
三、总结综上所述,Oracle 12c中的自适应SQL计划管理不仅增强了数据库自我优化的能力,还提供了足够的灵活性让管理员根据具体需求进行定制化配置。这对于保持复杂环境下的高效运行至关重要。
自适应SQL计划管理(AdaptiveSQLPlanManagement)在Oracle12c中的应用由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“自适应SQL计划管理(AdaptiveSQLPlanManagement)在Oracle12c中的应用”
下一篇
Rust配置笔记