【云和恩墨大讲堂】复合索引与绑定变量

讲师简介

邓秋爽(小鱼)

云和恩墨专家,有超过5年超大型数据库专业服务经验,擅长oracle 数据库优化、SQL优化和troubleshooting

今晚的恩墨大讲堂将有我为大家分享SQL审核中的两个典型案例,如下:

1复合索引前导列选择 2绑定变量分享——某业务SQL性能问题分析

在介绍这两个case之前先对目前我们的SQL审核交付服务做一个简单说明,我们目前的SQL审核交付服务包括以下几个部分:

  • 待上线SQL评审,预估性能瓶颈——控制系统新上线SQL质量
  • 线上系统SQL性能监控、优化——持续优化系统线上SQL,降低系统资源消耗
  • 线上系统预警、故障诊断分析——及时对系统故障进行干预和处理,最快响应客户的请求
  • 数据库版本变更、优化器参数调整评估——重大数据库参数调整评估,数据库版本变更评估
  • 对业务需求、设计进行评审——对表、索引结构设计进行咨询——业务切合数据库设计,最有效的管控系统风险

SQL审核服务工作两年来,我们一直都是在做线上和待上线SQL代码的审核分析,通过专业的SQL审核团队和方法我们取得了一定的成效,系统上线变更均没有出现重大故障,得到了客户的认可。

言归正常,接下来我们来分享这两个case:

1、复合索引前导列选择

待上线SQL审核发现核心业务模块的某SQL是大表全表扫描

执行计划如下:

这里我们看到,有一个全表扫描,优化器评估的是 263k,

以下是表的统计信息:

我们看到有3.2亿的数据量。

可能说到优化全表扫描,大家都会想到要创建索引,那么这个索引如何创建呢?

在执行计划的第五部分谓词部分我截取如下:

这里有service ID 和enddate两个谓词,我们要通过对这两个谓词创建索引进行优化,我们看统计信息里面,enddate的不同值有1628520个,serviceID的不同值有402个,而表有3.2亿的数据量,是很大的。

那么如何创建索引?可能有以下三种方案:

1、 serviceID+enddate复合索引 2、 enddate+serviceID复合索引 3、 enddate的单列索引

我们在创建索引的时候有以下原则:

核心业务SQL要求SQL执行计划尽量最高效,而刚好这个SQL是个核心业务SQL,用户需要响应时间最可能最短,由于复合索引的效率一般是远优于单列索引的,所以enddate的单列这里不再考虑

再看serviceid、enddate两列如何创建复合索引

我们首先来看serviceID+enddate的复合索引

这个索引在索引范围扫描的过程中消耗的逻辑读是33,回表次数达到373。

接下来是enddate+serviceID的复合索引:

这个在索引范围扫描的过程消耗的逻辑读是873,回表次数是1213次。

为什么两者的索引范围扫描消耗的逻辑读相差这么大。我们来看看谓词部分。

前者serviceid+enddate复合索引索引范围扫描全部走access,access就是说我在索引范围扫描的时候,在扫描页块节点中是不存在多余的页块的,也就是扫描的每一个叶块都是满足SQL的谓词条件的。

enddate+serviceid复合索引索引范围扫描enddate字段走access、serviceid走filter,因为在非等值列作为索引前导时,索引范围扫描叶块找到满足enddate的条件后,在双向链表下做索引扫描并不能保证扫描的每一个叶块都是满足serviceid条件的,所以需要取出来之后再过滤一次,这样就多了一次filter的操作。

因此我们看到,两者的区别就在索引范围扫描这里。在第二种方式中,读取的页块里面存在无效的页块。

复合索引的前导的选择:

1.索引的前导尽量让更多的核心业务sql能够使用,这样能够避免再另外创建单独的单列索引 2.对于单独的核心业务的sql语句,要求该SQL的性能尽可能最优,对于同时出现等值和非等值条件(比如范围、like等)时复合索引应该尽量选择where条件中等值条件列作为索引的前导列。例如where条件中同时出现state=X and create_time>sysdate-1,此时应该创建state+create_time的复合索引,state字段作为索引的前导列。

接下来跟大家分享一个跟绑定变量有关的内容

关于绑定变量的问题在OLTP系统中我们经常会遇到,因为在OLTP中存在很多SQL代码都是搞并发的,所以我们希望在SQL语句中尽量能使用到绑定变量,绑定变量最重要的作用就是代码重用

  • 绑定变量主要作用:

用于代码重用,降低系统的硬解析成本

  • 绑定变量的弊端:

绑定变量窥视副作用、自适应游标缺陷、影响执行计划的评估

  • 绑定变量如何使用:

结合绑定变量窥视、直方图、字段的业务类型综合考虑

接下来我们来看一个案例

执行计划是全表扫描,cost计算为1433,Oracle版本12.1.0.2? 带入具体值单次逻辑读和物理读需要4万多,平均返回0行数据,属于核心业务SQL(同业类类型的SQL还有一批)

在这条SQL中,不存在任何的分组或者聚合函数,返回0行数据走全表扫描多半不是最合理的执行计划

我们来看它的执行计划

下面是这张表的统计信息

我们看到processcode的不同值只有112个,number bucket有111,而表的数据量达到139万,我们看到这一列上是有索引的,那这里优化器是否应该走索引更高效了?

1、processcode是倾斜字段且已收集直方图,查询表数据得知processcode=‘UMCPMTELSTE_ZT’占表中数据量比例极少。 2、_optim_peek_user_binds被置为FALSE,窥视特性关闭。 3、索引没有失效、无sql profile、hint等其他影响。 由于processcode=‘UMCPMTELSTE_ZT’占有表中数据比例极少,索引范围扫描此时应该是最合理的执行计划,优化器难道cost计算错了?

添加hint/*+index(a IDX_INTERFACE_COMM_PROCESSCODE)*/比较cost成本

我们看到成本是2513,是远远大于之前的全表扫描的成本的。

CBO根据成本计算会选择全表扫描的执行计划,那么如何优化了:

我想很多DBA都遇到过执行计划异常的情况,我们采取最轻量级的解决方式,就是通过sql profile、sql plan baseline等绑定执行计划。但是这三种都有一个弊端,如果SQL text的变化,比如列的增加或者减少,这时候sql profile、sql plan baseline就会失效。

第二种方式就是通过hint 固化代码。但是这种方式也存在一个问题,我们刚刚提到processcode并不是一个“均配”字段,比如说A对应的值是极少数的,而B对应的数据很多。

那这种情况下我们如果把代码固定的话,如果别的SQL传入的是B,则对于这个业务的SQL执行计划并不是最合理的。

还是回到SQL优化的问题,我们不能避开它,看执行计划的时候,一定要注意谓词的部分,我们发现无论是索引范围扫描还是全表扫描,Oracle的优化器在评估的时候一定是先去评估processcode=:BIZID,评估该谓词条件后通过查询转换来进一步校验‘UMCPMTELSTE_ZT’=:BIZID是否合理

这里我们得出在where条件中同时出现processcode=:BIZID和processcode=具体值(UMCPMTELSTE_ZT)的时候,优化器这里是从最左边开始考虑各个执行计划的执行成本,然后通过查询转换在filter阶段再去校验‘UMCPMTELSTE_ZT’=:BIZID是否合理。

如果说优化器能优先考虑到processcode=‘UMCPMTELSTE_ZT’具体值,则CPU是否会自动选择索引范围扫描呢?

我们将具体值和绑定变量做个替换,where processcode=‘UMCPMTELSTE_ZT’and processcode=:BIZID,执行计划如下:

Oracle在索引范围扫描的时候会直接考虑processcode=‘UMCPMTELSTE_ZT’,然后再第二步的时候检查具体值是否等于绑定变量,做一个逻辑校验。

大家仔细看可能会发现,上面的那段代码事实上是不严谨的,processcode=:BIZID和processcode=具体值(UMCPMTELSTE_ZT)同时出现,而实际的绑定变量都传入的等于前面那个具体值,如果是为了校验传入变量是否和具体值一致也没必要丢给数据库去判断,而开发人员这种写法则导致了该SQL没有选择合适的索引,这种SQL 在评审阶段都是需要我们关注的。

这里我们也提一下个人认为使用绑定变量的标准供大家参考:

绑定变量 - 哪些列适合使用绑定变量

绑定变量窥视关闭、收集直方图环境下:

1、建议使用绑定变量的列:对于流水号、订单号、用户ID、电话号码、身份证 号码、证件号码等,这类列的特点是列的唯一性较好、列的不同值数量跟表的 数据量比较接近,而这类列涉及的sql语句往往存在高并发,写成绑定变量会 大大减少数据库的解析成本。 2、不建议使用绑定变量的列:能够枚举的列(比如状态类型字段,这部分类型 字段往往不同值较少,一般即使上千万的数据表中该列也只有十多个、几十个 的不同值), 即使不写绑定变量也只会产生极少数的sql_id,这部分的oracle 解析消耗微乎其微,并且这种类型的列有可能存在倾斜数据,而如果使用绑定 变量,则对于倾斜数据的评估并不准确,可能导致该列无法走索引范围扫描、 或者影响表JOIN阶段驱动表的选择等。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2016-12-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

mysql数据库开发常见问题及优化

mysql 数据库是被广泛应用的关系型数据库,其体积小、支持多处理器、开源并免费的特性使其在 Internet 中小型网站中的使用率尤其高。在使用 mysql ...

664
来自专栏数据和云

拨云见日—深入解析Oracle TX 行锁(上)

Balance is the only key to optimize. 平衡是性能优化的核心标准。 在刚刚过去不久的第七届数据技术嘉年华上,性能优化专家怀晓明...

3419
来自专栏维C果糖

史上最简单的 MySQL 教程(十九)「范式」

在数据存储之后,凡是能够通过关系寻找出来的数据,坚决不再重复存储,范式的终极目标是减少数据冗余。

3928
来自专栏数据和云

你真的会用索引吗?来看看COUNT(*)到底能有多快

作者简介 ? 案例说明 一个大表的COUNT,究竟能有多快?除类似物化视图的做法,我们所能做到的极限能有多快?这不是一个真实的案例,而是根据笔者在网上发的一篇帖...

3296
来自专栏数据小魔方

MySQL入门学习笔记——七周数据分析师实战作业

本篇推送主要涉及SQL语言中较为复杂的子查询与函数嵌套。 虽然这个MySQL系列取名为MySQL基础入门,但是个人不打算做单个函数的用法总结,或者说简单罗列,...

3737
来自专栏微信公众号:Java团长

一位Java工程师的阶段性工作总结

1.1.1、通常的模块分布:一般如果你要实现一个web应用,你从后台将数据展示到前端页面,在一个比较大的公司,你少不了跟其他项目有交集(你调用他的接口,他依赖你...

601
来自专栏数据分析

SQL Server 性能优化之——系统化方法提高性能

1. 概述 在比较大的范围内找出能够大幅提高性能的区域,并且专注于分析这个区域,这是最有效的优化SQL Server性能的方式。否则,大量的时间和精力可能被浪费...

4126
来自专栏用户画像

实验3.2 复杂的单表查询

熟练掌握SELECT查询语句中的Group by 子句、Having子句的用法,以及汇总函数的使用。

683
来自专栏Golang语言社区

服务器 数据库设计技巧--1

1. 原始单据与实体之间的关系 可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能...

2694
来自专栏Java帮帮-微信公众号-技术文章全总结

数据库三大范式【面试+工作】

设计良好结构的数据库,可以有效减小数据冗余,减少增删改中出现的问题。深入理解数据库设计的三范式,对于设计“健壮的数据库“十分有必要。数据库三范式是设计数据库 时...

3334

扫描关注云+社区