热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

数据库性能调优技术

一、概述这些内容,我会在后续的几篇文章中进行详细的描述。在此之前,我们首先需要掌握如何理解数据库执行计划。这篇文章讲解只涉及单表操作的执行计划。达梦数据库、oracle数据库、sqlserver数据库都可以显示给定语句的执行计划。我详细分析了这三个数

一、概述 这些内容,我会在后续的几篇文章中进行详细的描述。在此之前,我们首先需要掌握如何理解数据库执行计划。这篇文章讲解只涉及单表操作的执行计划。达梦数据库、oracle 数据库、sql server 数据库都可以显示给定语句的执行计划。我详细分析了这三个数


一、概述

这些内容,我会在后续的几篇文章中进行详细的描述。在此之前,我们首先需要掌握如何理解数据库执行计划。这篇文章讲解只涉及单表操作的执行计划。达梦数据库、oracle 数据库、sql server 数据库都可以显示给定语句的执行计划。我详细分析了这三个数据库的执行计划,三者之间并无本质区别。所以本文的内容适合于这三个数据库。同样,也应该适合绝大多数其它的数据库。

单表执行的深入理解,是了解多表执行计划的基础。达梦数据库显示的执行计划时显示的信息会多一些。因此,这篇文章中我选择达梦数据库作为实例数据库来讲解执行计划的原理。读完本文后,应该能够读懂这三个数据库的单表执行计划。

二、深入理解数据库执行计划

达梦数据库的执行计划有两种显示方式:第一种为图形化的显示方式;第二种为文本式的显示方式。这里采用第二种方式进行讲解。理解执行计划,是迈向理解数据库性能调优的重要一步。从执行计划中,我们可以看出数据库是如何执行查询语句,并根据执行计划判断出该查询语句的执行是否高效,以及如何进行优化。

下面我们将通过一些例子来理解数据库执行计划

1.没有索引的全表扫描过滤如何执行?

构造处执行场景:

create table t1(c1 int,c2 int);

insert into t1 values(1,1);

insert into t1 values(2,2);

insert into t1 values(3,3);

insert into t1 values(4,4);

insert into t1 values(5,5);

insert into t1 values(6,6);

查询语句为:

select * fromt1 wherec1=2;

该语句的执行过程,如果用语言描述可以描述成这样:

1)如果是第一次执行该步骤,则取得表的第一条记录;否则取得当前记录的下一条记录。如果记录已经扫描结束,则执行步骤 4,否则执行步骤 2。

2)判断该记录是否满足过滤条件c1=2,满足则执行步骤 3,否则执行步骤 1。

3)把该记录放到结果集中,执行步骤1。

4)将结果集返回给客户端。

实际上,数据库执行查询语句的过程也是类似的,下面是该查询语句的执行计划:

#RSET:[21, 1, 1];

#XFLT:[0, 0, 0]; EXPR0 = 2

#CSEK:[21, 1, 1]; INDEX33555545(T1),FULL_SCAN

该执行计划中出现的内容,在此做出解释:

1)CSEK(查找)类似于上文中描述的步骤1,方括号中的内容是执行该操作的评估代价,本文不作分析。“INDEX33555545(T1)”说明使用了 T1 表的聚集索引, “FULL_SCAN”表示对聚集索引 INDEX33555545(T1)进行全扫描。这里需要注意的是,达梦数据库中的表默认情况下是索引组织的。如果建表时指定了 cluster primary key,那么数据以该 clsuter primary key 组织数据,否则以 rowid 组织数据。

2)XFLT(过滤)类似于上文中描述的步骤 2,“EXPR0 = 2”是过滤条件。

3)RSET(结果集)类似于上文中描述的步骤 3,用来存放符合条件的记录集。

我们可以看出,数据库的执行过程和我们用语言描述的步骤是一致的。

该查询语句完整的执行流程如下:

1)CSEK 取得第一条记录(1,1)传给 XFLT,将控制权传给 XFLT。

2)XFLT 发现该记录(1,1)不符合条件,将控制权传给 CSEK。

3)CSEK 取得下一条记录(2,2)传给 XFLT,将控制权传给 XFLT。

4)XFLT 发现记录(2,2)符合条件,将该记录传给 RSET,将控制权传给 RSET。

5)RSET 将记录(2,2)放入结果集,将控制权传给 XFLT。

6)XFLT 给控制权传给 CSEK。

7)CSEK 取得下一条(3,3)传给 XFLT,将控制权传给 XFLT。

8)XFLT 发现该记录(3,3)不符合条件,将控制权传给 CSEK。

9)CSEK 取得下一条(4,4)传给 XFLT,将控制权传给 XFLT。

10)XFLT 发现该记录(4,4)不符合条件,将控制权传给 CSEK

11)CSEK 取得下一条(5,5)传给 XFLT,将控制权传给 XFLT。

12)XFLT 发现该记录(5,5)不符合条件,将控制权传给 CSEK。

13)CSEK 取得下一条(6,6)传给 XFLT,将控制权传给 XFLT。

14)XFLT 发现该记录(6,6)不符合条件,将控制权传给 CSEK。

15)CSEK 发现描述操作已经结束,通知 XFLT 结束。将控制权传给 XFLT。

16)XFLT 得知查询操作结束,通知 RSET 结束。将控制权传给 RSET。

17)RSET 得知操作结束。

18)发送结果集(包含记录(2,2))给客户端。

2.如果表 t1 上的 c1 列有非唯一索引,如何执行呢?

表 t1 的定义以及数据和 1 中描述的一样。

创建索引: create index it1_c1 on t1(c1);

查询语句“select* from t1 where c1=2;”对应的执行计划为:

#RSET:[201, 2, 1];

#CSEK(SECOND):[201, 2, 1]; IT1C1(T1), INDEX_EQU_SEARCH

CSEK 行的“SECOND”表示使用非聚集索引“IT1C1”,对该索引进行索引等值(INDEX_EQU_SEARCH)查找。

该执行计划的执行流程为:

1)CSEK 使用 c1=2 查找非聚集索引,得到第一条 c1=2 的索引记录(2,rowid1)中的 rowid1(为数值)。使用 rowid1 查找聚集索引得到对应的数据记录(2,2)传递给 RSET,将控制权传给 RSET。

2)RSET 将记录(2,2)放入结果集,将控制权传给CSEK。(因为 c1 上的索引是非唯一的,所以可能出现两条以上的记录满足c1=2,所以需要将控制权传给CSEK)。

3)CSEK 取得当前非聚集记录的下一条记录(3,rowid2),因为3!=2,所以扫描结束。将控制权传给RSET。(如果满足 c1=2 的记录数大于1条,需要继续传递记录给 RSET,以此类推,直到遇到不满足c1=2 的那条记录,结束操作。)

4)RSET 得知操作结束。

5)发送结果集(包含记录(2,2))给客户端。

3.如果表 t1 上的 c1 列有唯一索引,如何执行呢?

首先删除 c1 列上的非唯一索引,然后在 c1 列上创建唯一索引:

drop indexit1 c1;

create uniqueindex uit1 c1 on t1(c1);

查询语句“select* from t1 where c1=2;”对应的执行计划为:

#RSET:[201, 2, 1];

#CSEK(SECOND):[201, 2, 1]; UIT1C1(T1), INDEX_EQU_SEARCH

该执行计划的执行流程为:

1)CSEK 使用 c1=2 查找非聚集索引,得到 c1=2 的索引记录(2,rowid1)中的rowid1(为数值)。使用 rowid1 查找聚集索引得到对应的数据记录(2,2)传递给 RSET, 将 控制权传给 RSET。(当然,有人也许会问,如果没有记录满足 c1=2怎么办呢?那么, 此处什么记录都不传递给RSET,通知RSET查询操作结束,最后返回空集给客户端)。

2)RSET 将记录(2,2)放入结果集,操作结束(因为是唯一索引,所以最多只有 1 条记录满足 c1=2)。

3)发送结果集(包含记录(2,2))给客户端。

这里我们发现,例3使用了唯一索引,例2使用了非唯一索引。例3的执行速度大于例2的执行速度。

4.如何理解执行计划中的 top n 操作?

查询语句“select top 10 * from t1 wherec1>2;”对应的执行计划为:

#RSET:[21, 1, 1];

#XTOP:[0, 0, 0]; top_off(0), top_num(10)

#XFLT:[0, 0, 0]; EXPR1 > 2

#CSEK:[21, 1, 1]; INDEX33555545(T1),FULL_SCAN

XTOP(取得前 N 条记录):将 XFLT 操作符传递来的记录放入到 RSET(结果集)中,并判断记录数是否已经等于给定值 10(语句中的 top 10)。如果已经等于 10,则查询已经执行成功,退出。否则将控制权限传给 XFLT,继续执行。依次执行,直到取得 10 条记录,或者表CSEK操作已经查询结束(即符合条件的记录不满 10 条)。

5.如何理解执行计划中的 order by 操作?

查询语句“select top 10 * from t1 where c2>2order by c1;”对应的执行计划为:

#RSET:[21, 1, 1];

#XSORT:[0, 0, 0]; keys_num(1),is_distinct(FALSE)

#XFLT:[0, 0, 0]; EXPR1 > 2

#CSEK:[21, 1, 1]; INDEX33555545(T1),FULL_SCAN

XSORT(对记录进行排序):将 XFLT 操作符传递来的记录插入到 XSORT 维护的临时空间中的合理位置,按 c1 进行有序排列。然后将控制权传给 XFLT 以取得下一条符合条件的记录。等处理完所有符合条件的记录。XSORT操作符才会将控制权限传给 RSET。

6.是不是查询语句中一旦出现 order by 字句,执行计划中就会出现 XSORT 操作

符?

不是。比如,查询语句“selectc1 from t1 order by c1;”对应的执行计划为:

#RSET:[0, 0, 0];

#CSEK:[0, 0, 0]; UIT1C1(T1), FULL_SCAN

从执行中我们可以看出,达梦直接对索引 UIT1C1进行全索引扫描,对于得到的每一条记录不需要进行 XSORT 排序操作,直接放入 RSET(结果集)中。因为索引 UIT1C1 本身就是按照 c1 进行排序的。

7.有文档说,对于语句“select max(c1) from t1”,可以在 c1 列上创建索引从而查询速度变快。那么在执行计划中是如何体现的呢?

查询语句“selectmax(c1) from t1”对应的执行计划:

#RSET:[0, 0, 0];

#XEVL:[0, 0, 0];

#FAGR:[0, 0, 0]; function_num(1)

在这个执行计划中,我们没有看到 CSEK 操作符。因为 c1 上存在索引 UIT1C1,该索引叶子节点的最右端就是 c1 的最大值。FARG 直接返回该最大值。语句“select min(c1) from t1;”、语句“select count(*) fromt1;”的执行原理一样。XEVL是表达式计算,本文不进行讲解。

8.如果列上存在索引,如何理解中的 group by 操作?

查询语句“selectc1,count(*) from t1 where c1>=2 group by c1;”对应的执行计划为:

#RSET:[11, 1, 1];

#XEVL:[0, 0, 0];

#SAGR:[0, 0, 0]; group_by_num(1),function_num(1)

#CSEK:[11, 1, 1]; UIT1C1(T1), INDEX_GE_SEARCH

我们可以得到,CSEK使用了索引UIT1C1进行了范围查找。首先传递给SARG的是连续的 c1=2 的记录组,然后是 c1=3 的记录组,然后是 c1=4 的记录组,……

此处 SARG 的执行流程是

1)从 CSEK 取得一条 c1=2 记录,将计数加 1,

2)从 CSEK 取得下一条记录,如果该记录满足 c1,将计数+1。

3)重复执行步骤 2,直到取得第一条不满足 c1=2 的记录,将(2,对应的计算)传递给 XEVL,再传给 RSET(结果集)。接着对 c1=3 的记录组执行同样的流程。依此类推,直到处理完所有符合条件的记录。

这里我们的分组函数是count(*),如果是其它的分组函数,处理过程类似。

9.如果列上不存在索引,如何理解中的 group by 操作?

查询语句“selectc2,count(*) from t1 where c2>=2group by c2;”对应的执行计划为:

#RSET:[21, 1, 1];

#XEVL:[0, 0, 0];

#HAGR:[0, 0, 0]; group_by_num(1),function_num(1)

#XFLT:[0, 0, 0]; EXPR0 >= 2

#CSEK:[21, 1, 1]; INDEX33555550(T1),FULL_SCAN

这里因为 c2 上没有索引,HARG 的作用是 HASH 分组。

HARG 的执行流程是:

1)从 XFLT 取得一条记录

2)记录的 c1=m,如果在 hash 表中已经对应项,计数+1,如果不存在对应项,在创建一个新的 hash 项。

3)所有的符合过滤条件的记录处理完成之后,HARG 才会将控制权限传给上层操作符,HARG 每次向上层操作符传递一条(m,m 对应的计数)。

这里我们的分组函数是 count(*),如果是其它的分组函数,处理过程类似。

推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了通过mysql命令查看mysql的安装路径的方法,提供了相应的sql语句,并希望对读者有参考价值。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
author-avatar
拍友2502882315
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有