一、调优目标

SQL 调优的整体目标是使用最优的执行计划,使IO以及CPU代价最小。调优主要关注下列方面:

·表扫描

如果计划中对某大表使用了全索引扫描,那么需要关注该表是否存在某个查询条件使得过滤后可以淘汰至少一半的数据量。通过添加相应的索引,全索引扫描可能被转换为范围扫描或等值查找。

·连接操作的顺序和类型

多表连接时,尽可能找到一种能使中间结果保持最小的连接顺序。

对于给定的一个连接或半连接,DM 可以用 HASH 连接、嵌套循环连接、索引连接或者是归并连接实现。通过分析表的数据量大小和索引信息,SQL 调优目标是选择最适宜的操作符。对半连接而言,HASH 连接还可细分为左半 HASH 和右半 HASH。用户可以通过始终对数据量小的一侧建立 HASH 来进行调优。

·分组操作

分组操作往往要求缓存所有数据以找到属于同一组的所有数据,在大数据量情况下这会带来大量的 IO。应该检查 SQL 查询和表上索引信息,如果可以利用包含分组列的索引,执行计划就会使用排序分组从而不用缓存中间结果。

二、确定高负载的 SQL

打开监控开关(ENABLE_MONITOR=1MONITOR_TIME=1

通过查询动态视:

V$LONG_EXEC_SQLS显示最近 1000 条执行时间较长的 SQL 语句),如SELECT * FROM V$LONG_EXEC_SQLS;

V$SYSTEM_LONG_EXEC_SQLS显示服务器启动以来执行时间最长的20 SQL 语句)如SELECT * FROM V$SYSTEM_LONG_EXEC_SQLS;

三、开发有效的 SQL 语句

1.避免使用 OR 子句

如果OR子句都是对同一列进行过滤,用户可以考虑使用IN VALUE LIST的过滤形式。

SELECT ... WHERE CITY = 'SHANGHAI' OR CITY = 'WUHAN' OR CITY = 'BEIJING';

调整为

SELECT ... WHERE CITY IN( 'SHANGHAI','WUHAN','BEIJING');

2.避免使用困难的正则表达式

例如:a LIKE 'L%'可以优化为 a>='L' AND a<'M',这样就可以用到 a 上的索引。即使没有索引,转换后的比较也更快。

3.灵活使用伪表(SYSDUAL)

首先可以利用伪表进行科学计算,执行语句 SELECT 3*4 FROM SYSDUAL,则可以得

到结果 12

其次,在某些方面使用 SYSDUAL 可提高效率。例如:查询过程中要判断表 t1 中是否有满足 condition1 条件的记录存在,可执行以下语句:

SELECT COUNT(*) INTO x FROM t1 WHERE condition1;

然后根据变量 x 的取值来判断。但是当 t1 非常大时该语句执行速度很慢,而且由于不

知道 SELECT 返回的个数,不能用 SELECT *代替。事实上这个查询可以利用伪表来完成:

SELECT 'A' INTO y FROM SYSDUAL

WHERE EXISTS (SELECT 1 FROM t1 WHERE condition1);

判断 y 值,如等于'A'T1 中有记录。调整后的语句执行速度明显比上一句高。

另外,在 DM 的语法里是可以省略 FROM 子句的,这时系统会自动加上 FROM SYSDUAL

因此前面的科学计算例子可以简化为 SELECT 3*4;

4.SELECT 项避免“*”

除非用户确实要选择表中所有列,否则 SELECT *这种写法将让执行器背上沉重的负荷。

因为每一列的数据不得不自下往上层层向上传递。不仅仅如此,如果用户查询的是列存储表,

那么列存储所带来的 IO 优势将损耗殆尽。

任何时候,用户都要了解表结构和业务需求,小心地选择需要的列并一一给出名称,避

免直接用 SELECT *

5.避免功能相似的重复索引

索引并非越多越好。抛开优化器面对众多索引逐一试探所耗费的时间不谈,如果表上增

删改操作频繁,那么索引的维护将会成为大麻烦,尤其是函数索引的计算开销更不能忽略

6.使用 COUNT(*)统计结果行数

如果对单表查询 COUNT(*)且没有过滤条件,那么 DM 优化器会直接读取相关索引中存

储的行数信息,加以回滚段中其他事务插入或删除元组的行数修正,迅速地给出最终结果而

避免对实际数据的读取。相比之下,COUNT(列名)会对数据进行读操作,执行效率远低于

COUNT(*)

即使查询中含有过滤条件,由于 DM 特有的批处理方式,COUNT(*)依旧快于其他写法。

这是因为 COUNT(*)无需取得行的具体值而仅仅需要行数这一信息。

需要额外说明的是,COUNT(*)会将 NULL 值计算在内而 COUNT(列名)是不包含 NULL

值的,因此用户要结合应用场景决定是否可以使用 COUNT(*)

7.使用 EXPLAIN 来查看执行计划

在查询语句或者插入、删除、更新语句前增加 EXPLAIN 关键字,DM 将显示其执行计划

而无需实际执行它。查阅 V$SQL_NODE_NAME 表中每个操作符的含义,用户可以很方便且

直观地了解数据如何被处理及传递。如果启用了统计信息收集,那么对照执行计划和对动态

视图 V$SQL_NODE_HISTORYV$SQL_NODE_NAME 的查询结果,用户就可以知道在实际执 行中每一个操作符执行的时间,进而找出性能瓶颈。

8.UNION UNION ALL 的选择

UNION UNION ALL 的区别是前者会过滤掉值完全相同的元组,为此 UNION 操作符

需要建立 HASH 表缓存所有数据并去除重复,当 HASH 表大小超过了 INI 参数指定的限制时 还会做刷盘。

因此如果应用场景并不关心重复元组或者不可能出现重复,那么 UNION ALL 无疑优于

UNION

9.优化 GROUP BY ... HAVING

GROUP BY 最常见的实现有 HASH 分组(HAGR)和排序分组(SAGR)。前者需要缓存中

间结果;如果用户在 GROUP BY 的列上建立索引,那么优化器就会判断并可能使用上该索引,这时的 GROUP BY 就会变为 SAGR

HAVING 是分组后对结果集进行的过滤,如果过滤条件无关集函数操作,可以考虑将过滤条件放在 WHERE 而不是 HAVING 中。

10.使用优化器提示(HINT

利用经验对优化器的计划选择进行调整,HINT SQL 调整不可或缺的一步。

详细说明参见《DM8系统管理员手册》

四、查看执行计划

1. 使用EXPLAIN 命令查看执行计划

示例:

explain 

select dept.dept_name, emp.* FROM departments dept

left JOIN dept_emp de ON de.dept_no = dept.dept_no

left JOIN employees emp ON emp.emp_no = de.emp_no;

 

2.使用EXPLAIN FOR 命令查看执行计划

explain  for

select dept."dept_name", emp.* FROM "DMTEST"."departments" dept

left JOIN "DMTEST"."dept_emp" de ON de."dept_no" = dept."dept_no"

left JOIN "DMTEST"."employees" emp ON emp."emp_no" = de."emp_no";

 

3.执行计划中常见的操作符解释

常见的操作符有:

NSET2:结果集

PRJT2:投影,,用于选择表达式项的计算。

CSCN2 :聚集索引扫描

SSCN :直接使用二级索引进行扫描

SSEK2 :二级索引数据定位

CSEK2 :聚集索引数据定位

BLKUP2 :定位查找

SLCT2:关系的选择select)运算,用于查询条件的过滤。

SORT: SORT是做排序操作时使用到的操作符。

HAGR:HASH AGR操作,是最基础的分组方式,对于没有优化条件的分组语句,一般都会按这种方式进行分组。

SAGR: SORTED AGR操作,同一分组的数据按照顺序取出。

NEST LOOP INNER JOIN(嵌套循环连接) :最基础的连接方式,将一张表的一个值与另一张表的所有值拼接,形成一个大结果集,再从大结果集中过滤出满足条件的行。

HASH JOIN(哈希连接):没有索引的情况下,大多数连接的处理方式,将一张表的连接列做成HASH表,另一张表的数据向这个HASH表匹配,满足条件的返回。

INDEX JOIN(索引连接): 将一张表的数据拿出,去另外一张表上进行范围扫描找出需要的数据行,需要右表的连接列上存在索引。

MERGE JOIN(归并连接):两张表都扫描索引,按照索引顺序进行归并。

  • 查看ET

ET是达梦自带的系统存储过程,能统计SQL每个操作符的时间花费,从而定位到有性能问题的操作,指导我们去优化;

达梦ET默认未启用,设置启动以下三个参数可以启用ET(ENABLE_MONITOR、MONITOR_TIME和MONITOR_SQL_EXEC);

其中,ENABLE_MONITOR和MONITOR_TIME默认已开启,如果未开启可以使用如下方法开启:

SP_SET_PARA_VALUE(1,‘ENABLE_MONITOR’,1);

SP_SET_PARA_VALUE(1,‘MONITOR_TIME’,1);

MONITOR_SQL_EXEC为会话级动态参数,可以设置只针对当前会话开启:

SF_SET_SESSION_PARA_VALUE(‘MONITOR_SQL_EXEC’,1);

执行SQL语句,我们会看到一个执行号,直接点这个执行号,即可调用ET;

在知道执行号的情况下,也可以这样使用ET CALL ET(124571)

ET结果说明:

OP: 操作符

TIME(us): 时间开销,单位为微秒

PERCENT: 执行时间占总时间百分比

RANK: 执行时间耗时排序

SEQ: 执行计划节点号

N_ENTER: 进入次数

 

 

 达梦学习社区地址:https://eco.dameng.com

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐