运维技巧 - 活用临时表隔离冷热数据

编辑手记:Oracle给了我们很多工具,在日常数据库管理中活用这些工具方可发挥最大效能。

作者简介: 张洪涛 富士康 DBA

在数据库监控过程中发现考勤数据库上Employees_ControlData存储过程执行时间需20分钟。这个存储过程逻辑很简单,就是打开一个游标,做LOOP循环,再删除重复数据,结构如下:

CREATE OR REPLACE PROCEDURE Employees_ControlData IS tmpVar NUMBER(6); tmpVar1 NUMBER(6); tmpVar2 NUMBER (6); tmpVar3 NUMBER(6); CURSOR EMP_NO IS SELECT WORKNO FROM ZZ_EMPLOYEES; BEGIN --LINE 12行 FOR USERID INEMP_NO LOOP ...... END LOOP;

--Line128行,删除重复数据

Delete .... ; COMMIT; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; WHEN OTHERS THEN -- Consider logging the error and then re-raise RAISE; END Employees_Controldata;

ZZ_EMPLOYEES有4万多笔数据,LOOP也会执行4万多次。AWR报告与GridControl监控都显示效能瓶颈在LOOP循环中六处SQL,再看一下LOOP循环中六处问题SQL:

--Line14行

SELECT COUNT(*) INTO tmpVar FROM CPYTGL.EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO ANDTO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='F' AND INOROUT_TIME >SYSDATE-3 AND ROWNUM<6;

--Line38行

INSERT INTOEMPLOYEE_CONTROL_EXCEPTION SELECT DISTINCT * FROM EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='F' AND INOROUT_TIME >SYSDATE-3;

--Line49行

INSERT INTOEMPLOYEE_CONTROL_EXCEPTION SELECT DISTINCT * FROM EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='F' AND INOROUT_TIME >SYSDATE-3 AND INOROUT_TIME NOTIN (SELECT INOROUT_TIME FROM EMPLOYEE_CONTROL_EXCEPTION WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME, 'YYYY/MM/DD')= TO_CHAR(SYSDATE-2, 'YYYY/MM/DD') AND TYPE='F' AND INOROUT_TIME >SYSDATE-3);

--Line72行

SELECT COUNT(*) INTO tmpVar1 FROM CPYTGL.EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO ANDTO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='L' AND INOROUT_TIME >SYSDATE-3 AND ROWNUM<6;

--Line82行

INSERT INTO EMPLOYEE_CONTROL_EXCEPTION SELECT DISTINCT * FROM EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='L' AND INOROUT_TIME >SYSDATE-3;

--Line87行

INSERT INTO EMPLOYEE_CONTROL_EXCEPTION SELECT DISTINCT * FROM EMPLOYEE_CONTROL WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND TYPE='L' AND INOROUT_TIME >SYSDATE-3 AND INOROUT_TIME NOTIN(SELECTINOROUT_TIME FROM EMPLOYEE_CONTROL_EXCEPTION WHERE EMP_NO = USERID.WORKNO AND TO_CHAR(INOROUT_TIME, 'YYYY/MM/DD')= TO_CHAR(SYSDATE-2, 'YYYY/MM/DD') AND TYPE='L' AND INOROUT_TIME >SYSDATE-3);

这六处SQL都查询了EMPLOYEE_CONTROL考勤信息表。此表已有近两亿笔数据,根据INOROUT_TIME字段进行分区,并对相关字段建立了索引。

CREATE INDEX EMPLOYEE_CONTROL_EMPNOINOROUT ON EMPLOYEE_CONTROL (TO_CHAR("INOROUT_TIME",'YYYY/MM/DD'),EMP_NO) LOCAL; SQL> SELECT column_name 2 FROM dba_part_key_columns 3 WHERE name = 'EMPLOYEE_CONTROL'; COLUMN_NAME -------------------------------------------------------------------------------- INOROUT_TIME SQL> SELECT num_rows 2 FROM dba_tables 3 WHERE table_name = 'EMPLOYEE_CONTROL'; NUM_ROWS ---------- 193585044

EMPLOYEE_CONTROL考勤信息表至少需保留一年数据备查。六条SQL已加INOROUT_TIME >SYSDATE-3条件,执行计划中可进行分区裁剪,删减数据这条路行不通。

WHERE中的条件也正确使用了索引,似乎所有常规优化方法都已用上,如何才能进一步提升存储过程LOOP循环执行速度?

我们再分析这六条SQL,在WHERE条件中都出现了对EMPLOYEE_CONTROL表以下限定条件:

TO_CHAR(INOROUT_TIME,'YYYY/MM/DD')= TO_CHAR(SYSDATE-2,'YYYY/MM/DD') AND INOROUT_TIME >SYSDATE-3 AND TYPE='L' AND TYPE='F'

存储过程实际要读取的只有一天的数据,这部分数据一般只有10万笔为热点数据。如果我们先把此部分数据单独读出,在LOOP循环中就可只读取临时表内容,避免4万次读取有两亿笔数据的EMPLOYEE_CONTROL考勤资料表。

依此思路,我们先建立一个临时表,并为临时表EMP_NO字段添加索引:

CREATE GLOBAL TEMPORARY TABLE CPYTGL.EMPLOYEE_CONTROL_TEMP ON COMMIT DELETE ROWS AS SELECT * FROMCPYTGL.EMPLOYEE_CONTROL WHERE 1 = 0; CREATE INDEX CPYTGL.EMPLOYEE_CONTROL_TEMP_NOON CPYTGL.EMPLOYEE_CONTROL_TEMP (EMP_NO);

再在存储过程头部将符合条件的数据取出:

INSERT INTO cpytgl.EMPLOYEE_CONTROL_TEMP SELECT* FROM CPYTGL.EMPLOYEE_CONTROL WHERE TO_CHAR (INOROUT_TIME, 'YYYY/MM/DD') = TO_CHAR(SYSDATE - 2, 'YYYY/MM/DD') AND INOROUT_TIME > SYSDATE- 3 AND TYPE IN('F', 'L');

这样就可改写消耗资源的六条SQL查询临时表。以第14行SQL为例,需改写为:

--Line14行

SELECT COUNT (*) INTO tmpVar FROM cpytgl.EMPLOYEE_CONTROL_TEMP WHERE EMP_NO = USERID.WORKNO AND TYPE = 'F' AND ROWNUM< 6;

LOOP循环中六条SQL改为查询10万笔记录的临时表后,存储过程只需1分钟即可跑完。相较之前20分钟运行时间有大幅度提升。

此例核心为使用临时表隔离冷热数据。DBA一次调优不一定能想出最佳方法,通过对应用的不断深入观察,以及Oracle工具的合理使用,加上一点点灵光一现那些看似解决不了的难题都可一一化解。

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

原文发表时间:2017-07-19

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4055
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.2K7
来自专栏菩提树下的杨过

Flash/Flex学习笔记(23):运动学原理

先写一个公用的小球类Ball: package{ import flash.display.Sprite; //小球 类 public class B...

25410
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2232
来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

5676
来自专栏芋道源码1024

熔断器 Hystrix 源码解析 —— 断路器 HystrixCircuitBreaker

本文主要基于 Hystrix 1.5.X 版本 1. 概述 2. HystrixCircuitBreaker 3. HystrixCircuitBreaker....

5377
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

32510
来自专栏魂祭心

原 canvas绘制clock

4244
来自专栏张善友的专栏

Mix 10 上的asp.net mvc 2的相关Session

Beyond File | New Company: From Cheesy Sample to Social Platform Scott Hansel...

2627
来自专栏hbbliyong

WPF Trigger for IsSelected in a DataTemplate for ListBox items

<DataTemplate DataType="{x:Type vm:HeaderSlugViewModel}"> <vw:HeaderSlug...

4074

扫码关注云+社区