我们知道,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注入到执行计划里非常有效。