达梦SQL优化
达梦数据库的SQL优化,最重要的是读懂执行计划,绝大多数时候,可以通过添加索引提升查询性能,有时候可能也需要调整下sql的结构,或者是通过hint去优化。
达梦有个sqllog,可以抓应用使用的sql(pachage中的sql抓不到,这个后续再整理),抓出sql后,可以用达梦自己研发的日志分析工具去分析sql,一般大于1s的都算慢sql了,这种sql需要优化一下。
一、抓慢sql
1、开启sql监控
SP_SET_PARA_VALUE(1,'SVR_LOG',1);--开启sql监控 动态参数,修改立即生效。
SP_SET_PARA_VALUE(1,'SVR_LOG',0);--关闭sql监控 动态参数,修改立即生效。
select para_name,para_value from v$dm_ini where para_name='SVR_LOG';--查当前参数值
可以监控是在应用系统还是在数据库操作了哪些sql,哪个ip操作的。
日志文件在达梦数据库安装目录下的log目录下。
日志文件名称一般为 dmsql_实例名称.log,日志文件有多份的情况实例名称后面会加具体时间。
sqllog.ini用于sql日志的配置,当且仅当INI参数SVR_LOG=1时使用。
sqllog.ini 中 ASYNC_FLUSH = 1 表示异步
SQL_TRACE_MASK = 1 表示记录全部DDL DML 类型语句
建议开异步,几个文件循环写,影响5%-10%的性能,所以监控完建议关闭。
如果在服务器启动过程中,修改了sqllog.ini文件。修改之后的文件,只要调用过程SP_REFRESH_SVR_LOG_CONFIG() 就会生效。
2、分析dmsql日志
1)下载DMLOG8.3日志分析工具
链接:https://pan.baidu.com/s/1zqEvxQod6fR-KiCRYDTNXA
提取码:wjph
2)修改dmlog.properties
3)执行
最好会生成一个RESULT_xxx的文件夹,常用的是那两个xls。
一个是根据执行时间排序,另一个是根据执行次数排序。
抓出的sql,一般是?这种,工具也会把参数值替换到sql中,具体可以看excel中的”替换参数后SQL”。
在做sql调优的时候,最好?占位符这种,和定参这种,都测试一下,因为这两种可能走的是不同的执行计划。
二、sql调优常用动态性能视图
1、 查询刷盘( v$mtab_used_history)
参数配置的不合理,资源不够,就会刷盘。
select * from v$mtab_used_history;
该视图如果有记录,说明查询刷盘。可根据op_type$字段,通过v$sql_node_name 确定是哪个操作符导致刷盘。
select sql_text from v$mtab_used_history a, v$sql_node_name b
where a.op_type$=b.type$ ;
最多存1000条,可通过SP_DYNAMIC_VIEW_DATA_CLEAR进行清理;
SP_DYNAMIC_VIEW_DATA_CLEAR('V$MTAB_USED_HISTORY');
OP_TYPE$ 是操作符节点类型
v$sql_node_name 显示所有的 sql 节点描述信息,包括 sql 节点类型、名字和详细描述。
2、定位慢SQL( v$sql_history)
select * from v$sql_history;
查找活动会话慢sql,需要开启monitor监控 :
SP_SET_PARA_VALUE(1,'ENABLE_MONITOR',1);
SP_SET_PARA_VALUE(1,'MONITOR_TIME',1);
select time_used/1000.0/1000.0||'s',top_sql_text, * from v$sql_history
where sess_id in (select sess_id from v$sessions where state='ACTIVE' )
order by time_used desc;
SP_DYNAMIC_VIEW_DATA_CLEAR('V$SQL_HISTORY');-- 清空动态性能视图 V$SQL_HISTORY 的历史数据
--查询已执行超过2s的完整的活动sql
select * from (
select sess_id,sql_text,datediff(ss,last_recv_time,sysdate) Y_EXETIME,
sf_get_session_sql(sess_id) fullsql,clnt_ip
from v$sessions where state='ACTIVE'
) ;
where Y_EXETIME>=2;
注意:sql_history中记录的sql,有长度限制,超过的会被隐藏,可以通过sf_get_session_sql(sess_id)来获取完整sql。
3、hash连接刷盘(v$hash_merge_used_history)
select * from v$hash_merge_used_history;
4、排序刷盘(v$sort_history)
select * from v$sort_history;
当 INI 参数 ENABLE_MONITOR=1 都打开时,显示系统自启动以来使用排序页数最多的50 个操作符信息。
5、查看资源信息 (V$SYSSTAT)
查看资源信息,包括排序和hash的资源使用情况。
select * from V$SYSSTAT;--显示系统统计信息
id 统计对象ID
classid 统计对象所属类别id 1:字典信息 2:sql 3:事务 4:检查点 5:rlog 6:undo 7:IO 8:B树 9:网络 10:文件 11:内存 12:cpu 13:OS 14:缓冲区 15:限流控制 20:其他
select * from v$sysstat where classid=11; --内存相关参数资源使用情况
6、统计信息(v$sysstat)
因为统计信息对于计划影响很大,所以生成了一个好的计划后,就不要更新或者清理统计信息。以免计划的改变。
select * from v$sysstat;--显示系统统计信息
未显式指定采样率,系统根据数据规模指定采样率:
1、数据量越大,采样率越低。
2、对象数据量很大,很占用时间,尽量对连接列和过滤列收集统计信息,不建议采样系统包对表进行收集;指定采样率。
3、sysstats 视图确认该表是否有统计信息。
三、执行计划
执行计划是 SQL 语句的执行方式,由查询优化器为语句设计的执行方式,交给执行器去执行。在 SQL 命令行使用 EXPLAIN 可以打印出语句的执行计划。
重启数据库的话,执行计划会重新生成。
执行计划 ,执行过程为:控制流从上向下传递,数据流从下向上传递。最上面是最外层,里面是内层。
1、常见操作符:
key_num(1) 表示去重。
key_num(0) 表示无需去重。
SPL2 临时表;和 NTTS2 不同的是,它的数据集不向父亲节点传送,而是被编号,用编号和 KEY 来定位访问;而 NTTS2 的数据,主动传递给父亲节点。
PIPE2 管道;先做一遍右儿子,然后执行左儿子,并把左儿子的数据向上送,直到左儿子不再有数据。
exp_num(3) 表达式数量有3个。
is_atom(FALSE) 结果是否要求单行。
blkup2 回表 比如select c2 form c where c1=1; c1上有索引,通过c1先查询出数据,然后再回表去c2 数据量多的话尽量消掉回表。
SAGR2 流分组 select中的max之类的集函数。
NEST LOOP INNER JOIN 嵌套循环连接
NEST LOOP INDEX JOIN 索引连接 NEST LOOP INNER JOIN 和 NEST LOOP INDEX JOIN 代表实现内连接的不同方法。
具体就不在这里说了,之前写过一篇执行计划的博客可以参考下。
https://www.cnblogs.com/wuran222/p/15132004.html
四、收集统计信息
收集统计信息这个,比较常用,比如数据从其他库迁移到达梦,一般要先收集一下统计信息,为了方便,一般是收集全库的统计信息。在针对某个sql做调优的时候,一般是收集用到的列和索引的统计信息。
1、收集全库统计信息
DBMS_STATS.GATHER_SCHEMA_STATS('HNSIMIS', 100,FALSE,'FOR ALL COLUMNS SIZE AUTO');--HNSIMIS是模式名
这种收集方式,并不是100%收集,所以有一定的弊端。
2、收集表的统计信息
dbms_stats.GATHER_TABLE_STATS('TEST','test',null,100);--收集表的统计信息,包括列和索引等 第一个参数是模式,第二个参数是表 Null是缺省,100是采样率100%,默认不是。
3、收集列统计信息
stat 100 on fw(CREATE_USERID); --收集某一列的统计信息 fw是表名 CREATE_USERID是列名称
这个在做sql调优的时候比较常用,因为比较准。
4、收集索引统计信息
sp_index_stat_init('DREAMWEB_PUDONG','IX_FW_ROWSTATE',100); --收集索引的统计信息 DREAMWEB_PUDONG是模式名称 IX_FW_ROWSTATE是索引名称
这个也比较常用,建议创建完索引,立刻收集下统计信息。
5、补充
有时候sql非常复杂,用了大量的表和索引,又不确定统计信息是否准,可以用explain for +SQL的方式,能够显示出用了哪些表和索引,拷贝到excel里,去重一下,针对用到的表和索引去收集下统计信息。
注意:收集统计信息的方式非常多,这里我只整理了自己比较常用的。
五、hint方式优化
hint方式优化有个非常大的缺点,就是后续数据量变化非常大的时候,之前设置的hint可能就不好用了,如果hint是加在代码中的,之后还需要修改代码。所以使用hint一定要慎重,最好能预估后续数据量,方便做出合理的判断。
1、or
常用hint是OPTIMIZER_OR_NBEXP(OR表达式的优化方式,默认值0,动态会话级参数)。
0:不优化(一般是使用UNION FOR OR处理OR表达式,然后通过ROWID去掉重复数据);
1:生成UNION_FOR_OR 操作符时,优化为无 KEY 比较方式(扩展OR条件,加入LNNVL函数,使用UNION ALL方式实现, LNNVL(bool_exp),过滤掉左孩子c1=1的数据);
2:OR 表达式优先考虑整体处理方式;
默认把Or拆分计算每个分支的结果集,然后UNION方式汇总。存在可以使用索引的过滤条件,则性能较好。如果是大数据量表,没有可利用的索引,则反复全表扫描,性能较差。
例一
去重
SELECT /*+OPTIMIZER_OR_NBEXP(1) */ * FROM T1 WHERE C1=1 OR C2=5;
key_num(1) 表示去重
key_num(0) 表示无需去重
expll lnnvl(bool_exp) 过滤掉左孩子c1=1的数据 这样就不用去重了
例二
把Or当作整体计算表达式。
2、指定连接顺序
/*+ order(MM,MS,m,m1)*/
MM等是表的别名。
有些表数据量非常大,可以放在最后处理。
3、like
p.patname like '%'||'杨鑫蔚'||'%
参数如果是常量,可以创建函数索引,达梦会转成position,提高效率。
如果like的参数传动态进来的,无法建函数索引,可以在et里看看慢在哪里,从其他方面去优化。
4、group
分组的时候,如果报错:不是 GROUP BY 表达式,mysql中可以,达梦中报错的, 可以设置GROUP_OPT_FLAG=1,值为1表示非 mysql 兼容模式下(即 COMPATIBLE_MODE 不等于 4),支持查询项不是GROUP BY 表达式。
5、子查询
/*+ENABLE_RQ_TO_NONREF_SPL(1)*/
对查询项中出现的相关子查询表达式进行优化处理
6、层次查询的参数
CNNTB_OPT_FLAG是否使用优化的层次查询执行机制,默认0。
0:不使用;1:强制使用;2:优化器自动决定是否使用;4:层次查询不支持并行;8:检查层次查询的表达式是否包含 ROWNUM,若包含则报错;16:层次查询的条件包含 OR 时仍尝试使用优化,且将OR 连接的条件表达式当做整体处理;32:根据PRIOR关联列的DISTINCT值与统计信息中总行数的比例自适应选择执行机制;64:对于简单条件(如:DUAL.LEVEL<=10)准确估算层次查询行数。支持使用上述有效值的组合值,如 31 表示同时进行 1、2、4、8、16 的优化
注意:in或者not in,内层查询建议加distinct。本例就是,加了distinct查询速度提升很多。
7、NEST LOOP
OUTER_OPT_NLO_FLAG 是否进行外连接、内连接的相关优化,默认值0
OUTER_OPT_NLO_FLAG=0 时,不进行优化;
OUTER_OPT_NLO_FLAG=1 时,若满足下列条件,将外连接转换为嵌套外连接进行优化:
两层嵌套循环结构,有驱动表和被驱动表之分。
选定一张表作为驱动表,遍历驱动表中的每一行,根据连接条件去匹配第二张表中的行。驱动表的行数就是循环的次数,这个很大程度影响了执行效率。
外层循环选取一个,然后从内层循环中查找匹配,返回匹配行
外层循环的行数决定内层循环做扫描多少次,外部循环表的行数要小于内部循环的行数
需注意的问题:
- 选择小表作为驱动表。统计信息尽量准确,保证优化器选对驱动表。
- 大量的随机读。如果没有索引,随机读很致命,每次循环只能读一块,不能读多块。使用索引可以解决这个问题。
使用场景:
- 驱动表有很好的过滤条件
表连接条件能使用索引
结果集比较小
将 OUTER_OPT_NLO_FLAG 置为 1,执行相同的语句。此时将 C.E1 转化为 VAR 并作为T2 表的过滤条件,语句中的 LEFT JOIN 转换为 NEST LOOP LEFT JOIN。
8、哈希连接
哈希连接分为两个阶段:生成和探测阶段,首先是生成阶段
生成阶段:将数据小的表中的每行数据经过散列函数的计算都放到不同的Hash 槽中。
探测阶段,对另一个数据量大的表,同样针对每一行进行散列函数,确定其所应在的Hash 槽,在针对这行和对应Hash槽中的每一行进行匹配,如果匹配则返回对应的行
哈希匹配需要创建HASH表,并涉及到散列函数,所以并发环境下,CPU和内存的消耗会较高,高并发下非必要尽量减少使用hash连接。
Hash连接比较消耗内存,如果系统有很多这种连接时,需调整以下3个参数:
HJ_BUF_GLOBAL_SIZE HJ_BUF_SIZE HJ_BLK_SIZE
还有一个参数hash大小也需要关注:JOIN_HASH_SIZE
并发下通常不建议使用hash连接,因为每次执行都需要创建hash表,使用完后又释放,对性能有损耗。在此类场景下,尽量使用索引连接。
9、子查询
1)相关子查询
子查询中的过滤条件存在外表列和内表列的连接,会影响最终的查询结果。
create table t1(c1 int,c2 int);
create table t2(d1 int,d2 int);
select * from t1 where c1 in(select d1 from t2 where d2=c2);
例1中的子查询中条件为c2=d2,条件列分别属于t1和t2表。查询结果不仅要满足c1=d1,还要满足c2=d2。
2)非相关子查询
子查询不包含外表的列,查询结果由内表决定。
select * from t1 where c1 in(select d1 from t2 where c2<10);
例2中的子查询条件为c2<10,最终结果需要满足条件c1=d1 and c2<10,查询结果不受t1表影响。
3)事例
create table a(a1 int, a2 int); create table b(b1 int, b2 int); create table c(c1 int, c2 int); insert into a select level, level * 10 from dual connect by level <= 100000; insert into b select level, level * 10 from dual connect by level <= 100000; insert into c select level, level * 10 from dual connect by level <= 100000; commit; select /*+ENABLE_RQ_TO_NONREF_SPL(0)*/ count(*) from ( select ( select count(*) from a, b where a1 = b1 and a2 = c1 ) x from c ) tmpt where x > 0;
ENABLE_RQ_TO_NONREF_SPL(0) 坦化方式处理相关查询表达式
ENABLE_RQ_TO_NONREF_SPL(1) 一行一行处理相关查询表达式 相关查询表达式转化为非相关查询表达式
select ( select count(*) from a, b where a1 = b1 and a2 = c1) x from c
这是个相关子查询,里层连接查询的查询条件,用到了外层表c的字段
优化方式是,相关查询表达式转化为非相关查询表达式,c1当成常量传给a2用来查询
当走到执行计划5的时候,触发SPL2(临时表)
C表数据量不是特别大的时候,这种方式查询速度快
4)派生表
派生表可以当作视图来优化
优化前是先全表扫描,再根据条件查询,所有distinct,但是实际上distinct查询出的结果集非常少,完全可以先执行内层查询,让视图条件不下放
10、视图
1)普通视图
--视图定义 create or replace view v_t1 as select t1.id+t2.id as c11, t2.name,t1.id from t1,t2 where t1.id=t2.id; --定义体计划 1 #NSET2: [21, 100000, 56] 2 #PRJT2: [21, 100000, 56]; exp_num(3), is_atom(FALSE) 3 #HASH2 INNER JOIN: [21, 100000, 56]; KEY_NUM(1); KEY(T1.ID=T2.ID) KEY_NULL_EQU(0) 4 #CSCN2: [1, 10000, 4]; INDEX33556127(T1) 5 #CSCN2: [11, 100000, 52]; INDEX33556128(T2) --原始sql select a.c11,b.name from v_t1 a,t1 b where a.id=b.id and a.id=100; 1 #NSET2: [54, 2500, 108] 2 #PRJT2: [54, 2500, 108]; exp_num(2), is_atom(FALSE) 3 #NEST LOOP INNER JOIN2: [54, 2500, 108]; 4 #SLCT2: [1, 1, 52]; B.ID = 100 5 #CSCN2: [1, 10000, 52]; INDEX33556127(T1 as B) 6 #PRJT2: [26, 2500, 56]; exp_num(1), is_atom(FALSE) 7 #NEST LOOP INNER JOIN2: [26, 2500, 56]; 8 #SLCT2: [1, 1, 4]; T1.ID = 100 9 #CSCN2: [1, 10000, 4]; INDEX33556127(T1) 10 #SLCT2: [12, 2500, 52]; T2.ID = 100 11 #CSCN2: [12, 100000, 52]; INDEX33556128(T2)
8和10 视图外的查询条件进行下压到具体视图内部表,减少结果集
关注参数:
VIEW_FILTER_MERGING:指定是否对视图条件进行优化以及如何优化
VIEW_PULLUP_FLAG:是否对视图进行上拉优化,将视图转化为其原始定义
complex_view_merging:是否对视图进行合并
视图经常遇到,查询条件不下放的问题
VIEW_FILTER_MERGING默认138,如果视图定义体中有max之类的集函数,会遇到这种问题
8:如果派生表存在集函数,则不进行条件下推优化;
解决方法是不开8,值设置位130
2)派生表
派生表可以当作视图来优化
优化前是先全表扫描,再根据条件查询,所有distinct,但是实际上distinct查询出的结果集非常少,完全可以先执行内层查询,让视图条件不下放
11、exist
IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况
create table tt1(c1 int, c2 int); create table tt2(d1 int, d2 int); insert into tt1 select level, level * 10 from dual connect by level < 100000; insert into tt2 select level, level * 10 from dual connect by level < 100; select /*+REFED_EXISTS_OPT_FLAG(0)*/* from tt1 where exists (select * from tt2 where c1 = d1); 1 #NSET2: [18, 32999, 16] 2 #PRJT2: [18, 32999, 16]; exp_num(3), is_atom(FALSE) 3 #HASH RIGHT SEMI JOIN2: [18, 32999, 16]; n_keys(1) KEY(TT2.D1=TT1.C1) KEY_NULL_EQU(0) hash右半连接 4 #CSCN2: [1, 99, 4]; INDEX33556133(TT2) 5 #CSCN2: [10, 99999, 16]; INDEX33556132(TT1) ------------尝试在tt1上创建IDX_IDX1索引 在右表建索引,左表数量少 争取走索引连接 左表数据量少,右表数据量多,在右表上创建索引,可以走索引连接 select /*+REFED_EXISTS_OPT_FLAG(0) ADAPTIVE_NPLN_FLAG(2)*/ * from tt1 where exists (select * from tt2 where c1 = d1); 1 #NSET2: [7, 99, 20] 2 #PRJT2: [7, 99, 20]; exp_num(3), is_atom(FALSE) 3 #NEST LOOP INDEX JOIN2: [7, 99, 20] 半连接转内连接 4 #DISTINCT: [1, 99, 4] 5 #CSCN2: [1, 99, 4]; INDEX33555508(TT2) 6 #BLKUP2: [6, 1, 0]; IDX_IDX1(TT1) 7 #SSEK2: [6, 1, 0]; scan_type(ASC), IDX_IDX1(TT1), scan_range[TT2.D1,TT2.D1] select /*+REFED_EXISTS_OPT_FLAG(1) ADAPTIVE_NPLN_FLAG(2)*/ * from tt1 where exists (select * from tt2 where c1 = d1); 1 #NSET2: [7, 99, 20] 2 #PRJT2: [7, 99, 20]; exp_num(3), is_atom(FALSE) 3 #NEST LOOP INDEX JOIN2: [7, 99, 20] 4 #DISTINCT: [1, 99, 4] 5 #PRJT2: [1, 99, 4]; exp_num(1), is_atom(FALSE) 6 #CSCN2: [1, 99, 4]; INDEX33555508(TT2) 7 #BLKUP2: [6, 1, 0]; IDX_IDX1(TT1) 8 #SSEK2: [6, 1, 0]; scan_type(ASC), IDX_IDX1(TT1), scan_range[DMTEMPVIEW_16778926.colname,DMTEMPVIEW_16778926.colname]
----等待改写语句转化
select /*+ADAPTIVE_NPLN_FLAG(2)*/ * from tt1 where c1 in (select d1 from tt2 );
第八行是个常量值,exist转成ini去查询的
REFED_EXISTS_OPT_FLAG:是否将相关EXISTS查询转化为非相关IN查询
是否把相关 EXISTS 优化为非相关 IN 查询。0:否;1:是
有些情况,内标数据可能会越来越多,比外表多,内表数据多,外表数据少,适合用exist来查询,这个时候可以用enable_rq_to_nonref_spl(2)来优化
/*+enable_rq_to_nonref_spl(2)*/ 对查询项和 WHERE 表达式中出现的相关子查询表达式进行优化处理
12、top和排序
DROP TABLE T1; DROP TABLE T2; CREATE TABLE T1(C1 INT, C2 INT); CREATE TABLE T2(D1 INT, D2 INT); INSERT INTO T1 SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 80000; INSERT INTO T2 SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 80000; CREATE INDEX IND1 ON T1(C1, C2); CREATE INDEX IND2 ON T2(D1); SELECT /*+ TOP_ORDER_OPT_FLAG(0) */ TOP 10 C1, D1, D2 FROM T1 JOIN T2 ON C1 = D1 ORDER BY C1; 1 #NSET2: [4151, 10, 12] 2 #PRJT2: [4151, 10, 12]; exp_num(3), is_atom(FALSE) 3 #SORT3: [4151, 10, 12]; key_num(1), is_distinct(FALSE), top_flag(1), is_adaptive(0) 4 #HASH2 INNER JOIN: [33, 63296639, 12]; KEY_NUM(1); KEY(T1.C1=T2.D1) KEY_NULL_EQU(0) 5 #SSCN: [8, 80000, 4]; IND1(T1) 6 #CSCN2: [8, 80000, 8]; INDEX33556144(T2)
top_flag(1) top标志 先hash再排序,hash出来的数据很多,时间都花费在排序上了。其实需要的数据只是前10行,我们可以对排序列建合适的索引,尽量把排序消掉
SELECT /*+ TOP_ORDER_OPT_FLAG(1) */ TOP 10 C1, D1, D2 FROM T1 JOIN T2 ON C1 = D1 ORDER BY C1; 1 #NSET2: [2, 10, 12] 2 #PRJT2: [2, 10, 12]; exp_num(3), is_atom(FALSE) 3 #TOPN2: [2, 10, 12]; top_num(10) 4 #NEST LOOP INDEX JOIN2: [2, 600, 12] 5 #SSCN: [1, 300, 4]; IND1(T1) 6 #BLKUP2: [1, 2, 0]; IND2(T2) 7 #SSEK2: [1, 2, 0]; scan_type(ASC), IND2(T2), scan_range[T1.C1,T1.C1]
--blkup2 回表 比如select c2 form c where c1=1; c1上有索引,通过c1先查询出数据,然后再回表去c2 数据量多的话尽量消掉回表
TOP_ORDER_OPT_FLAG:是否对带有TOP和ORDER BY子句的查询进行优化,以移除SORT操作符
连接列和排序列是同一个,查询出来的是排序过的,不用再排序了
13、高并发
关注点:
1、尽量减少全表扫描,创建合适索引;
2、尽量减少二次回表,创建的索引尽量包含所需要的列。
3、尽量少用hash连接,能创建索引的,尽量走索引连接。
4、分区表注意分区列索引的选择,是全局索引和本地索引。数据不在一个分区,需要用全局索引,减少子表间切换的代价。
5、dm.ini参数是否合理,特别是一些资源参数,参考<内存管理>。
6、CPU资源比较高,关注是user还是sys?
1、sys高肯定存在问题,抓pstack分析堆栈是否存在临界区或者上锁等待之类的冲突等
2、生产系统非必要建议monitor参数关掉;
3、WORKER_CPU_PERCENT这个参数,不用动。否则可能tps上不去。
7、优化目标:高频sql和慢sql
8、单句做很慢,并发下很慢?
并发跑的情况下,抓单独sql的执行时间。通过ET分析慢在哪个操作符上?确认是达到资源上线?可优化的空间?
9、sql在manager上执行比较快,但是应用中比较慢或者响应时间上不去?
1、是否是绑定变量方式?和常量的计划是否一致?
2、应用的中间件日志级别
14、distinct
1、distinct这里都是等值查询了,不能再优化了
2、本来想消掉12行的回表的,创建个组合索引没用
-- drop index idx_20220510_1
-- create index idx_20220510_1 on clc_dtmain(patientid,nodecode,hosnum);
-- sp_index_stat_init('HISUSER','IDX_20220510_1',100);
3、统计信息不准,行数是2行,出来的结果有4行,而且代价也太大了,有88,用管理工具右键收集统计信息,速度瞬间上去了
15、sort
排序优化参数很多,这个要根据具体情况去测试。
这个SORT排序消不掉,因为这个不属于集函数包含distinct,也不是索引前导列
六、改写sql
这里就列举一个例子,这里问题卡了我很久,请教了好多大佬才搞定的。
原始sql是把where条件加在最后,但是因为sql中有大量的inner join和left join,导致执行计划不是最优方案。
把sql调整成如下这样,把where条件放到对应的表中,速度提升很多。
这个sql非常长,是报表中使用的,oracle中查询需要十几秒,在达梦中也需要十几秒,改写后只需要2s。