在数据库的使用中,书写正确的 SQL 语句只是完成了万里长征的第一步。事实上,在 DBA 的日常的工作中,SQL 优化占据了很大的一部分的内容,本文就将给大家介绍一些 SQL 优化有关的基础知识。
在理解如何优化 SQL 语句之前,我们首先要了解几个基本概念
执行计划是 SQL 语句的执行方式,由查询优化器为语句设计的执行方式,交给执行器去执行。在 SQL 命令行使用EXPLAIN可以打印出语句的执行计划。
操作符是 SQL 执行的基本单元,所有的 SQL 语句最终都是转换成一连串的操作符最后在服务器上执行,得到需要的结果,操作符也是读懂执行计划的基础,这里列举一下经常出现的操作符:
CSCN SSCN SSEK CSEK BLKUP
这些操作符是 SQL 查询数据的原始来源,SQL 里面出现的基表,都会出现在这些操作符的描述中,通过这些操作符来确定对应的表在执行计划中在何时以什么样的方式进入。
下面列出操作符的具体含义
更多的操作符解释,可以参见 DM 系统管理员手册附录 4《执行计划操作符》。
创建表 T1 并录入数据,相关 SQL 语句如下:
SQL> CREATE TABLE T1(C1 INT,C2 INT);
SQL> insert into t1 select level,level from dualconnect by level < 10000;
--SEL1
SQL> explain select * from t1 where c1 = 5;
1 #NSET2: [1, 249, 16]
2 #PRJT2: [1, 249, 16]; exp_num(3),is_atom(FALSE)
3 #SLCT2: [1, 249, 16]; T1.C1 =5
4 #CSCN2: [1, 9999, 16];INDEX33555446(T1)
我们创建了一个普通表,没有任何索引,过滤,从 T1 中取出数据只能走全表扫描 CSCN,下面我们创建一条索引:
SQL> create index i_test1 on t1(c1);
再看下面这个语句的计划
--SEL2
SQL> explain select c1 from t1;
1 #NSET2: [1, 9999, 12]
2 #PRJT2: [1, 9999, 12]; exp_num(2),is_atom(FALSE)
3 #SSCN: [1, 9999, 12];I_TEST1(T1)
--SEL3
SQL> explain select c2 from t1;
1 #NSET2: [1, 9999, 12]
2 #PRJT2: [1, 9999, 12]; exp_num(2),is_atom(FALSE)
3 #CSCN2: [1, 9999, 12]; INDEX33555446(T1)
这个时候 T1 存在两个入口,CSCN T1 基表,或者 SSCN 二级索引 I_TEST1,SEL2 中,只要求获取 C1,二级索引上存在 C1,且数据长度比基础表要少(多出一个 C2),索引选择 SSCN,对于 SEL3,依然没有更好的入口,还是选择 CSCN 全表。
一般来说,我们认为 CSCN 和 SSCN 的耗时是差不多了,SSCN 和 CSCN 的区别在于,SSCN 扫描出来的数据是按索引列排序的,这一点在一些情况下可以利用,现在看 SSEK 的情况。
--SEL4
SQL> explain select * from t1 where c1 = 5;
1 #NSET2: [0, 249, 16]
2 #PRJT2: [0, 249, 16]; exp_num(3),is_atom(FALSE)
3 #BLKUP2: [0, 249, 16];I_TEST1(T1)
4 #SSEK2: [0, 249, 16];scan_type(ASC), I_TEST1(T1), scan_range[5,5]
查询条件 C1 = 多少,存在 C1 索引,需要注意的是操作符后面的描述 scan_range[5,5],表示精准定位到 5,无疑,多数情况下这样是比较有效率的。
另外一点,SSEK 上面出现了 BLKUP 操作符,由于 I_TEST1上 没有 C2 的数据,而查询需要 SELECT *,索引需要 BLKUP 回原表查找整行数据,很容易的,我们可以想到如果只查询 C1,那么 BLKUP 操作符应该不存在,验证一下。
--SEL5
SQL> explain select c1 from t1 where c1 = 5;
1 #NSET2: [0, 249, 12]
2 #PRJT2: [0, 249, 12]; exp_num(2),is_atom(FALSE)
3 #SSEK2: [0, 249, 12];scan_type(ASC), I_TEST1(T1), scan_range[5,5]
聚簇索引是比较特殊的索引(对应操作符 CSEK),在 DM 上,同一张表的聚簇索引只允许存在一个,默认建表时(不建堆表的情况下),基表就是一个 ROWID 聚簇索引,可以预见到对 ROWID 的精准定位应该会走 CSEK。
--SEL6
SQL> explain select c1 from t1 where rowid = 6;
1 #NSET2: [0, 1, 12]
2 #PRJT2: [0, 1, 12]; exp_num(2),is_atom(FALSE)
3 #CSEK2: [0, 1, 12];scan_type(ASC), INDEX33555446(T1), scan_range[exp_cast(6),exp_cast(6)]
如果我们创建了一个自定义聚簇索引:
SQL> create cluster index i_index2 on t1(c2);
那么 ROWID 这个聚簇索引就不存在了,取而代指的是按 C2 为顺序的聚簇索引。
--SEL7
SQL> explain select c1 from t1 where rowid = 6;
1 #NSET2: [1, 249, 12]
2 #PRJT2: [1, 249, 12]; exp_num(1),is_atom(FALSE)
3 #SLCT2: [1, 249, 12];T1.ROWID = var1
4 #SSCN: [1, 9999, 12];I_TEST1(T1)
这里查询中需要 C1 以及 ROWID,而普通二级索引 I_TEST1 上正好都有,且比聚簇索引的长度要短,所以选择 SSCN I_TEST1。
--SEL8
SQL> explain select c1 from t1 where c2 = 6;
1 #NSET2: [0, 249, 8]
2 #PRJT2: [0, 249, 8]; exp_num(1),is_atom(FALSE)
3 #CSEK2: [0, 249, 8]; scan_type(ASC),I_INDEX2(T1), scan_range[6,6]
我们可以看到,对 ROWID 的精准定位不再走精准定位的 CSEK,而是全索引扫描 I_TEST1,对 C2 的精准过滤走的 CSEK,且不存在 BLKUP。
文章
阅读量
获赞