Oracle 、Oceanbase、GoldenDB数据库比较系列(二十四):sql profile/ outline 影响范围(上)
在关系数据库性能优化技术中,有时存在数据库优化器评估问题,产生了错误的执行计划或执行计划改变,导致SQL执行效率变差,在无法或短时间内修改应用SQL代码时,需要从数据库端快速固定指定的执行计划,在oracle中相关技术有outline, sql profile, sql baseline, sql patch 等技术,可以实现不接触应用代码达到固定执行计划的目的,在国产库中如Oceanbase同样支持outline 用于固定执行计划。但是和Oracle在作用域还是存在差异。
2个场景:
- 多个用户多个table(含索引)相同结构,对于相同的SQL文本,是否会互相影响?
- 多个用户相同的table, 对于相同的SQL文本,是否会互相影响?
因为篇幅原因,包含测试用例,分成2篇,本篇记录Oracle,后面记录Oceanbase. GoldenDB
准备测试数据
# create user anbob and weejar SQL> create table weejar.test_profile(id int,name varchar2(50)); Table created. SQL> create table anbob.test_profile(id int,name varchar2(50)); Table created. SQL> insert into weejar.test_profile select rownum,'weejar'||rownum from xmltable('1 to 10000'); 10000 rows created. SQL> insert into anbob.test_profile select 1,'weejar'||rownum from dual connect by rownum<=10000; --- all of ID value is 1 10000 rows created. SQL> create index anbob.idx_test_profile on anbob.test_profile(id); Index created. SQL> create index anbob.idx_test_profile on anbob.test_profile(id); Index created. SQL> create index weejar.idx_test_profile on weejar.test_profile(id); Index created.
Note:
创建了2个用户, 分别每个用户下1张表,id加索引,但是weejar下的表id NDK 10000, 而ANBOB下的表ID NDK 1, 大差异的选择率,后面我们对比full table scan和index scan。
测试执行计划
# session anbob $sqlplus anbob/anbob@172.20.23.85/rac11g SQL> @gts TEST_PROFILE Gather Table Statistics for table TEST_PROFILE... PL/SQL procedure successfully completed. SQL> explain plan for select * from test_profile where id=1; Explained. SQL> @x2 PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------------------- Plan hash value: 4176284699 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10000 | 136K| 9 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| TEST_PROFILE | 10000 | 136K| 9 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"=1) 13 rows selected. # session weejar SQL> @gts test_profile Gather Table Statistics for table test_profile... PL/SQL procedure successfully completed. SQL> explain plan for select * from test_profile where id=1; Explained. SQL> @x2 PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------- Plan hash value: 3044072948 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 15 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_PROFILE | 1 | 15 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_TEST_PROFILE | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) 14 rows selected.
# weejar
SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar1 SQL> @sqlt anbob_profile HASH_VALUE SQL_ID CHLD# OPT_MODE SQL_TEXT ---------- -------------------------- ---------- -------------------- ---------------------------------------------------------------------------------------------------- 3678309713 d43p4szdmx3aj 0 ALL_ROWS select /*anbob_profile*/ * from test_profile where id=1 # anbob SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar776 1 weejar777 ... SQL> @sqlt anbob_profile HASH_VALUE SQL_ID CHLD# OPT_MODE SQL_TEXT ---------- -------------------------- ---------- -------------------- ---------------------------------------------------------------------------------------------------- 3678309713 d43p4szdmx3aj 0 ALL_ROWS select /*anbob_profile*/ * from test_profile where id=1 3678309713 d43p4szdmx3aj 1 ALL_ROWS select /*anbob_profile*/ * from test_profile where id=1 SQL> select sql_id,SQL_TEXT,EXECUTIONS,PARSING_SCHEMA_NAME,CHILD_NUMBER,PLAN_HASH_VALUE,EXACT_MATCHING_SIGNATURE,FORCE_MATCHING_SIGNATURE from v$sql where sql_id='d43p4szdmx3aj' SQL_ID SQL_TEXT EXECUTIONS PARSING_SCHEMA_ CHILD_NUMBER PLAN_HASH_VALUE EXACT_MATCHING_SIGNATURE FORCE_MATCHING_SIGNATURE -------------------------- ------------------------------------------------------------ ---------- --------------- ------------ ------------------------------ ------------------------------ ------------------------------ d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 WEEJAR 0 3044072948 3974185871915606149 3656018408532075703 d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 ANBOB 1 4176284699 3974185871915606149 3656018408532075703
Note:
和预期一样,因为选择率问题,两个用户的相同SQL, 1个SQL ID 两个sql cursor, 两个执行计划。如果我们把使用索引的用户下的sql 固定执行计划,当前全表扫的SQL是否也会受到影响?
SQL profile绑定执行计划
SQL> @coe d43p4szdmx3aj 3044072948 Parameter 1: SQL_ID (required) PLAN_HASH_VALUE AVG_ET_SECS --------------- ----------- 3044072948 .002 4176284699 .005 Parameter 2: PLAN_HASH_VALUE (required) Values passed: ~~~~~~~~~~~~~ SQL_ID : "d43p4szdmx3aj" PLAN_HASH_VALUE: "3044072948" Execute coe_xfr_sql_profile_d43p4szdmx3aj_3044072948.sql on TARGET system in order to create a custom SQL Profile with plan 3044072948 linked to adjusted sql_text. COE_XFR_SQL_PROFILE completed. SQL>@coe_xfr_sql_profile_d43p4szdmx3aj_3044072948.sql ... SQL>REM SQL>VAR signature NUMBER; SQL>REM SQL>DECLARE 2 sql_txt CLOB; 3 h SYS.SQLPROF_ATTR; 4 BEGIN 5 sql_txt := q'[ 6 select /*anbob_profile*/ * from test_profile where id=1 7 ]'; 8 h := SYS.SQLPROF_ATTR( 9 q'[BEGIN_OUTLINE_DATA]', 10 q'[IGNORE_OPTIM_EMBEDDED_HINTS]', 11 q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]', 12 q'[DB_VERSION('11.2.0.4')]', 13 q'[ALL_ROWS]', 14 q'[OUTLINE_LEAF(@"SEL$1")]', 15 q'[INDEX_RS_ASC(@"SEL$1" "TEST_PROFILE"@"SEL$1" ("TEST_PROFILE"."ID"))]', 16 q'[END_OUTLINE_DATA]'); 17 :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt); 18 DBMS_SQLTUNE.IMPORT_SQL_PROFILE ( 19 sql_text => sql_txt, 20 profile => h, 21 name => 'coe_d43p4szdmx3aj_3044072948', 22 description => 'coe d43p4szdmx3aj 3044072948 '||:signature||'', 23 category => 'DEFAULT', 24 validate => TRUE, 25 replace => TRUE, 26 force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ ); 27 END; 28 / PL/SQL procedure successfully completed.
# weejar
SQL>select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar1 SQL>@x2 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------ Plan hash value: 3044072948 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 15 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_PROFILE | 1 | 15 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_TEST_PROFILE | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) Note ----- - SQL profile "coe_d43p4szdmx3aj_3044072948" used for this statement SQL> select NAME,category, status, sql_text, force_matching,SIGNATURE from dba_sql_profiles; NAME CATEGORY STATUS SQL_TEXT FORCE_ SIGNATURE ------------------------------------------------------------ --------------- ---------------- ---------------------------------------------------------------------- ------ ------------------------------------------------- coe_d43p4szdmx3aj_3044072948 DEFAULT ENABLED NO 3974185871915606149 z_bmvxw9vnys273_1270443855 DEFAULT ENABLED select '--' pdb, t.tablespace_name, t.totalmb, t.totals, YES 7807399389790495497 coe_adx0bqa0kb9tp_3956160932 DEFAULT ENABLED select /*+ FULL(emp) */ ename,empno from scott.emp where empno=7788 YES 15426704112737772199 SQL> @sql_profile_hints coe_d43p4szdmx3aj_3044072948 OUTLINE_HINTS -------------------------------------------------------------------------------------------------- BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('11.2.0.4') DB_VERSION('11.2.0.4') ALL_ROWS OUTLINE_LEAF(@"SEL$1") INDEX_RS_ASC(@"SEL$1" "TEST_PROFILE"@"SEL$1" ("TEST_PROFILE"."ID")) END_OUTLINE_DATA 8 rows selected. SQL> l 1 SELECT 2 hint outline_hints 3 FROM ( 4 SELECT p.name, p.signature, p.category, ROW_NUMBER() 5 OVER (PARTITION BY d.signature, d.category ORDER BY d.signature) row_num, 6 EXTRACTVALUE(VALUE(t), '/hint') hint 7 FROM 8 sys.sqlobj$data d 9 , dba_sql_profiles p, 10 TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE(d.comp_data), '/outline_data/hint'))) t 11 WHERE 12 d.obj_type = 1 13 AND p.signature = d.signature 14 AND p.category = d.category 15 AND p.name LIKE ('&1')) 16 ORDER BY 17 name 18* , row_num SQL> SQL> select sql_id,SQL_TEXT,EXECUTIONS,PARSING_SCHEMA_NAME,CHILD_NUMBER,SQL_PROFILE,PLAN_HASH_VALUE,EXACT_MATCHING_SIGNATURE,FORCE_MATCHING_SIGNATURE from v$sql where sql_id='d43p4szdmx3aj'; SQL_ID SQL_TEXT EXECUTIONS PARSING_SC CHILD_NUMBER SQL_PROFILE PLAN_HASH_VALUE EXACT_MATCHING_SIGNATURE FORCE_MATCHING_SIGNATURE -------------------------- ------------------------------------------------------------ ---------- ---------- ------------ ------------------------------ --------------- ------------------------- ------------------------- d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 WEEJAR 0 3044072948 3974185871915606149 3656018408532075703 d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 ANBOB 1 4176284699 3974185871915606149 3656018408532075703 d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 2 WEEJAR 2 coe_d43p4szdmx3aj_3044072948 3044072948 3974185871915606149 3656018408532075703
# anbob
SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar776 1 weejar777 1 weejar778 1 weejar779 ... SQL> @x2 PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------- Plan hash value: 3044072948 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 10000 | 136K| 62 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_PROFILE | 10000 | 136K| 62 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_TEST_PROFILE | 10000 | | 34 (0)| 00:00:01 | --受影响原来是FULL TABLE SCAN ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) Note ----- - SQL profile "coe_d43p4szdmx3aj_3044072948" used for this statement
Note:
FTS 变成了sql profile固定的 IFS, cost从9增加到了62。
SQL> select sql_id,SQL_TEXT,EXECUTIONS,PARSING_SCHEMA_NAME,CHILD_NUMBER,SQL_PROFILE,PLAN_HASH_VALUE,EXACT_MATCHING_SIGNATURE,FORCE_MATCHING_SIGNATURE from v$sql where sql_id='d43p4szdmx3aj'
SQL_ID SQL_TEXT EXECUTIONS PARSING_SC CHILD_NUMBER SQL_PROFILE PLAN_HASH_VALUE EXACT_MATCHING_SIGNATURE FORCE_MATCHING_SIGNATURE
-------------------------- ------------------------------------------------------------ ---------- ---------- ------------ ------------------------------ --------------- ------------------------- -------------------------
d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 WEEJAR 0 3044072948 3974185871915606149 3656018408532075703
d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 ANBOB 1 4176284699 3974185871915606149 3656018408532075703
d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 2 WEEJAR 2 coe_d43p4szdmx3aj_3044072948 3044072948 3974185871915606149 3656018408532075703
d43p4szdmx3aj select /*anbob_profile*/ * from test_profile where id=1 1 ANBOB 3 coe_d43p4szdmx3aj_3044072948 3044072948 3974185871915606149 3656018408532075703
Note:
可见在Oracle中是基于SQL TEXT,共享SQL, 所以在用户WEEJAR创建了sql profile固定为index scan时,另一个用户是相同的SQL ,也使用了sql profile,新生成了sql cursor,降低了SQL的使用效率。 哪有没有办法可以控制 SQL PROFILE的影响范围呢?
SQL PROFILE 影响范围
不同用户不同表相同SQL,默认创建的SQL PROFILE会互相影响,所以提醒我们不要在数据库中任性创建sql profile,而好心做了错事,影响了过其他用户的SQL. 有没有办法控制SQL PROFILE影响边界呢? 当然有,我相信大多数人可能不知道。这点就是与Ocenabase差异较大的地放。
- 1, 制造差异化
SQL文本或索引名等制造差异化,让SQL HINT失效。
- 2,SQL PROFILE配置CATEGORY
BEGIN DBMS_SQLTUNE.ALTER_SQL_PROFILE ( name => 'coe_d43p4szdmx3aj_3044072948' , attribute_name => 'CATEGORY' , value => 'WEEJAR_TEST' ); END; # anbob SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar776 1 weejar777 SQL> @x2 PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------- Plan hash value: 4176284699 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10000 | 136K| 9 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| TEST_PROFILE | 10000 | 136K| 9 (0)| 00:00:01 | --恢复了full table scan ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"=1) # weejar SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar1 SQL> @x2 PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------- Plan hash value: 3044072948 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 15 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_PROFILE | 1 | 15 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_TEST_PROFILE | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) -- without sql profile SQL> @pd sqltune Show all parameters and session values from x$ksppi/x$ksppcv... NUM N_HEX NAME VALUE DESCRIPTION ---------- ---------- -------------------------------------------------------- ------------------------------ --------------------------------------------------------------------------------------------------- 2299 8FB _enable_automatic_sqltune TRUE Automatic SQL Tuning Advisory enabled parameter 2838 B16 sqltune_category DEFAULT Category qualifier for applying hintsets 2839 B17 _sqltune_category_parsed DEFAULT Parsed category qualifier for applying hintsets SQL> alter session set sqltune_category='WEEJAR_TEST'; Session altered. SQL> select /*anbob_profile*/ * from test_profile where id=1; ID NAME ---------- ---------------------------------------------------------------------------------------------------- 1 weejar1 SQL> @x2 PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------------------------------- Plan hash value: 3044072948 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 15 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_PROFILE | 1 | 15 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_TEST_PROFILE | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) Note ----- - SQL profile "coe_d43p4szdmx3aj_3044072948" used for this statement
Note:
使用sql profile CATEGORY和sqltune_category参数约束了SQL PROFILE的影响范围。
总结
默认oracle 是基于sql text实现的SQL PROFILE时的匹配,这也是oracle设计为的是共享的初衷,同时为了影响范围,引入了category分组,默认为default组。那不同用户相同表相同SQL也就不在演示,也是相同的结果。
对不起,这篇文章暂时关闭评论。