达梦执行计划的选择与优化思路

我们知道,SQL的语句执行效率取决于与之匹配执行计划的好坏。执行计划就是数据库引擎对某条SQL语句执行过程的内部规划,路线图。执行前优化器把SQL拆解成若干部分,先执行什么再执行什么。方法的不同可能导致执行效率的天渊之别。这个过程叫SQL硬解析。解析一次,反复使用。

DM8查询优化器采用基于代价的方法。在估计代价时,主要以表达数据分布的统计信息为依据。在大多数情况下,估计的代价都是准确的。但在一些比较特殊的场合,如果缺少统计信息,或统计信息陈旧,或者抽样数据不能很好的反映数据分布时,优化器选择的执行计划可能都不是“最优”的,甚至是很差的执行计划。

大多数情况下重新收集准确的统计信息,问题可以得到解决,但有的时候仍然需要进一步干预,比如通过在SQL中添加HINT等方法生成最优计划。

一、测试环境

1、创建测试表和索引,加载数据

create table test(id int,name varchar(10));
create table test2(id int,name varchar(10));
insert into test select level,'选择性差' from dual connect by level <=100000;
insert into test values(100001,'选择性好');
create index ind_test on test(name);

2、显示测试表的数据分布

SQL> select name,count(*) from test
2   group by name;

行号       NAME     COUNT(*)
---------- -------- --------------------
1          选择性差 100000
2          选择性好 1

二、未有统计信息时的糟糕计划

1、用explain命令显示优化器生成的执行计划

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [2, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500, 52]; scan_type(ASC), IND_TEST(TEST), scan_range['选择性差','选择性差']

执行计划的数据流从下向上传递。其中【2,2500,52】三个数字分别表示估算的操作符代价(毫秒)、处理的记录行数和每行记录的字节数。

该表100001行,通过条件(name='选择性差')实际会筛选出100000行。但由于没有统计信息。优化器无法了解真实情况,只能根据"常识"猜测该字段的选择性为1/40,估算通过等值条件(name='选择性差')可以筛选出2500行数据。该字段上创建有索引,因此采用了索引访问路径(SSEK2)。

该计划的执行流程如下:
(1)SSEK2:搜索IND_TEST索引定位条件值(name='选择性差'),返回相应的ROWID给上级节点。
(2)BLKUP2:用SSEK2节点搜索到的ROWID回表查询其他字段数据,将结果传递给父节点PRJT2。
(3)PRJT2:用于表达式项的计算。本例中该节点什么都不做。
(4)INSERT:将查询子句返回的数据写入test2表。
(5)重复过程(1)—(4)直至SSEK2数据全部取完。

2、disql执行SQL,同时显示真实计划

SQL> set autotrace trace
SQL> insert into test2 select * from test where name='选择性差';
影响行数 100000

1   #INSERT : [0, 0, 0]; table(TEST2), type(select) mpp_opt(0)
2     #PRJT2: [2, 2500->100000, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500->100000, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500->100000, 52]; scan_type(ASC), IND_TEST(TEST)

已用时间: 190.300(毫秒). 执行号:1211.

由上可见,使用了explain命令推荐的执行计划。显示结果与前面explain唯一不同的是“[2, 2500, 52]”变成了“[2, 2500->100000, 52]”,表示处理的记录数估算值是2500,但实际是100000行。我们知道符合条件的数据有100000行。很显然这是一个糟糕的执行计划。

3、用ET命令统计执行过程

SQL> sf_set_session_para_value('MONITOR_SQL_EXEC',1);
DMSQL 过程已成功完成

已用时间: 0.830(毫秒). 执行号:1212.
SQL> et(1211);

行号       OP      TIME(US)             PERCENT RANK                 SEQ         N_ENTER     MEM_USED(KB)
---------- ------- -------------------- ------- -------------------- ----------- ----------- --------------------
           DISK_USED(KB)        HASH_USED_CELLS      HASH_CONFLICT        DHASH3_USED_CELLS DHASH3_CONFLICT
           -------------------- -------------------- -------------------- ----------------- ---------------
1          DLCK    3                    0%      5                    0           2           0
           0                    0                    0                    NULL              NULL

2          PRJT2   122                  0.06%   4                    2           260         0
           0                    0                    0                    NULL              NULL

3          SSEK2   14245                7.50%   3                    4           130         0
           0                    0                    0                    NULL              NULL

行号       OP      TIME(US)             PERCENT RANK                 SEQ         N_ENTER     MEM_USED(KB)
---------- ------- -------------------- ------- -------------------- ----------- ----------- --------------------
           DISK_USED(KB)        HASH_USED_CELLS      HASH_CONFLICT        DHASH3_USED_CELLS DHASH3_CONFLICT
           -------------------- -------------------- -------------------- ----------------- ---------------
4          INSERT2 28238                14.87%  2                    1           131         0
           0                    0                    0                    NULL              NULL

5          BLKUP2  147269               77.56%  1                    3           260         0
           0                    0                    0                    NULL              NULL

绝大部分时间都用在了BLKUP2(回表)操作上。

我们知道当查询的数据多时,往往整块读的全表扫描效率更高,不用再回表每行数据。

三、调整执行计划

1、通过收集统计信息改变执行计划

1)收集统计信息

SQL> dbms_stats.gather_TABLE_stats('SYSDBA','TEST',NULL,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
DMSQL 过程已成功完成

2)查询统计信息

SQL> dbms_stats.table_stats_show('SYSDBA','TEST');

行号       NUM_ROWS             LEAF_BLOCKS          LEAF_USED_BLOCKS
---------- -------------------- -------------------- --------------------
1          100001               464                  459

SQL> dbms_stats.column_stats_show('SYSDBA','TEST','NAME');

行号       NUM_DISTINCT         LOW_VALUE HIGH_VALUE NUM_NULLS            NUM_BUCKETS SAMPLE_SIZE          HISTOGRAM
---------- -------------------- --------- ---------- -------------------- ----------- -------------------- ---------
1          2                    选择性差  选择性好   0                    2           100001               FREQUENCY

SQL>  dbms_stats.index_stats_show('SYSDBA','IND_TEST');

行号       BLEVEL      LEAF_BLOCKS          DISTINCT_KEYS        CLUSTERING_FACTOR NUM_ROWS
---------- ----------- -------------------- -------------------- ----------------- --------------------
           SAMPLE_SIZE
           --------------------
1          2           848                  2                    0                 100001
           100001

可以看出数据字典内已经登记了该表的数据分布情况。

3)用explain命令显示执行计划

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [12, 100000, 52]; exp_num(2), is_atom(FALSE)
3       #SLCT2: [12, 100000, 52]; TEST.NAME = '选择性差'
4         #CSCN2: [12, 100001, 52]; INDEX33558522(TEST)

 由上可见,查询优化器根据统计信息推荐了新的执行计划。不再使用二级索引IND_TEST了。

该计划的大致执行流程如下:

(1)CSCN2:通过聚集索引对test表进行全表扫描,每行数据传递上级节点。
(2)SLCT2:根据查询条件(name='选择性差')筛选出符合的数据传递给上级节点。
(3)PRJT2:用于表达式项的计算。本例中该节点什么都不做。
(4)INSERT:将查询子句返回的数据写入test2表。
重复过程(1)—(4)直至CSCN2数据全部取完。

说明:达梦默认创建索引组织表,每个表都有一个唯一聚簇索引(clustered index ),除此之外的非聚簇索引叫二级索引(secondary indexes)。因此达梦数据库执行计划中不再有ORACLE中常见的“全表扫描”,代之为CSCN2,扫描聚簇索引。

4)disql执行语句并显示真实计划

SQL> insert into test2 select * from test where name='选择性差';
影响行数 100000

1   #INSERT : [0, 0, 0]; table(TEST2), type(select) mpp_opt(0)
2     #PRJT2: [2, 2500->100000, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500->100000, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500->100000, 52]; scan_type(ASC), IND_TEST(TEST)

您可能惊讶的是执行计划还是收集统计信息之前的那个,并没有采用最新EXPLAIN命令推荐的。究其原因,数据库为了缩短总体执行时间只进行一次硬解析,第一次解析生成的计划会被缓存到内存中。下次同样的SQL执行将重用缓存中的计划。

怎么办?

方法是清除旧的执行计划,再一次硬解析创建新的执行计划。

2、通过清除缓存改变执行计划

上一节提到收集统计信息可以改变执行计划,但如果该SQL以前执行过,系统缓存了执行计划,新执行计划不能启用。这时需要先清除缓存。

1)查询内存中执行计划的CACHE_ITEM

根据sql语句文本在v$cachepln视图内搜索对应的cache_item

SQL> select cache_item,sqlstr from v$cachepln
2   where sqlstr like '%insert into test2 select%' and sqlstr not like '%cachepln%';

行号       CACHE_ITEM           SQLSTR
---------- -------------------- -----------------------------------------------------------
1          2011994272           insert into test2 select * from test where name='选择性差';

2)清除缓存的执行计划

SQL> sp_clear_plan_cache(2011994272);
DMSQL 过程已成功完成

SQL> select cache_item,sqlstr from v$cachepln
2   where sqlstr like '%insert into test2 select%' and sqlstr not like '%cachepln%';
未选定行

清除执行计划除了采用sp_clear_plan_cache过程以外。还可以在表上执行一个DDL语句,涉及的SQL的执行计划自动被清除。

3)生成新的执行计划

清除完某SQL缓存的执行计划后,下一次执行时会根据当前的统计信息硬解析,生成系统认为最优的计划。本测试在disql中执行,同时显示执行计划。

SQL> insert into test2 select * from test where name='选择性差';
影响行数 100000

1   #INSERT : [0, 0, 0]; table(TEST2), type(select) mpp_opt(0)
2     #PRJT2: [12, 100000->100000, 52]; exp_num(2), is_atom(FALSE)
3       #SLCT2: [12, 100000->100000, 52];
4         #CSCN2: [12, 100001->100001, 52]; INDEX33558522(TEST)

已用时间: 44.678(毫秒). 执行号:1224.

由上可见,执行计划已经与前面explain推荐相同了。对比上一次执行时间从193毫秒大幅缩短到44.678毫秒。

4)使用DBMS_SQLTUNE包查询执行计划

使用DBMS_SQLTUNE.REPORT_SQL_MONITOR方法可以查看上述执行号为1224的执行计划详细信息。

SQL> select dbms_sqltune.report_sql_monitor(sql_exec_id=>1224);

行号       DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_EXEC_ID=>1224)
---------- -----------------------------------------------------------------------------------------------------------
1          SQL Monitoring Report

SQL Text
------------------------------
insert into test2 select * from test where name='选择性差';

Global Information
------------------------------
 Status                                 :  DONE (ALL ROWS)
 Session                                :  SYSDBA (2051051832:153)
 SQL ID                                 :  1355
 SQL Execution ID                       :  1224
 Execution Started                      :  2022-12-20 15:46:12
 Duration                               :  0.034433s
 Program                                :  DIsql.exe

Global Stats
=========================================================
| Affected |  Bytes   | Bytes |  Physical  |  Logical   |
|   Rows   | Allocate | Free  | Read(page) | Read(page) |
=========================================================
|   100000 |        0 |     0 |     100000 |       7674 |
=========================================================

SQL Plan
------------------------------
1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [12, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #SLCT2: [12, 2500, 52]; TEST.NAME = '选择性差'
4         #CSCN2: [12, 100001, 52]; INDEX33561563(TEST)

SQL Plan Monitoring Details
====================================================================================================================
| Id | Operation | Name  |  Rows   | Cost  |   Time    |   Start   | Execs |   Rows   | Activity | Activity Detail |
|    |           |       | (Estim) |       | Active(s) |  Active   |       | (Actual) |   (%)    |   (# samples)   |
====================================================================================================================
|  0 | DLCK      |       |         |       |  0.000002 | +0.034338 |     2 |          |     0.01 |                 |
|  1 | INSERT2   | TEST2 |         |       |  0.027380 | +0.034334 |   102 |          |    79.98 |                 |
|  2 | PRJT2     |       |    2500 |    12 |  0.000052 | +0.034334 |   202 |   100000 |     0.15 |                 |
|  3 | SLCT2     |       |    2500 |    12 |  0.001136 | +0.034333 |   203 |   100000 |     3.32 |                 |
|  4 | CSCN2     | TEST  |  100001 |    12 |  0.005665 | +0.034333 |   102 |   100001 |    16.55 |                 |
====================================================================================================================

3、通过在SQL中添加HINT改变执行计划

其实很多时候开发人员和用户对于数据分布是很清楚的,他们往往知道SQL语句按照哪种方法执行会更快。在这种情况下,用户可以有一种方法,指示优化器按照固定的方法选择SQL的执行计划。DM8把这种热工干预优化器的方法称为HINT。

1)未收集统计信息时的执行计划

先清除统计信息。回到没有统计信息的状态。

DBMS_STATS.DELETE_TABLE_STATS('SYSDBA','TEST');

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [2, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500, 52]; scan_type(ASC), IND_TEST(TEST), scan_range['选择性差','选择性差']

2)添加HINT

SQL> explain insert /*+ NO_INDEX(TEST,IND_TEST) */ into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [12, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #SLCT2: [12, 2500, 52]; TEST.NAME = '选择性差'
4         #CSCN2: [12, 100001, 52]; INDEX33561563(TEST)

可见执行计划放弃了索引路径。

4、HINT注入

在投产系统中遇到SQL性能问题,有时通过DBA分析后需要加hint解决,但这个时候又由于某种原因无法立即更新代码。这时通过SF_INJECT_HINT方法把hint注入到执行计划里非常有效。官方文档(《DM8_SQL语言使用手册》668页)对该功能的描述如下:

提供无需修改SQL语句但依然能按照指定的HINT运行语句的相关功能。使用时有以下限制:

  • INI 参数 ENABLE_INJECT_HINT 需设置为 1
  • SQL 只能是语法正确的增删改查语句;
  • SQL 会经过系统格式化,格式化之后的 SQL 和指定的规则名称必须全局唯一;
  • HINT 一指定,则全局生效;
  • 系统检查 SQL 匹配时,必须是整条语句完全匹配,不能是语句中子查询匹配;
  • 可通过 SYSINJECTHINT 视图查看已指定的 SQL 语句和对应的 HINT

1)SF_INJECT_HINT注入

SF_INJECT_HINT('insert into test2 select * from test where name=''选择性差''',
'NO_INDEX(TEST,IND_TEST)',
'HINT_1219',
'避免test表走索引',
TRUE,
TRUE
);

参数说明:

第一个参数:数据库有性能问题的语句,这个语句贴到函数中,请勿格式化,也不要前后带有空格,否则不匹配;
第二个参数:NO_INDEX(TEST,IND_TEST),这里填写需要新增的hint,无需加/*+ */这类标识符;
第三个参数:HINT_1219只是一个名称;
第四个参数:注解;
第五个参数:是否理解生效
第六个参数:匹配规则为精准匹配或模糊匹配,设置NULL和TRUE是模糊匹配,FALSE 或缺省时精确匹配;

2)可通过SYSINJECTHINT视图查看已添加的HINT规则

SQL> select name,description,validate,sql_text,hint_text
2   from sysinjecthint;

行号       NAME      DESCRIPTION      VALIDATE SQL_TEXT
---------- --------- ---------------- -------- ----------------------------------------------------------
           HINT_TEXT
           -----------------------
1          HINT_1219 避免test表走索引 TRUE     insert into test2 select * from test where name='选择性差'
           NO_INDEX(TEST,IND_TEST)

3)显示新的执行计划

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [12, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #SLCT2: [12, 2500, 52]; TEST.NAME = '选择性差'
4         #CSCN2: [12, 100001, 52]; INDEX33561563(TEST)

可见没有修改SQL语句,执行计划也修改了,放弃索引路径。

4)使用SF_ALTER_HINT禁用指定的HINT规则

SQL> SF_ALTER_HINT('HINT_1219','STATUS','DISABLED');
DMSQL 过程已成功完成
已用时间: 3.667(毫秒). 执行号:2108.

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [2, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500, 52]; scan_type(ASC), IND_TEST(TEST), scan_range['选择性差','选择性差']

执行计划又恢复到了最初的样子

再查看SYSINJECTHINT视图,显示VALIDATE为FALSE

SQL> select name,description,validate,sql_text,hint_text
2   from sysinjecthint;

行号       NAME      DESCRIPTION      VALIDATE SQL_TEXT
---------- --------- ---------------- -------- ----------------------------------------------------------
           HINT_TEXT
           -----------------------
1          HINT_1219 避免test表走索引 FALSE    insert into test2 select * from test where name='选择性差'
           NO_INDEX(TEST,IND_TEST)

4)使用SF_ALTER_HINT启用指定的HINT规则

SF_ALTER_HINT('HINT_1219','STATUS','ENABLED');

5)使用SF_DEINJECT_HINT 删除 SQL增加的HINT规则

SQL> SF_DEINJECT_HINT('HINT_1219');
DMSQL 过程已成功完成
已用时间: 2.074(毫秒). 执行号:2110.

SQL> select * from sysinjecthint;
未选定行
已用时间: 1.581(毫秒). 执行号:2111.

SQL> explain insert into test2 select * from test where name='选择性差';

1   #INSERT : [0, 0, 0]; table(TEST2), type(select), hp_opt(0), mpp_opt(0)
2     #PRJT2: [2, 2500, 52]; exp_num(2), is_atom(FALSE)
3       #BLKUP2: [2, 2500, 52]; IND_TEST(TEST)
4         #SSEK2: [2, 2500, 52]; scan_type(ASC), IND_TEST(TEST), scan_range['选择性差','选择性差']
已用时间: 0.828(毫秒). 执行号:0.

四、执行计划使用简介

执行计划顺序:
各计划节点的执行顺序为:缩进越多的越先执行,同样缩进的上面的先执行,下面的后执行,上下的优先级高于内外。
缩进最深的,最先执行;缩进深度相同的,先上后下。
口诀:最右最上先执行


常见操作符:
BLKUP2 二次扫描(回表)
CSCN2 聚簇索引扫描(全表扫描)
HASH2 INNER JOIN hash 内连接
NEST LOOP FULL JOIN2 嵌套连接
MERGE INNER JOIN3 归并连接
NSET2 结果集
PRJT2 投影
SSCN 二级索引扫描
SSEK2 二级索引数据定位
CSEK2 聚簇索引定位
SLCT:选择,用于查询条件的过滤
AAGR:简单聚集,用于没有GROUP BY的COUNT、SUM等聚集函数的计算;

五、SQL优化思路浅析

1、定位最慢语句

1)显示系统最慢语句

最简单的方法是查询v$long_exec_sqls和v$system_long_exec_sqls视图,查看执行超过1000毫秒的语句。通过call sp_set_long_time()可以改变监控的最短执行时间阀值。

2)最近一个小时已经执行过的最慢语句TOP20

有的时候某条SQL执行时间很短,毫秒级,收录不到v$long_exec_sqls视图里。但执行次数多,对系统造成的影响很大。这样的SQL应该优先进行优化。下面的语句显示最近一个小时内累计执行时间最多的SQL,统计SQL执行次数,单次执行时间,累计执行时间。

#统计最近1小时内,累计执行时间最多的20个SQL ID
select sql_id,substr(top_sql_text,1,50) sql_text,sum(TIME_USED),count(*) executions, sum(TIME_USED)/count(*) time_per_exec
from v$sql_history 
where start_time >sysdate-1.0/24
group by sql_id,substr(top_sql_text,1,50) order by 3 desc limit 20;

#注意,当系统中的语句使用了绑定变量时,无论带入的变量值怎么变,SQL ID都不变。

#当sql语句没有使用绑定变量时,由于每次条件不同,SQL ID也不同。那么统计累计时间最多的语句就不能按照SQL ID分组了。可以假设条件出现在sql的末尾部分,前50个字符不改变:
select substr(top_sql_text,1,50)  sql_text,sum(TIME_USED),count(*) executions, sum(TIME_USED)/count(*) time_per_exec
from v$sql_history 
where start_time >sysdate-1.0/24
group by substr(top_sql_text,1,50) order by 3 desc limit 20;

3)当前正在执行的最慢语句TOP20

v$long_exec_sqls、v$system_long_exec_sqls还有v$sql_history都只能显示已经执行完的语句。如果某条语句一直没有执行完,则无法统计到。这时就需要下面的语句:

select clnt_ip,sec_to_time(datediff(ss,last_send_time,sysdate)) elapsed,appname,user_name,RUN_STATUS,sql_id,sql_text,last_send_time
from v$sessions where state in ('ACTIVE','WAIT')
order by elapsed desc
limit 20

4)统计最慢的SQL执行节点

with SQL_HISTORY
as
(
select a.*
from v$sql_history a,v$session_history b
where a.START_TIME > sysdate - 1.0/24
and a.sess_id=b.sess_id and a.sess_seq=b.sess_seq
and b.user_name='SYSDBA' 
)
select a.sql_id,substr(a.top_sql_text,1,35)  || decode(substr(a.top_sql_text,36,1),'','',' .....') sql_text,
a.name,node_time_used/1000000.0 node_time,
a.execs,sql_time_used/1000000.0 sql_time,b.execs,
round(node_time_used*100.0/sql_time_used,2) "ratio %"
from
(select sql_id,top_sql_text,name,sum(b.time_used) node_time_used,count(*) execs
from SQL_HISTORY a,v$sql_node_history b,v$sql_node_name c
where  a.exec_id=b.exec_id and b.type$=c.type$
group by sql_id,top_sql_text,name
) a,
(
select sql_id,top_sql_text,sum(time_used) sql_time_used,count(*) execs
from SQL_HISTORY
group by sql_id,top_sql_text
) b
where a.sql_id=b.sql_id 
order by 4 desc 
limit 20

显示结果大致如下:

如上图所示:

18号SQL(select * from t3 where id=1;)最近一个小时共执行了3次,累计执行164秒。该语句内CSCN2累计执行了162.8秒,占SQL总时间的99.23%。

177号SQL (select * from t2 where id=2;)最近一个小时共执行了1次,累计执行29.246秒。该语句内CSCN2累计执行了29.15秒,占SQL总时间的99.68%。

18号SQL语句内SLCT2累计执行了1.149秒,占SQL总时间的0.7%。

2、EXPLAN推荐与当前执行计划对比

当前执行计划可以在disql中通过先设置set autotrace trace而后执行语句获得。

如果是DML命令,或者查询语句返回数据太多无法手动执行的话,可以通过alter session set events 'immediate trace name plndump level 6591105184,dump_file ''d:/1.log'''命令把缓存中的执行计划保存到本地文件中查看。“6591105184”是v$cachepln视图的cache_item字段,根据实际情况修改。“'d:/1.log”根据实际目录修改。

如果EXPLAIN显示的执行计划推荐和缓存中的执行计划不同,则考虑是否需要清除缓存以应用最新的统计信息生成“更优”计划。

3、用ET工具协助分析

用et(执行号,每次执行完会显示;历史语句查找v$sql_history视图的exec_id字段)执行计划中哪部分时间占比最多。通过建索引或修改SQL语法或加HINT生成“更优”计划消除这个性能瓶颈。

使用et之前需要先通过sf_set_session_para_value('MONITOR_SQL_EXEC',1);命令修改会话级参数。

4、HINT注入

在投产系统中遇到SQL性能问题,有时通过DBA分析后需要加hint解决,但这个时候又由于某种原因无法立即更新代。这时通过SF_INJECT_HINT方法把hint注入到执行计划里非常有效。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值