达梦数据库的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() 就会生效。

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql

2、分析dmsql日志

1)下载DMLOG8.3日志分析工具

链接:https://pan.baidu.com/s/1zqEvxQod6fR-KiCRYDTNXA

提取码:wjph

 

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_02

2)修改dmlog.properties

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_03

3)执行

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_04

最好会生成一个RESULT_xxx的文件夹,常用的是那两个xls。

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_05

一个是根据执行时间排序,另一个是根据执行次数排序。

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_06

抓出的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 节点类型、名字和详细描述。

 

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_07

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;--显示系统统计信息

未显式指定采样率,系统根据数据规模指定采样率:

 

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_08

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 代表实现内连接的不同方法。

具体就不在这里说了,之前写过一篇执行计划的博客可以参考下。

四、收集统计信息

收集统计信息这个,比较常用,比如数据从其他库迁移到达梦,一般要先收集一下统计信息,为了方便,一般是收集全库的统计信息。在针对某个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里,去重一下,针对用到的表和索引去收集下统计信息。

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_09

注意:收集统计信息的方式非常多,这里我只整理了自己比较常用的。

五、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方式汇总。存在可以使用索引的过滤条件,则性能较好。如果是大数据量表,没有可利用的索引,则反复全表扫描,性能较差。

例一

去重

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_10

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当作整体计算表达式。

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_11

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 表达式。

 

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_12

5、子查询

/*+ENABLE_RQ_TO_NONREF_SPL(1)*/

对查询项中出现的相关子查询表达式进行优化处理

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_13

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 的优化

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_14

注意:in或者not in,内层查询建议加distinct。本例就是,加了distinct查询速度提升很多。

7、NEST LOOP

OUTER_OPT_NLO_FLAG 是否进行外连接、内连接的相关优化,默认值0

OUTER_OPT_NLO_FLAG=0 时,不进行优化;

OUTER_OPT_NLO_FLAG=1 时,若满足下列条件,将外连接转换为嵌套外连接进行优化:

两层嵌套循环结构,有驱动表和被驱动表之分。

选定一张表作为驱动表,遍历驱动表中的每一行,根据连接条件去匹配第二张表中的行。驱动表的行数就是循环的次数,这个很大程度影响了执行效率。

外层循环选取一个,然后从内层循环中查找匹配,返回匹配行

外层循环的行数决定内层循环做扫描多少次,外部循环表的行数要小于内部循环的行数

需注意的问题:

- 选择小表作为驱动表。统计信息尽量准确,保证优化器选对驱动表。

- 大量的随机读。如果没有索引,随机读很致命,每次循环只能读一块,不能读多块。使用索引可以解决这个问题。

使用场景:

- 驱动表有很好的过滤条件

 表连接条件能使用索引

 结果集比较小

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_15

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_16

将 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

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_17

并发下通常不建议使用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)  坦化方式处理相关查询表达式

 

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_18

ENABLE_RQ_TO_NONREF_SPL(1) 一行一行处理相关查询表达式 相关查询表达式转化为非相关查询表达式

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_19

select ( select count(*) from a, b where a1 = b1 and a2 = c1) x from c

这是个相关子查询,里层连接查询的查询条件,用到了外层表c的字段

优化方式是,相关查询表达式转化为非相关查询表达式,c1当成常量传给a2用来查询

当走到执行计划5的时候,触发SPL2(临时表)

C表数据量不是特别大的时候,这种方式查询速度快

4)派生表

 

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_20

派生表可以当作视图来优化

优化前是先全表扫描,再根据条件查询,所有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)派生表

 

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_21

派生表可以当作视图来优化

优化前是先全表扫描,再根据条件查询,所有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 表达式中出现的相关子查询表达式进行优化处理

 

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_22

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操作符

连接列和排序列是同一个,查询出来的是排序过的,不用再排序了

 

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_23

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

 

达梦数据库性能vsmysql 达梦数据库性能调优_SQL_24

达梦数据库性能vsmysql 达梦数据库性能调优_子查询_25

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

排序优化参数很多,这个要根据具体情况去测试。

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_26

这个SORT排序消不掉,因为这个不属于集函数包含distinct,也不是索引前导列

六、改写sql

这里就列举一个例子,这里问题卡了我很久,请教了好多大佬才搞定的。

原始sql是把where条件加在最后,但是因为sql中有大量的inner join和left join,导致执行计划不是最优方案。

 

达梦数据库性能vsmysql 达梦数据库性能调优_sql_27

把sql调整成如下这样,把where条件放到对应的表中,速度提升很多。

 

达梦数据库性能vsmysql 达梦数据库性能调优_达梦数据库性能vsmysql_28

这个sql非常长,是报表中使用的,oracle中查询需要十几秒,在达梦中也需要十几秒,改写后只需要2s。