oracle大表执行慢的优化方法,Oracle性能突然变慢的解决思路
场景:某个ETL任务正常情况下每天11:30~12:00之间结束,突然某天后延到13:50,次日15点,然后逐步每天稳定在17:00,同服务器下其他任务也相对变慢。
一、IO层面问题排查
1.查看操作系统资源管理器磁盘活动,确定排名靠前的磁盘IO,定位具体进程,确认是否有异常或不必要的IO进程,此处定位并暂停OGG进程(谨慎停止),观察一整天数据库性能
2.排除系统自动备份任务造成的IO性能资源堵塞,此处排除
经过一天观察,性能虽有些微的提升,但不能排除观察时点任务稀疏的特殊性造成的假象。再次经过任务高峰期比对历史性能差异,排除该进程抢占磁盘资源造成的性能问题,进而忽略IO层面问题
二、数据库层面问题排查
1.确认等待时间和阻塞原因
查看数据库中过去一天内排在前10位的等待事件及其总等待时间
SELECT *
FROM (SELECT EVENT,
TOTAL_WAIT_TM,
ROUND(TOTAL_WAIT_TM / SUM(TOTAL_WAIT_TM) OVER(ORDER BY 1), 4) * 100 || '%' ZB
FROM (SELECT NVL(EVENT, 'ON CPU') EVENT, COUNT(*) TOTAL_WAIT_TM
FROM V$ACTIVE_SESSION_HISTORY
WHERE SAMPLE_TIME > TRUNC(SYSDATE) -- 15 / (24 * 60)
GROUP BY EVENT
ORDER BY 2 DESC))
WHERE ROWNUM <= 10;
image.png
发现有大量的log file switch (checkpoint incomplete)等待事件
网上百度的解释:
服务器进程正要写入重做记录的时刻,若重做日志文件已满不能继续写入操作,则进程想LGWR请求执行对日志文件的切换。服务器进程由于LGWR,直到日志文件切换结束为止,需等待log file switch completion事件。但日志文件的切换结束时,如果将要投入使用的重做日志文件,还有没有完成的工作,就需要另外等待如下事件。
(1)如果对欲重新使用的重做日志文件尚未结束检查点,进程就应该等待由DBWR来结束检查点。这时,进程将等待log file switch (checkpoint incomplete)事件。
(2)如果对欲重新使用的重做日志文件尚未完成对党工作,进程就应该等待ARCH进程来结束归档工作。这时,进程将等待log file switch (archiving needed)事件。这个事件只在归档模式数据库上发生。
(3)如果对于欲重新使用的重做日志文件尚未完成对private strand的flush工作,就应该等待这个工作结束。这时,进程将等待log file switch (private strand flush incomplete)事件。
以上的三种等待现象在重做日志文件被循环使用的情况下,将生成许多重做数据,所以在尚未完成工作就重新使用时发生。因此这些等待现象一向是与log file switch completion等待现象一起出现的。准确的说,服务器进程首先等待log file switch completion事件,特殊情况下还会等待log file switch (checkpoint incomplete)、log file switch (archiving needed)和log file switch (private strand flush incomplete)事件。
因为名字相似,所以给管理人员带来了相当混乱的几个等待现象,发生原因和解决方式相同。发生原因是比起事务所创建的重做数据,重做日志文件过小。所以解决方法是将重做日志文件的大小调整为足够大。而且,使用Direct load operation或nologging选项对减少重做数据的量也是有帮助的。
解决办法(3选1):
1.添加日志组
2.按照实际情况增大日志组成员的大小。
3.或者同时做1,2
此处选择方法2:
--1.查看当前日志组成员
SELECT MEMBER FROM V$LOGFILE;
--2.查看当前日志组状态
SELECT GROUP#,MEMBERS,BYTES/1024/1024,STATUS FROM V$LOG;
--3.增加日志组
ALTER DATABASE ADD LOGFILE GROUP 4 ('D:\APP\CLPC_EDW\CLPC_EDW\REDO04.LOG') SIZE 200M;
ALTER DATABASE ADD LOGFILE GROUP 5 ('D:\APP\CLPC_EDW\CLPC_EDW\REDO05.LOG') SIZE 200M;
ALTER DATABASE ADD LOGFILE GROUP 6 ('D:\APP\CLPC_EDW\CLPC_EDW\REDO06.LOG') SIZE 200M;
--4.切换到新增的日志组上
ALTER SYSTEM SWITCH LOGFILE;--(可多次执行,直到CURRENT指向新建的日志组)
--5.查看当前日志组状态
SELECT GROUP#,MEMBERS,BYTES/1024/1024,STATUS FROM V$LOG;
SELECT * FROM V$LOG;
各种状态含义:
A.CURRENT指当前的日志文件,在进行实例恢复时是必须的;
B.ACTIVE是指活动的非当前日志,在进行实例恢复时会被用到。ACTIVE状态意味着,CHECKPOINT尚未完成,因此该日志文件不能被覆盖。这时也不能DROP掉,应该执行ALTER SYSTEM CHECKPOINT; --强制执行检查点;然后在操作。
C.INACTIVE是非活动日志,在实例恢复时不再需要,但在介质恢复时可能需要。
D.UNUSED表示该日志从未被写入,可能是刚添加的,或RESETLOGS后被重置。
--6.删除旧的日志组
ALTER DATABASE DROP LOGFILE GROUP 1;
ALTER DATABASE DROP LOGFILE GROUP 2;
ALTER DATABASE DROP LOGFILE GROUP 3;
--6.1如果删不掉
ALTER SYSTEM SWITCH LOGFILE;
SELECT GROUP#,MEMBERS,BYTES/1024/1024,STATUS FROM V$LOG;
ALTER DATABASE CLEAR UNARCHIVED LOGFILE GROUP 1;
ALTER DATABASE DROP LOGFILE GROUP 1;
--7.在操作系统下删除掉REDOLOG 日志文件
备注:可以先新建日志组作为过渡日志组,然后删除原有日志组并修改大小后进行重建。
再次查看数据库中过去15分钟内排在前5位的等待事件及其总等待时间,恢复正常:
image.png
到此,数据库性能突然变慢的症结已解决,后面的均为数据库业务功能层面的优化,时间紧迫可以忽略
2.查看过去15分钟内使用CPU及资源最多的前5位会话
SELECT *
FROM (SELECT S.USERNAME, S.MODULE, S.SID, S.SERIAL#, S.EVENT, COUNT(*)
FROM V$ACTIVE_SESSION_HISTORY H, V$SESSION S
WHERE H.SESSION_ID = S.SID
AND H.SESSION_SERIAL# = S.SERIAL#
AND SESSION_STATE = 'ON CPU'
AND SAMPLE_TIME > SYSDATE - INTERVAL '15' MINUTE
GROUP BY S.USERNAME, S.MODULE, S.SID, S.SERIAL#, S.EVENT
ORDER BY COUNT(*) DESC)
WHERE ROWNUM <= 5;
SELECT DISTINCT EVENT FROM V$SESSION
SESSION_STATE列有两个有效的值:ON CPU和WAITING,分别表示会话是活动的还是在等待资源,同样将 SESSION_STATE指定为WAITING值,可以查看在等待资源的前5位会话信息。
3、查看那些业务用户占用最多的资源
SELECT *
FROM (SELECT U.USERNAME,
H.MODULE,
H.SESSION_ID SID,
H.SESSION_SERIAL# SERIAL#,
COUNT(*)
FROM DBA_HIST_ACTIVE_SESS_HISTORY H, DBA_USERS U
WHERE H.USER_ID = U.USER_ID
AND SESSION_STATE = 'ON CPU'
AND (SAMPLE_TIME BETWEEN
TO_DATE('2018-07-22:00:00:00', 'yyyy-mm-dd:hh24:mi:ss') AND
TO_DATE('2021-07-22:23:59:59', 'yyyy-mm-dd:hh24:mi:ss'))
AND U.USERNAME != 'SYS'
GROUP BY U.USERNAME, H.MODULE, H.SESSION_ID, H.SESSION_SERIAL#
ORDER BY COUNT(*) DESC)
WHERE ROWNUM <= 5;
113 19422 561
141 54513 529
110 20841 515
38 11920 511
12 45449 490
4、定位造成大量资源消耗的对象信息
SELECT *
FROM (SELECT O.OBJECT_NAME,
O.OBJECT_TYPE,
S.EVENT,
S.TIME_WAITED,
S.CURRENT_OBJ#
FROM DBA_HIST_ACTIVE_SESS_HISTORY S, DBA_OBJECTS O
WHERE S.SAMPLE_TIME BETWEEN
TO_DATE('2020-12-26:00:00:00', 'yyyy-mm-dd:hh24:mi:ss') AND
TO_DATE('2021-07-22:23:59:59', 'yyyy-mm-dd:hh24:mi:ss')
AND S.CURRENT_OBJ# = O.OBJECT_ID
ORDER BY S.TIME_WAITED DESC);
select * from DBA_HIST_ACTIVE_SESS_HISTORY s where S.CURRENT_OBJ#='1042886'
5、查看过去15分钟内造成最多数据库等待的sql语句
SELECT ASH.USER_ID,
U.USERNAME,
S.SQL_TEXT,
SUM(ASH.WAIT_TIME + ASH.TIME_WAITED) TTL_WAIT_TIME
FROM V$ACTIVE_SESSION_HISTORY ASH, V$SQLAREA S, DBA_USERS U
WHERE ASH.SAMPLE_TIME BETWEEN SYSDATE - 15 / (24 * 60) AND SYSDATE
AND ASH.SQL_ID = S.SQL_ID
AND ASH.USER_ID = U.USER_ID
GROUP BY ASH.USER_ID, S.SQL_TEXT, U.USERNAME
ORDER BY TTL_WAIT_TIME
6、找出谁持有阻塞锁
SELECT L1.BLOCK,
S1.USERNAME || '@' || S1.MACHINE || ' ( SID=' || S1.SID ||
' ) is blocking ' || S2.USERNAME || '@' || S2.MACHINE || ' ( SID=' ||
S2.SID || ' ) ' AS BLOCKING_STATUS
FROM V$LOCK L1, V$SESSION S1, V$LOCK L2, V$SESSION S2
WHERE S1.SID = L1.SID
AND S2.SID = L2.SID
AND L1.BLOCK = 1
AND L2.REQUEST > 0
AND L1.ID1 = L2.ID1
AND L2.ID2 = L2.ID2;
7、定位某些有问题的表
1.首先对某些你觉得有问题的表进行分析:
ANALYZE TABLE T_RPT_TRUST_GATHER_WEEKLY COMPUTE STATISTICS;
image.png
SELECT table_name,blocks, empty_blocks, num_rows
FROM user_tables order by empty_blocks desc
说明:
BLOCKS 列代表该表中曾经使用过得数据库块的数目,即水线。
EMPTY_BLOCKS 代表分配给该表,但是在水线以上的数据库块,即从来没有使用的数据块。
三、问题总结
1.log file switch (checkpoint incomplete)等待事件:数据库性能突然变慢的症结为REDO日志文件空间不足导致等待
2.数据库残留问题包含:数据读直接跳过SGA,从硬盘读数据快到PGA,由于服务器处理PGA中数据块的速度远大于从磁盘读数据块到PGA的速度,通常解决这个问题的方法都是增大PGA的大小。由于系统资源有限,将pga_aggregate_target增大到1G,direct path read temp等待时间还是很明显
3.direct path read等待事件:很多表处于高水位,导致表的数据量很大,远大于_small_table_threshold(0.02*2G),所以就走了directpath read