sql 一对多获得一条数据_分享测试环境中一条sql拖垮整个数据库的解决思路

概述

今天主要简单记录一下最近UAT环境中一条sql拖垮数据库的案例,仅供参考。


1、告警

一大早起来,就冒出这么个告警,看起来又要干活了~

446953a0526d764768170f4bc78b5b0e.png

2、检查服务器状态

可以发现缓存了13G,内存已经不够用了,这台服务器上同时部署了Oracle和mysql服务器,初步判断问题在Oracle数据库上。

07da0a0a5159cc95ac7ffcf46d6236c4.png
1b470b9064631bb2d9322baca1638409.png

3、尝试清缓存

无效

26f1bb649406ed6833edc86b49bae908.png

4、查看当前等待事件

这台数据库是没有部署DG的,暂时不知道为什么有DG相关进程

--V$sessiong_wait提供了任何情况下session在数据库中当前正在等待什么(如果session当前什么也没在做,则显示它最后的等待事件),注意这里排除了一些常见的IDLE等待事件SELECT  inst_id,wait_class,EVENT, SUM(DECODE(WAIT_TIME, 0, 0, 1)) "Prev", SUM(DECODE(WAIT_TIME, 0, 1, 0)) "Curr", COUNT(*) "Tot" , state, sum(SECONDS_IN_WAIT) SECONDS_IN_WAIT FROM GV$SESSION_WAITWHERE event NOTIN ('smon timer','pmon timer','rdbms ipc message','SQL*Net message from client','gcs remote message')    AND event NOT LIKE '%idle%'    AND event NOT LIKE '%Idle%'    AND event NOT LIKE '%Streams AQ%'GROUP BY inst_id,wait_class,EVENT,stateORDER BY 1,6 desc;说明:1)当state值为Waiting,Second_in_wait值才是实际的等待时间(单位:秒),当state值为Waiting known time,那么wait_time值就是实际等待时间。Prev代表上一次等待次数,Curr代表当前等待次数
1a19dc60992998c231f7ef1ebdebefbd.png
47a681108452bd51a47e7e8dbd1de6d1.png

5、查看当前会话基本信息

其实在这里就可以看到第二个会话是问题sql了

--可以获得会话基本信息、执行时间、执行sql、使用的临时表空间大小、undo大小和表空间等SELECT /* use_hash(sess,proc,undo,tmp) use_nl(s)*/distinct sess.inst_id,         sess.sid,         sess.serial#,         sess.username,         substr(osuser, 1, 10) osuser,         status,         sess.process,         proc.spid,         sess.machine,         sess.program,         regexp_substr(NUMTODSINTERVAL(nvl((SYSDATE - SQL_EXEC_START) * 24 * 60 * 60,                                           last_call_et),                                       'SECOND'),                       '+d{2} d{2}:d{2}:d{2}') running_sec,         TEMP_MB,         UNDO_MB,         s.sql_id,         TSPS.NAME TSPS,         decode(sess.action, null, '', sess.action || ', ') ||         replace(s.sql_text, chr(13), ' ') sql  FROM gv$session sess,       gv$process proc,       gv$sql s,       (select ses_addr as saddr, sum(used_ublk / 128) UNDO_MB          from v$transaction         group by ses_addr) undo,       (select session_addr as saddr,               SESSION_NUM serial#,               sum((blocks / 128)) TEMP_MB          from gv$sort_usage         group by session_addr, SESSION_NUM) tmp,       (select inst_id, sid, serial#, event, t.name          from gv$session ls, sys.file$ f, sys.ts$ t         where status = 'ACTIVE'           and ls.p1text in ('file number', 'file#')           and ls.p1 = f.file#           and f.ts# = t.ts#) tsps WHERE sess.inst_id = proc.inst_id(+)   and sess.saddr = tmp.saddr(+)   and sess.serial# = tmp.serial#(+)   AND sess.status = 'ACTIVE'   and sess.username is not null   and sess.sid = tsps.sid(+)   and sess.inst_id = tsps.inst_id(+)   and sess.serial# = tsps.serial#(+)   AND sess.paddr = proc.addr(+)   and sess.sql_id = s.sql_id(+)   and sess.saddr = undo.saddr(+) ORDER BY running_sec desc, 4, 1, 2, 3;
0b113eabc238bdb8ef550f3c9f7d83ce.png

6、观察awr报告

最快的办法就是看等待事件--》找对应sql

等待事件很明显是CPU占用过高,对应看sql order by CPU time实际上就有结果了。

76fc56bf4f40f5095ccbb94d4dce41da.png
1fe07aede48c3ca677a80aaf8a47e520.png

有兴趣的朋友可以算一下这条sql产生了多少的逻辑读..

429a7a2d842c26f6c8ef7c10a32e2c63.png

7、关于'acknowledge over PGA limit' Wait Event

细心的朋友从AWR应该可以观察到'acknowledge over PGA limit' Wait Event,查询mos文档,发现此类问题是由于PGA大小达到了PGA_AGGREGATE_LIMIT的值,防止ORA-4036错误,后面进程分配pga时需要等待其他进程释放pga。

77c21e18fc366995b9e0c24f1303915f.png

临时解决方案:

1. Set PGA_AGGREGATE_LIMIT=0 (as SYS user) to revert to 11g behavior of PGA memory management using PGA_AGGREGATE_TARGET parameter and the wait event will be alleviated. ALTER SYSTEM SET PGA_AGGREGATE_LIMIT=0 SID='*' SCOPE=BOTH;2. Set PGA_AGGREGATE_LIMIT to higher than the default value (as SYS user), which was calculated based on the PGA_AGGREGATE_TARGET, PROCESSES or "_pga_limit_target_perc" setting and that will also help to reduce this wait event.ALTER SYSTEM SET PGA_AGGREGATE_LIMIT= &new_value  SID='*' SCOPE=BOTH; oracle建议设置PGA_AGGREGATE_LIMIT=0或者增大这个参数的值解决ALTER SYSTEM SET PGA_AGGREGATE_LIMIT=0 SCOPE=BOTH;

上面这个方法只是暂时解决,究竟是什么导致的呢,需要继续检查pga使用情况

首先判断该应用pga设置是否合理,即连接数数量和pga大小,可以通过查询process数量和pga使用,如果明显出现process较小而pga使用很大,则有可能是某些应用进程或者数据库进程异常导致,其实就是下面sql导致了,也就是上面AWR看到的sql。

进一步探究可用以下sql:

--列出占用pga最大的进程select pid,       spid,       substr(username, 1, 20) "USER",       program,       PGA_USED_MEM,       PGA_ALLOC_MEM,       PGA_FREEABLE_MEM,       PGA_MAX_MEM  from v$process where pga_alloc_mem = (select max(pga_alloc_mem)                          from v$process                                          where program not like '%LGWR%');--检查应用进程分配的pga,查询出大于50m的select a.sid,a.status,a.sql_id,a.last_call_et,b.pga_alloc_mem/1024/1024,a.event,a.state from v$session a,v$process b where a.paddr=b.addr and a.status='INACTIVE' and b.pga_alloc_mem/1024/1024 > 50;--查看当前pga使用情况select * from v$pgastat

8、问题sql分析

两个800万的表全扫后做hash连接,连接是没问题的,问题在于两点:

1)为什么800万的表要全扫

2)为什么同样的表要查两次

ps:看不懂执行计划不要问我,www.baidu.com或者看之前文章..

SELECT id FROM xxl_job_log WHERE ID NOT IN (SELECT id FROM xxl_job_log WHERE (trigger_code in (0, 200) and handle_code = 0) OR (handle_code = 200) ) AND alarm_status = 0 ORDER BY id ASC
b12bc2cc6fa32bd2ec058c2d0541196f.png
a3216d4531d5a15964f04140892503c4.png

9、最终建议

1、改写sql,走索引

这个sql存在的问题:

1)NOT IN和OR导致这个表没走索引

2)引用了两次这个表,是不是可以考虑只查一次?例如with xxx

2、这个是日志表,可以删除不必要的数据,只保留1周数据(考虑定时任务解决)


后面会分享更多devops和DBA方面内容,感兴趣的朋友可以关注下!

这里引申一个问题,oracle要去找历史的一些问题还是有很多办法的,那么mysql呢?当你重启mysql数据库后怎么去定位历史的一些问题源头?

9bbc28931365479f0d3a6dd357242705.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值