百倍性能的PL/SQL优化案例(r11笔记第13天)

我相信你是被百倍性能的字样吸引了,不过我所想侧重的是优化的思路,这个比优化技巧更重要,而结果嘛,其实我不希望说成是百倍提升,“”自黑“”一下。

    有一个真实想法和大家讨论一下,就是一个SQL语句如果原本运行20秒,优化到了1秒,性能提升该说是20倍还是提高了95%。当然还见过一种说法,一条SQL语句每次运行20秒,每天运行100次,优化后每次运行1秒,运行还是100次,那么性能提升是说成优化累计时间为100*20-100=1990秒?

好了,我们来看看PL/SQL的优化,前期自己分析了一些信息,可以参考闪回区报警引发的性能问题分析(r11笔记第11天)

总体来说就是数据库层面的闪回区暴增,很快就接近报警阈值。

发现其中的一个重要因素就是一个update操作时间极长,大概是4个小时,而且资源消耗巨大。

SQL_FULLTEXT
----------------------------------------------------------------------------------------------------
UPDATE CARDINFO A SET A.MAX_LEVEL = NVL((SELECT USER_CLASS FROM ROLE_CLASS_INFO B WHERE A.GROUPID =
B.GROUP_ID AND B.CN_GUID = A.ROLE_GUID), A.MAX_LEVEL) WHERE DRAWED = 'Y'而经过数据分析发现这是一个规律性的变化,是在每周二会触发一次。经过确认这是一个scheduler JOB运行导致。而其中的关键就是调用的存储过程。


好了,重点就是存储过程,当然里面的逻辑还是有一些复杂。我简化一下。


简单解释一下,数据库a的表card_new中会存储一些礼包卡的数据,用户激活卡信息之后就会插入一条记录。而数据库b则是一个统计数据库,会从数据库a中基于规则表tasklist抽取这些数据,然后在统计端基于业务需求做信息的变更校准,信息都在cardinfo这个表里。规则表tasklist简单补充一下,就好像我们的手机卡号,比如152xxxx001-152xxxx999是一个号段,里面定义的就是这些信息,从源库按照这个规则抽取。

SQL> select count(*)from card_new where cardid between 'j23450010000' and 'j23500009999';  
  COUNT(*)
----------  
      5000

存储过程的信息大体如下

CREATE  or replace PROCEDURE        "PROC_UPDATE_CARDINFO"
AS
BEGIN
  for cur in (select * from tasklist where is_droped = 'N') loop
    MERGE INTO cardinfo a
    USING (SELECT *
             FROM card_new@tmp_link t
            WHERE t.cardid >= cur.t_start
              AND t.cardid <= cur.t_end
              ) b
    ON (a.cardid = b.cardid)
    WHEN MATCHED THEN
      UPDATE
         SET a.groupid   = b.GROUPID,
             a.role_guid = b.role_guid,
             a.drawed    = b.drawed,
             a.max_level = b.max_level
    WHEN NOT MATCHED THEN
      insert
        (cardid, groupid, role_guid, drawed, max_level)
      values
        (b.cardid, b.groupid, b.role_guid, b.drawed, b.max_level);
    COMMIT;
  end loop;

  /** 做字段1的映射变更*/
  UPDATE cardinfo a
     SET a.used_jewel = (SELECT jewel_total
                           FROM role_costs_info b
                          WHERE b.GROUP_ID = a.groupid
                            AND b.cn_guid = a.role_guid)
   WHERE drawed = 'Y' and cardid in(select cardid from tmp_cardinfo);
  COMMIT;

  /**  做字段2的映射变更**/
  UPDATE cardinfo a
     SET a.max_level = nvl((SELECT user_class
                             FROM role_class_info b
                            WHERE a.groupid = b.GROUP_ID
                              AND b.cn_guid = a.role_guid),
                           a.max_level)
   WHERE drawed = 'Y' and cardid in(select cardid from tmp_cardinfo);
  COMMIT;
END;
/

上面的表,除了规则表tasklist是不到1万条数据库(类似号段的数据),其它的数据量都在亿级,所以优化空间很大,优化难度不小。

    和开发同学简单了解了需求之后,我的初步结论是update的部分有待提高,因为update的部分变更都是全表更新,这个影响面较大,没法确定增量的数据,基本上按照1周的频率来说,增量数据应该会在百万以内。而查看后面几个update的部分,发现变更的数据量都在千万级别,性能极差。

不过在优化的过程中,感觉我似乎偏离了方向,因为目标端按照现有的条件和补充条件发现始终变更的数据量太大,都是千万级别,和预期相去甚远,简单来说,按照目前的条件得到的数据不是增量数据,所以我的注意力就关注在了源头的数据抽取上。

因为源库的配置较好,使用了PCIE-SSD,查询亿级大表也蛮给力,我在备库查询了一下数据的情况。

SQL> SELECT count(t.cardid)
  2  FROM card_new t ,tasklist cur
  3  WHERE t.cardid >= cur.t_start
  4  AND t.cardid <= cur.t_end;
COUNT(T.CARDID)
---------------
      599845975

一看结果有5亿多条数据,当然大家仔细看,其实语句本身也是有问题的。

其实按照逻辑抽取的数据有2亿,也就是源库表中所有的数据。

如此一来,下游的数据变更都会直接影响,导致了现在的状况。

所以瓶颈很明显,在两个地方
1.抽取的时候对线上业务有性能压力,是全量抽取
2.更新的时候是全量更新,字段匹配数据范围太大

改进思路相对就很简单了。

  1. 明确增量的数据
  2. 使用临时表或者是在cardinfo中标记增量数据进行增量数据变更
  3. 进行完整的数据测试,保证性能改进真实有效。

我们来逐个说一下。

  1. 增量的数据,我查看了源表的字段,里面有一个基于时间的字段,看字段的名字应该是礼品卡的激活时间。和开发同事进行了确认,这个地方明确下来。

我们按照这样的思路来看,增量数据大概在7万左右。

 SQL> select count(*)from card_new where DRAWDATE>sysdate-10;
  COUNT(*)
----------
     78174

如此一来就抓住了问题的本质,后面的更新部分就可以限制条件,避免全量更新。我就创建建了一个临时表来处理。得到从源库抽取所得的增量数据。

2.增量数据变更优化

原本的更新是这样的逻辑,

 UPDATE cardinfo a
     SET a.used_jewel = (SELECT jewel_total
                           FROM role_costs_info b
                          WHERE b.GROUP_ID = a.groupid
                            AND b.cn_guid = a.role_guid)
   WHERE drawed = 'Y' ;

改进之后,限制了条件,就是下面的形式

 UPDATE cardinfo a
     SET a.used_jewel = (SELECT jewel_total
                           FROM role_costs_info b
                          WHERE b.GROUP_ID = a.groupid
                            AND b.cn_guid = a.role_guid)
   WHERE drawed = 'Y' and cardid in(select cardid from tmp_cardinfo);

当然还有一些小细节处做了改进,再次先不赘述。

3.性能测试

接下来就是性能测试了,如何真实的模拟测试这个问题,11g中要充分利用Sapshot Standby的福利。

备库切换为Snapshot Standby的方法
dgbroker中把当前的备库设置为disable
然后使用sqlplus在备库操作:

recover managed standby database cancel; --取消日志应用
alter database convert to snapshot standby; --切换为Snapshot Standby
alter database open;  --切换后打开数据库
select database_role,open_mode from v$database;  --检查变更是否生效

然后开始性能测试,我把数据源指向了源库对应的备库,这样对线上就没有直接的压力。在目标数据库中修改存储过程,运行测试。

SQL> exec PROC_UPDATE_CARDINFO1;
PL/SQL procedure successfully completed.
Elapsed: 00:01:04.38

原本执行至少4个小时的存储过程现在1分钟即可搞定。

完成测试,开始恢复备库为Physical Standby:
sqlplus备库:  shutdown immediate
 startup mount
 alter database convert to physical standby; --切换数据库为physical standby
shutdown immediate --修改后数据库为nomount,重新启动
startup mount
select database_role,open_mode from v$database;
alter database open;
然后在主库使用DG Broker来enable原来的备库即可。

小结

整个一个流程走下来,让我对这个问题的认知,从原本的闪回区报警逐步发掘,扩展到PL/SQL的存储过程实现,当然这个部分还是花了些时间熟悉了下业务,为了更好的满足优化需求,优化中尤其需要牢牢把握性能瓶颈,抓住本质,然后逐个击破即可。而对于性能问题的测试,Snapshot Standby就是一个很不错的补充。评估运行时间等都会更加真实有效。

最后的性能提升,从4个小时提升为1分钟。

--------------------------

一周以后,我再次跟踪这个问题,确认已经修复。闪回前的使用率大大降低。

而实际的SQL执行情况比预期还要好一些,原本的update语句执行需要个把小时,当前执行只需要1秒钟。

时间: 2024-11-10 11:52:39

百倍性能的PL/SQL优化案例(r11笔记第13天)的相关文章

Oracle PL/SQL入门案例实践_oracle

正在看的ORACLE教程是:Oracle PL/SQL入门案例实践. 前面已经了解了关于PL/SQL编程的基础,本文将结合一个案例来加深对这些知识点的理解. 一. 案例介绍 某数据库有两张表,是关于某公司员工资料.薪水和部门信息的,它们分别是emp表和dept表,两张表的结构如下: 要求如下: 1.按照上表结构建立相应的表,并每张表写入5组合法数据. 2.操纵相关表,使得"技术部"的员工的薪水上涨20%. 3.建立日志,追踪薪水变动情况. 4.建立测试包. 二. 案例的分析与实现 从前

使用SQL Profile进行SQL优化案例

一个社保系统的自助查询系统查询个人医疗费用明细的查询语句要用一分多钟还没查询出来,语句如下: select * from  v_zzzd_ylbx_ylfymxcx where aac002='430703198202280017' 从上面的语句可知是从视图 v_zzzd_ylbx_ylfymxcx中查询数据.v_zzzd_ylbx_ylfymxcx视图的创建语句如下: create or replace view v_zzzd_ylbx_ylfymxcx as select a.indi_id

【重磅干货】看了此文,Oracle SQL优化文章不必再看!

听"俊"一席话,胜读十年书.看了这篇由DBA+社群联合发起人丁俊大师(网名:dingjun123)分享的SQL优化大作,其他Oracle SQL优化文章都不必再看了!   专家简介    丁俊 网名:dingjun123 DBA+社群联合发起人   性能优化专家,Oracle ACEA,ITPUB开发版资深版主.8年电信行业从业经验,在某大型电信系统提供商工作7年,任资深工程师,从事过系统开发与维护.业务架构和数据分析.系统优化等工作.擅长基于ORACLE的系统优化,精通SQL.PL/

看了此文,Oracle SQL优化文章不必再看!

  第一章 看了此文,Oracle SQL优化文章不必再看! DBAplus社群 | 2015-11-17 23:44 目录SQL优化的本质 SQL优化Road Map 2.1 制定SQL优化目标 2.2 检查执行计划 2.3 检查统计信息 2.4 检查高效访问结构 2.5 检查影响优化器的参数 2.6 SQL语句编写问题 2.7 SQL优??\x2F限制导致的执行计划差 SQL优化案例 SQL执行计划获取 4.1 如何获取准确的执行计划 4.2 看懂执行计划执行顺序 一SQL优化的本质 一般来

冻结时间倒数前一小时,记一次步步惊心的SQL优化

作者介绍 黄浩:从业十年,始终专注于SQL.十年一剑,十年磨砺.3年通信行业,写就近3万条SQL:5年制造行业,遨游在ETL的浪潮:2年性能优化,厚积薄发自成一家.   9月版本是一个大版本,上上下下都在紧锣密鼓地张罗着.   9月10日版本上线,8日开始,能明显的感觉到大战前战鼓擂动人喊马嘶的紧张氛围.项目组人头簇动,奔走如织:邮箱内,关于BUG单通报及处理意见的邮件,在这个骄阳似火的南方,犹如冷冽寒冬时北方的雪花般漫天纷飞.     14:40  主动出击   快下午三点钟的时候,一片雪花悄

MySQL优化案例系列-mysql分页优化_Mysql

通常,我们会采用ORDER BY LIMIT start, offset 的方式来进行分页查询.例如下面这个SQL: SELECT * FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 100, 10; 或者像下面这个不带任何条件的分页SQL: SELECT * FROM `t1` ORDER BY id DESC LIMIT 100, 10; 一般而言,分页SQL的耗时随着 start 值的增加而急剧增加,我们来看下面这2个不同起始值的分页SQL执行

细致入微 | 让 SQL 优化再多飞一会儿

第一章  细致入微 | 让 SQL 优化再多飞一会儿 云和恩墨 | 2016-04-12 20:54 怀晓明 云和恩墨性能优化专家 本文来自于本周四云和恩墨大讲堂怀晓明老师的分享. 内容:作为 DevOps 的最佳落地方式之一的 SQL 审核,如何才能做好?这是一件很有挑战性的事情,他将通过两个具体案例,来展现 SQL 审核工作如何才能做得更好,更有价值.简言之就是八个字--"细致入微,方显价值 ". 我们都知道,细致认真,可以将一件事情做得尽可能完美,在 SQL 审核与优化中,同样需

Oracle之PL/SQL学习笔记

自己在学习Oracle是做的笔记及实验代码记录,内容挺全的,也挺详细,发篇博文分享给需要的朋友,共有1w多字的学习笔记吧.是以前做的,一直在压箱底,今天拿出来整理了一下,给大家分享,有不足之处还望大家批评指正.   PL/SQL定义:PL/SQL是由Oracle开发,专门用于Oracle的程序设计语言. PL---Procedural Language. SQL-Structure QueryLanguage.PL/SQL包括过程化语句和SQL语句     PL/SQL的单位:块. 一个块中可以

MySQL下的RAND()优化案例分析_Mysql

众所周知,在MySQL中,如果直接 ORDER BY RAND() 的话,效率非常差,因为会多次执行.事实上,如果等值查询也是用 RAND() 的话也如此,我们先来看看下面这几个SQL的不同执行计划和执行耗时. 首先,看下建表DDL,这是一个没有显式自增主键的InnoDB表: [yejr@imysql]> show create table t_innodb_random\G *************************** 1. row *************************