见微知著:一条 SQL 性能问题引发的核心系统悲剧


黄廷忠(网名:认真就输)

云和恩墨技术专家

个人博客:http://www.htz.pw/

本篇整理内容是黄廷忠在“云和恩墨大讲堂”微信分享中的讲解案例,SQL 优化及 SQL审核,是从源头解决性能问题的根本手段,无论是开发人员还是DBA,都应当持续深入的学习 SQL 开发技能,从而为解决性能问题打下根基。

第一篇为:性能为王:SQL标量子查询的优化案例分析

第二篇为:SQL审核:OR展开与子查询优化案例详解

本篇为系列案例之三:IN子查询返回结果集很小

这是不久前在一个客户现场遇到的一条 SQL 性能问题,此 SQL 子查询结果集返回最多10行,但是整个 SQL 的性能却不好,此 SQL 最后还导致了一个核心系统故障,引起了一个悲剧的事情。

 

业务反应慢,查询 v$session 发现同时有24个会话在执行此 SQL:7ug8q9myb0bsz,由于此 SQL 性能不好引起大量的 GC 等待,导致其它的业务受影响。

SQL性能问题诊断

下面直接给出常量的 SQL

Child_number 0

 

Child_number 1

首先说明一下,是 OLTP 环境。也就意味着要快速的返回结果,并且多数情况下,SQL 返回的结果集不多。

 

在 SQL 切图中,有两处我们用红色的箭头标识出来,这部分信息需要我们关注的。在整个 SQL 中,就只存在2处过滤信息,一个是 redu_owner_id,一个是 status_cd。但是 status_cd 在两个子执行计划中都是相同的,所以这里就只剩余 redu_owner_id 这列了,我们也可以执行 redu_owner_id 所在的 OP 这个表,肯定是驱动表,并且 redu_owner_id 这列应该存在数据倾斜的情况那么 redu_owner_id 返回的结果集将直接影响整个 SQL 性能的好坏。

 

下面继续查看 SQL 部分,可以发现一个重要的信息就是在子查询中存在 rownum<10,也就意味子查询最多返回10行。在 OLTP 系统中,存在一个表最后最多返回10行的情况,这里也就大概想到了用子查询做去驱动表了,如果执行计划中,没有用子查询做驱动表,那么很有肯能执行计划就是错误的,那么这里的自己认为的驱动表与之前根据 SQL 前部分猜测出来当前执行的驱动表(OP)不一样。

 

下面查看执行计划 

在执行计划中,我们看到当前执行计划的驱动表示 OFFER_PROD(OP) 这个表,与之前我们猜想一样,那么基本可以肯定,redu_owner_id 列的数据存在倾斜,当返回大量结果集时,性能就很不好。

 

在执行计划中,这里特意把子查询标记出来,就是需要引起重视,子查询当着一个整体与主查询做 HASH 连接,没有作为驱动表走 NL,也就可以肯定整个执行计划连最基本的驱动表都选择错误。下图可以更直观的看到。 

这里做个补充:子查询当着整体,也就是被当着一个视图与主查询做关联,什么情况下子查询会当着一个整体呢? 

其实 MOS 有相关的文档说明的,大家可以去 MOS 一下,在本案例是由于 ROWNUM<10 导致的。

基础信息分析

在 V$SQL 中查看每个 child 的统计信息

这里看到,存在两个子游标,他们的执行计划相等,但是两个子优化的性能相差很大,并且性能不好的子优化执行次数很多。 

在上面我们提到主查询就只存在两个过滤条件。执行计划+谓词信息可以看到驱动表使用那个列来过滤数据。


 

在上面一直在说 redu_owner_id 这个列存在数据倾斜,那么下面来证实一下:

 

下面来查看,redu_owner_id 的值的分布,两个不同绑定变量返回的行数: 

通过这个信息,我们知道了,上面 SQL 由于列的值存在倾斜,导致 SQL 执行计划部分值执行很快,部分值执行很慢。

 

大家可能会说,在11G中,SQL 引入了 ACS 功能,但是很不幸的事在客户这里 ACS 都是禁用了的。

SQL 的修改

下面就是怎么来优化这个 SQL,在上面提到了子查询中最多返回10行,可以用于做NL的驱动,要让子查询的表做驱动表,应该怎么来修改 SQL?

在上一个案例中,通过了with as 的方式来改写。 

这个案例就不修改 SQL,通过提示(Hints)来达到目的。

这里使用 cardinality 提示,在 SQL 解析的时候告诉 CBO 表上存在多少行。表上存在的行数越少,也就意味着访问表的成本越低。

 

下面我们拿返回8611行的绑定变量来做测试

 

添加提示后的 SQL 如下:

 

红色部分就是添加的提示,执行计划如下:

 

可以看到,子查询的结果集已经作为驱动表了。

性能优化效果对比
 统计信息 

每次的逻辑读从原来的369,927降低到现在的45 ,性能提升很明显,并且主要解决了 RAC之间的 GC 等待,不影响其它的业务了。

 

优化 SQL 后,CPU 使用率从原来的70%左右直接下降到25%左右,此系统的主机性能很 NB 的,8路的 PC ,E7 的 CPU。

总结
此案例结束,主要提到两个知识点:

  1. 列的值分布不均匀,导致 SQL 性能出问题
  2. 通过 cardinality 来指定表的行数,达到指定表做驱动表的目的


SQL优化是一项专业的技能,必须深入了解数据库原理才可能做出准确的判断,在开发过程中强化开发人员培训,引入SQL审核、审计,是确保应用高性能的有力手段。
文章转自数据和云公众号,原文链接

时间: 2024-09-19 09:40:02

见微知著:一条 SQL 性能问题引发的核心系统悲剧的相关文章

[20150715]一条sql语句的优化.txt

[20150715]一条sql语句的优化.txt --生产系统发现一条语句. update presc_check t set t.diagnosis=replace(t.diagnosis,',慢性病drugs','') --第1眼看到的感觉真的很想骂人,什么能没有where条件呢? --我把这个表拷贝过来.这个表占用1G多1点,在测试环境执行看看: -- copy from system/xxxx@ip:1521/tyt create presc_check using select * f

[20130319]一条sql语句的优化.txt

[20130319]一条sql语句的优化.txt 生产系统,遇到这样一条语句:SELECT MAX (LENGTH (pe_id)) FROM pe_master_index WHERE SUBSTR (pe_id, 1, 2) = 'TJ'; --真不知道开发人员如何想的,写出这样的语句.字段pe_id是主键.--数据库版本 SQL> select * from v$version where rownum BANNER                                     

浅析_optimizer_null_aware_antijoin引发的SQL性能问题

前几天某客户联系我说之前我们进行存储迁移的系统,有个SQL跑的极慢,根本跑不出来结果.通过VPN登录看了下,SQL确认跑的很慢.开始我很难理解,我们仅仅是进行了存储迁移,数据库基本上没动,为什么会有SQL性能问题呢?  我们先来看看有问题的SQL: SYS@rptdb1> set autot traceonly exp SYS@rptdb1> select a.*,b.rate TAX_RATE,round(a.charge*b.rate/(1+b.rate),0) tax,a.charge-

sql中LEFT JOIN拆分成多条SQL语句 提高sql性能

本文讲解如何把一条带有一个或多个left join或right join的sql语句拆分成多条sql语句.MySQL进行连表查询效率是很低的,特别是数据很大,而且并发量很高的情况,索引都无法解决问题,最好的办法就是把sql语句拆分成多条单表查询的sql. 我们公司电商网站现在要做网站服务化,用java做中间件,PHP调用java接口获取数据,数据表也进行了拆分,分库,要求不使用连表查询,有连表查询的sql语句想办法拆分多条sql语句,然后统一使用java接口. 这样做的目的一是为了网站服务化做调

mybatis SQL性能调优

Mybatis SQL性能调优         1.  Mapper层参数为Map,由Service层负责重载       Mapper由于机制的问题,不能重载,参数一般设置成Map,但这样会使参数变得模糊,如果想要使代码变得清晰,可以通过service层来实现重载的目的,对外提供的Service层是重载的,但这些重载的Service方法其实是调同一个Mapper,只不过相应的参数并不一致.     也许有人会想,为什么不在Service层也设置成Map呢?我个人是不推荐这么做的,虽然为了方便,

ORACLE SQL性能优化系列 (二)

oracle|性能|优化 4. 选择最有效率的表名顺序(只在基于规则的优化器中有效) ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二

ORACLE SQL性能优化系列 (十四) 完结篇

oracle|性能|优化 46.       连接多个扫描 如果你对一个列和一组有限的值进行比较, 优化器可能执行多次扫描并对结果进行合并连接. 举例:     SELECT *     FROM LODGING     WHERE MANAGER IN ('BILL GATES','KEN MULLER');       优化器可能将它转换成以下形式     SELECT *     FROM LODGING     WHERE MANAGER = 'BILL GATES'     OR MA

Oracle数据库的SQL性能问题分析

在Oracle 10.2.0.4数据库中,有一个SQL执行缓慢,超过数分钟无结果,等待事件又是空闲的 SQL*Net message事件,最后只好强行中断. 这个SQL是一个普通的UPDATE语句,where子句中多张表 关联,关联的表都是临时表. update t_fund_product_info set is_valid = 'N' where prdt_id not in (select a.prdt_id from tmp_crm_DX_PRDT_FOR_INFO a, tmp_crm

SQL性能优化:如何定位网络性能问题

一同事跟我反馈他遇到了一个SQL性能问题,他说全表只有69条记录,客户端执行耗费了两分多钟,这不科学呀.要我分析一下原因并解决.我按照类似表结构,构造了一个案例,测试截图如下所示   这 个表有13800KB(也就是13M多大小),因为该表将图片保存到数据库(Item_Photo字段为iamge类型),这个是历史原因,暂且不喷这种 的设计.看来这个SQL执行时间长的性能问题不在于IO和SQL本身执行计划是否有问题,而是在网络数据传时间上(服务器与客户端位于异地,两地专线带宽 6M,不过很多应用.