百倍性能的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.更新的时候是全量更新,字段匹配数据范围太大
改进思路相对就很简单了。
明确增量的数据
使用临时表或者是在cardinfo中标记增量数据进行增量数据变更
进行完整的数据测试,保证性能改进真实有效。
我们来逐个说一下。
增量的数据,我查看了源表的字段,里面有一个基于时间的字段,看字段的名字应该是礼品卡的激活时间。和开发同事进行了确认,这个地方明确下来。
我们按照这样的思路来看,增量数据大概在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秒钟。
百倍性能的PL/SQL优化案例(r11笔记第13天)的更多相关文章
- SQL优化案例—— RowNumber分页
将业务语句翻译成SQL语句不仅是一门技术,还是一门艺术. 下面拿我们程序开发工程师最常用的ROW_NUMBER()分页作为一个典型案例来说明. 先来看看我们最常见的分页的样子: WITH CTE AS ...
- 数栈SQL优化案例:隐式转换
MySQL是当下最流行的关系型数据库之一,互联网高速发展的今天,MySQL数据库在电商.金融等诸多行业的生产系统中被广泛使用. 在实际的开发运维过程中,想必大家也常常会碰到慢SQL的困扰.一条性能不好 ...
- mysql的sql优化案例
前言 mysql的sql优化器比较弱,选择执行计划貌似很随机. 案例 一.表结构说明mysql> show create table table_order\G***************** ...
- SQL 优化案例 1
create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...
- 转://Oracle PL/SQL 优化与调整 -- Bulk 说明
一. Bulk 概述 本来只想测试一下Bulk Collect 和update性能的,但发现Bulk 的东西还是很多的,在OTN上搜了一些,整理如下. 1.1 Bulk Binding 和 Bulk ...
- SQL 优化案例
create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...
- Oracle PL/SQL语句基础学习笔记(上)
PL/SQL是ORACLE对标准数据库语言的扩展,ORACLE公司已经将PL/SQL整合到ORACLE server和其它工具中了,近几年中很多其它的开发者和DBA開始使用PL/SQL,本文将讲述PL ...
- PL/SQL语言的学习笔记
一.PL/SQL简介1.什么是PL/SQL程序?(PL/SQL是对SQL语言的一个扩展,从而形成的一个语言) 2.PL/SQL语言的特点(操作Orcale数据库效率最高的就是PL/SQL语言,而不是C ...
- PL/SQL基础2(笔记)
1 第一个PL/SQL的程序 DECLARE BEGIN DBMS_OUTPUT.PUT_LINE('Hello World!'); END; / --2一个简单的PL/SQL程序 DECLARE v ...
随机推荐
- Java Synchronized的原理
我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: public class SynchronizedDemo{ public void method(){ syn ...
- 10. CTF综合靶机渗透(三)
靶机说明 斗牛犬工业公司最近将其网站污损,并由恶意德国牧羊犬黑客团队拥有.这是否意味着有更多的漏洞可以利用?你为什么不知道?:) 这是标准的Boot-to-Root.你唯一的目标是进入根目录并看到祝贺 ...
- 使用jquery插件实现图片延迟加载--懒加载技术
原文链接:http://www.cnblogs.com/lei2007/archive/2013/05/31/3110725.html 感谢作者.以下为原文,备忘仅供自己学习. 第一:lazyLoad ...
- [java基础]short s1 = 1; s1 = s1 + 1;有什么错?short s1 = 1; s1 += 1;有什么错?
为什么写这篇文章是因为搜到的答案里并没有阐明s1 = s1 + 1为什么就要转换为int类型. 由一下实验可知: public class test { public static void main ...
- weex前端式写法解决方案---eros
前言 如果想用前端的方式写一个app怎么办呢? 如果你用的是 React,那么它已经有了一个比较完善的体系跟社区.如果你用的是Vue又不想花费太多时间去重新学习React,那么目前比较靠谱的方案就是w ...
- [笔记]解决git本地仓库不能上传到gitee远程仓库的问题
关键词:git.gitee.码云.上传远程仓库失败 1.gitee有一个远程仓库名字是CommandModel,里面只有两个README文件 2.假如我目录 D:\eclipse\workspace ...
- php 获取当前的访问的ip
<?php function get_client_ip() { $ip = $_SERVER['REMOTE_ADDR']; if (isset($_SERVER['HTTP_CLIENT_I ...
- 《OD学hadoop》20160903某旅游网项目实战
一.大数据的落地点 1.数据出售 数据商城:以卖数据为公司的核心业务 2. 数据分析 百度统计 友盟 GA IBM analysis 3.搜索引擎 4. 推荐系统 mahout 百分比 5.精准营销 ...
- ubuntu命令错误集
1.在ubuntu命令行使用rz从windows传输文件时出现乱码 解决方法:使用 rz -e 选项进行传输,一般小文件传输不用加 -e 选项,大文件传输需要.
- Apple开发者账号更改公司名称
对于大多数的开发者而言,可能很少遇见要修改账户的公司名称的问题,本来公司之前是这个名称,但是后来改名为另外的名称,这样的话在Apple开发者账号上的公司名称再用就不好了,所以就需要到Apple dev ...