达梦sql优化之执行计划
执行计划是什么呢?比如你执行一条sql语句,查询优化器会为这条sql语句设计执行方式,交给执行器去执行,查询优化器设计的执行方式就是执行计划。
EXPLAIN可以打印出语句的执行计划。
那么,执行计划主要是由什么组成的呢?答案是操作符(个人理解)。
执行计划是由各类操作符组成的一颗树,从内到外依次执行,缩进越多的越先执行,同样缩进的上面的先执行,下面的后执行,上下的优先级高于内外。
达梦执行计划涉及到的一些主要操作符有:
CSCN :基础全表扫描(a),从头到尾,全部扫描
SSCN :二级索引扫描(b), 从头到尾,全部扫描
SSEK :二级索引范围扫描(b) ,通过键值精准定位到范围或者单值
CSEK :聚簇索引范围扫描© ,通过键值精准定位到范围或者单值
BLKUP :根据二级索引的ROWID 回原表中取出全部数据(b + a)
一、执行计划解读
SQL> explain select * from SYSOBJECTS;
1 #NSET2: [0, 1531, 396]
2 #PRJT2: [0, 1531, 396]; exp_num(17), is_atom(FALSE)
3 #CSCN2: [0, 1531, 396]; SYSINDEXSYSOBJECTS(SYSOBJECTS as SYSOBJECTS)
从上面的执行计划中我们可以看到哪些信息呢?
首先,一个执行计划由若干个计划节点组成,如上面的1、2、3。
然后我们看到,每个计划节点中包含操作符(CSCN2)和它的代价([0, 1711, 396])等信息。
代价由一个三元组组成[代价,记录行数,字节数]。
代价的单位是毫秒,记录行数表示该计划节点输出的行数,字节数表示该计划节点输出的字节数。
拿上面第三个计划节点举例:操作符是CSCN2即全表扫描,代价估算是0ms,扫描的记录行数是1711行,输出字节数是396个。
二、举例说明操作符
1、准备测试表和数据
DROP TABLE T1; DROP TABLE T2; CREATE TABLE T1(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) ); CREATE TABLE T2(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) ); INSERT INTO T1 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL<=10000; INSERT INTO T2 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL<=10000; CREATE INDEX IDX_C1_T1 ON T1(C1); SP_INDEX_STAT_INIT(USER,'IDX_C1_T1');-- 收集指定的索引的统计信息
这里说明一下SP_INDEX_STAT_INIT的两个参数分别是模式名和索引名。我这里指定的是USER,会默认查找当前登录用户同名的模式,如果这个用户下有多个模式,查不到其他模式。
2、NSET:收集结果集
说明:用于结果集收集的操作符, 一般是查询计划的顶层节点。
EXPLAIN SELECT * FROM T1;
3、PRJT:投影
说明:关系的“投影”(project)运算,用于选择表达式项的计算;广泛用于查询,排序,函数索引创建等。
EXPLAIN SELECT * FROM T1;
4、SLCT:选择
说明:关系的“选择” 运算,用于查询条件的过滤。
EXPLAIN SELECT * FROM T1 WHERE C2='TEST';
5、AAGR:简单聚集
说明:用于没有group by的count,sum,age,max,min等聚集函数的计算。
EXPLAIN SELECT COUNT(*) FROM T1 WHERE C1 = 10;
6、FAGR:快速聚集
说明:用于没有过滤条件时从表或索引快速获取 MAX/MIN/COUNT值,DM数据库是世界上单表不带过滤条件下取COUNT值最快的数据库。
EXPLAIN SELECT COUNT(*) FROM T1;
7、HAGR:HASH分组聚集
说明:用于分组列没有索引只能走全表扫描的分组聚集,C2列没有创建索引。
EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C2;
8、SAGR:流分组聚集
说明:用于分组列是有序的情况下可以使用流分组聚集,C1上已经创建了索引,SAGR2性能优于HAGR2。
EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C1;
官方解释是:如果输入流是有序的,则使用流分组,并计算聚集函数。
9、BLKUP:二次扫描
说明:先使用2级别索引定位,再根据表的主键、聚集索引、 rowid等信息定位数据行。
EXPLAIN SELECT * FROM T1 WHERE C1=10;
bookmark lookup 翻译成中文是书签查找
BLKUP2 官方文档说明是:定位查找
10、CSCN:全表扫描
说明:CSCN2是CLUSTER INDEX SCAN的缩写即通过聚集索引扫描全表,全表扫描是最简单的查询,如果没有选择谓词,或者没有索引可以利用,则系统一般只能做全表扫描。在一个高并发的系统中应尽量避免全表扫描。
EXPLAIN SELECT * FROM T1;
11、SSEK、CSEK、SSCN:索引扫描
1) SSEK
说明:SSEK2是二级索引扫描即先扫描索引,再通过主键、聚集索引、ROWID等信息去扫描表;
EXPLAIN SELECT * FROM T1 WHERE C1=10;
2)CSEK
说明:CSEK2是聚集索引扫描只需要扫描索引,不需要扫描表;
CREATE CLUSTER INDEX IDX_C1_T2 ON T2(C1);
EXPLAIN SELECT * FROM T2 WHERE C1=10;
3)SSCN
说明:SSCN是索引全扫描,不需要扫描表。
官方解释是:直接使用二级索引进行扫描。
CREATE INDEX IDX_C1_C2_T1 ON T1(C1,C2);
EXPLAIN SELECT C1,C2 FROM T1;
三、简单连接查询例子
CREATE TABLE T1(C1 INT,C2 CHAR);
CREATE TABLE T2(D1 INT,D2 CHAR);
CREATE INDEX IDX_T1_C1 ON T1(C1);
INSERT INTO T1 VALUES(1,'A');
INSERT INTO T1 VALUES(2,'B');
INSERT INTO T1 VALUES(3,'C');
INSERT INTO T1 VALUES(4,'D');
INSERT INTO T2 VALUES(1,'A');
INSERT INTO T2 VALUES(2,'B');
INSERT INTO T2 VALUES(5,'C');
INSERT INTO T2 VALUES(6,'D');
SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;
EXPLAIN SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
NEST LOOP INDEX JOIN2 索引内连接
CSCN2 聚集索引扫描
SSEK2 二级索引数据定位
该计划的大致执行流程如下:
1) CSCN2: 扫描 T2 表的聚集索引,数据传递给父节点索引连接;
2) NEST LOOP INDEX JOIN2: 当左孩子有数据返回时取右侧数据;
3) SSEK2: 利用 T2 表当前的 D1 值作为二级索引 IDX_T1_C1 定位查找的 KEY,返回结果给父节点;
4) NEST LOOP INDEX JOIN2: 如果右孩子有数据则将结果传递给父节点 PRJT2,否则继续取左孩子的下一条记录;
5) PRJT2: 进行表达式计算 C1+1, D2;
6) NSET2: 输出最后结果;
7) 重复过程 1) ~ 4)直至左侧 CSCN2 数据全部取完。
应该是这么个意思,取右侧的一个值,去左侧匹配,然后到PRJT2去计算,接着再去取右侧一个值,再去左侧匹配,如此循环,直到匹配完,不知道我理解的对不对。
四、单表
CREATE TABLE T1(C1 INT,C2 INT);
insert into t1 select level,level from dual connect by level < 10000;
1、全表扫描(无索引时)
explain select * from t1 where c1 = 5;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
SLCT2 关系的―选择‖(select)运算,用于查询条件的过滤
CSCN2 聚集索引扫描
说明:创建了一个普通表,没有任何索引,过滤,从T1中取出数据只能走全表扫描CSCN
2、t1(c1)加索引i_test1
create index i_test1 on t1(c1);
1)直接使用二级索引扫描
explain select c1 from t1;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
SSCN 直接使用二级索引进行扫描
说明:这个时候T1存在两个入口,CSCN T1基表(全表扫描T1),或者SSCN 二级索引I_TEST1,本例只要求获取C1,二级索引上存在C1,且数据长度比基础表要少(基表多出一个C2),索引选择SSCN。
2)全表扫描(有索引时)
explain select c2 from t1;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
CSCN2 聚集索引扫描
说明:依然没有更好的入口,还是选择CSCN全表
3)定位查找
explain select * from t1 where c1 = 5;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
BLKUP2 定位查找
SSEK2 二级索引数据定位
说明:查询条件C1 = 多少,存在C1索引,需要注意的是操作符后面的描述scan_range[5,5],表示精准定位到5,无疑,多数情况下这样是比较有效率的。
另外一点,SSEK 上面出现了BLKUP操作符,由于I_TEST1上没有C2的数据,而查询需要SELECT *,索引需要BLKUP回原表查找整行数据。
很容易的,我们可以想到如果只查询C1,那么BLKUP操作符应该不存在,验证一下。
explain select c1 from t1 where c1 = 5;
3、聚簇索引
1)ROWID聚簇索引
聚簇索引是比较特殊的索引(对应操作符CSEK),在DM7上,同一张表的聚簇索引只允许存在一个,默认建表时(不建堆表的情况下),基表就是一个ROWID聚簇索引,可以预见到对ROWID的精准定位应该会走CSEK。
explain select c1 from t1 where rowid = 6;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
CSEK2 聚集索引数据定位
2) t1(c2)自定义聚簇索引i_index2
create cluster index i_index2 on t1(c2);
那么ROWID这个聚簇索引就不存在了,取而代指的是按C2为顺序的聚簇索引
explain select c1 from t1 where rowid = 6;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
SLCT2 关系的―选择‖(select)运算,用于查询条件的过滤
SSCN 直接使用二级索引进行扫描
说明:这里查询中需要C1以及ROWID,而普通二级索引I_TEST1上正好都有,且比聚簇索引的长度要短,所以选择SSCN I_TEST1
explain select c1 from t1 where c2 = 6;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
CSEK2 聚集索引数据定位
五、复杂连接查询
CREATE TABLE TEST5(ID INT); CREATE TABLE TEST6(ID INT); CREATE TABLE TEST7(ID INT); CREATE TABLE TEST8(ID INT); insert into test5 values(3); insert into test6 values(4); insert into test7 select level %100 from dual connect by level < 10000; insert into test8 select level %100 from dual connect by level < 10000; explain select /*+no_use_cvt_var*/* from (select test5.id from test5,test6 where test5.id = test6.id)a, (select id from (select test7.id from test7,test8 where test7.id = test8.id) group by id ) b where a.id = b.id;
用到的操作符说明:
NSET2 结果集(result set)收集,一般是查询计划的顶层节点
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
HASH2 INNER JOIN HASH 内连接
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
HASH2 INNER JOIN HASH 内连接
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
HASH2 INNER JOIN HASH 内连接
CSCN2 聚集索引扫描
CSCN2 聚集索引扫描
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
HAGR2 HASH 分组,并计算聚集函数
PRJT2 关系的―投影‖(project)运算,用于选择表达式项的计算
HASH2 INNER JOIN HASH 内连接
CSCN2 聚集索引扫描
CSCN2 聚集索引扫描
no_use_cvt_var 不考虑变量改写方式实现连接,仅 OPTIMIZER_MODE=1 有效。
执行顺序 6->7->5->12->13->11->9->3
首先执行TEST5和TEST6的HASH连接,然后执行TEST7,TEST8的HASH连接并将连接结果进行HASH分组,再将两个结果再次进行HASH连接得到最终结果集。
六、补充下索引的知识点
聚集(clustered)索引,也叫聚簇索引。聚簇索引的索引和数据是存储在一起的。
定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
说实话,看着有点拗口,用大白话说就是,我们的sql数据库是行数据库,数据是一行一行存储的,而聚集索引是个特殊的索引,相当于这一行行记录的物理编号,描述这一行行数据的物理存储顺序。所以,一张表只会有一个聚集索引。
除了聚集索引外的其他索引类型都属于二级索引。
非聚集索引和聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据
更多资讯请上达梦技术社区了解: https://eco.dameng.com
原文地址:https://www.cnblogs.com/wuran222/p/15132004.html
- 32和64位的CentOS 6.0下 安装 Mono 2.10.8 和Jexus 5.0
- 跨平台的 .NET 运行环境 Mono 3.2 新特性
- Ibatisnet Quick Start
- 常见的3种Hive参数配置方法
- 6杂域名超16万元结拍
- GridView控件使用经验
- WordPress登陆文件wp-login.php添加相关参数,安全有保障
- WordPress在RSS Feed 中输出自定义特色图像(缩略图)
- 单拼域名can.com以高达99.2万元成交!
- CI学习 CCNET Config 第一天
- IBatisNet基础组件
- 学习altas笔记[客户端JS和Altas环境初始化关系和DataTable返回数据的客户端处理]
- 在Windows 8 Hyper-V下的安装CentOS 6和SSH配置
- WordPress RSS Feed 优化/设置技巧六则
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Java交互界面实现计算器开发设计【附函数源码】
- Python 技术篇-读取本地文件的字节数
- Golang Flag包的参数格式问题
- Python 技术篇-对音频、图片等文件进行base64编码和解码
- 图片:“给你五十行代码把我变成字符画!” 程序:“太多了,一半都用不完!”
- HTML布局标记和列表标记
- 使用frp搭建内网穿透
- CNS图表复现03—单细胞区分免疫细胞和肿瘤细胞
- C#记事本项目开发,一个可以实现批量操作的记事本!【附源码】
- Python 库引用问题:name 'json' is not defined,原因及解决办法
- Java集合类之Collection接口,集合的“爸爸”接口了解一下?
- Python 微信机器人-下载微信接收到的语音、图片等资源
- Python源文件打包成可执行的exe应用,给你的代码变个身!
- Python 技术篇-ffmpeg.exe的安装及配置
- Python 库配置问题-"Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work",原因及解决办法