merge语句导致的CPU使用率过高的优化

今天有一个数据库有点反常,早上的时候报出了CPU使用率的警告。
警告内容如下:

ZABBIX-监控系统:
------------------------------------
报警内容: CPU utilization is too high
------------------------------------
报警级别: PROBLEM
------------------------------------
监控项目: CPU idle time:44.61 %
------------------------------------
报警时间:2015.10.30-06:51:00

但是问题持续的时间较短,过了会就自动恢复了,早上忙着排查其它的问题,就没有特别注意。结果到了下午的时候,开始频繁报出警告。
首先查看了CPU的使用情况

查看了DB time的情况,发现在早晨和下午的时候都开始有大的波动。
至于初步原因,自己查看分析图发现在这段时间内产生了大量的日志切换。

所以这个问题查看了sar的结果得到了基本的确认。
07:40:01 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
03:40:01 AM     all     40.46      0.00     42.52      0.09      0.00     16.92
03:50:01 AM     all     42.85      0.00     46.63      0.06      0.00     10.45
04:00:01 AM     all     44.83      0.03     50.16      0.05      0.00      4.92
04:10:01 AM     all     38.23      0.00     50.79      0.11      0.00     10.87
04:20:02 AM     all     45.22      0.00     52.64      0.04      0.00      2.10
04:30:01 AM     all     39.96      0.00     51.98      0.21      0.00      7.85
04:40:01 AM     all     40.59      0.00     52.29      0.12      0.00      6.99
04:50:01 AM     all     36.73      0.00     48.04      0.17      0.00     15.06
查看了awr中的sql占用DB time的情况,发现有一条语句的占用情况实在是太高了。从执行情况来看似乎也不是很慢,但是DB time就是很高。
   SNAP_ID SQL_ID     EXECUTIONS_DELTA ELAPSED_TI PER_TOTAL
---------- ------------------------------- ---------- ----------
     12952 04twf1qr6hbxb   77421       12231s     94%
     12952 14qykh7dc3gts   52014       4s         0%
     12952 4v43gna7jcpwd   51975       6s         0%
     12952 9fz0usaqnsubf       2       0s         0%
     12952 c831ks00zc8c2   16676       2s         0%
对于这个问题查看了下执行计划,发现
Execution Plan                                                                        
--------------------------------------------------------------------------------------
| Id  | Operation              | Name                   | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT        |                        |       |       |     3 (100)|
|   1 |  MERGE                 |   UC_OPENPLATFORM_USER |       |       |            |
|   2 |   VIEW                 |                        |       |       |            |
|   3 |    NESTED LOOPS OUTER  |                        |     6 |  5634 |     3   (0)|
|   4 |     VIEW               |                        |     1 |    13 |     0   (0)|
|   5 |      SORT AGGREGATE    |                        |     1 |   115 |            |
|   6 |       INDEX UNIQUE SCAN| OPNE_USER_ID_IDX       |     1 |   115 |     0   (0)|
|   7 |     VIEW               |                        |     6 |  5556 |     3   (0)|
|   8 |      FILTER            |                        |       |       |            |
|   9 |       TABLE ACCESS FULL|   UC_OPENPLATFORM_USER |     6 |  5556 |     3   (0)|
--------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
发现一个特别之处在于使用了动态采样,意味着相关的表没有收集统计信息。从动态采样的结果来看,资源消耗似乎不高。
我们通过awr sql report来抓取一个执行时的执行计划的报告。
---------------------------------------------------------------------------------------
| Id  | Operation              | Name                   | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT        |                        |   802K|   129M|  1891   (1)|
|   1 |  MERGE                 |   UC_OPENPLATFORM_USER |       |       |            |
|   2 |   VIEW                 |                        |       |       |            |
|   3 |    NESTED LOOPS OUTER  |                        |   802K|   718M|  1891   (1)|
|   4 |     VIEW               |                        |     1 |    13 |     2   (0)|
|   5 |      SORT AGGREGATE    |                        |     1 |    20 |            |
|*  6 |       INDEX UNIQUE SCAN| OPNE_USER_ID_IDX       |     1 |    20 |     2   (0)|
|   7 |     VIEW               |                        |   802K|   708M|  1889   (1)|
|*  8 |      FILTER            |                        |       |       |            |
|   9 |       TABLE ACCESS FULL|   UC_OPENPLATFORM_USER |   802K|    41M|  1889   (1)|
---------------------------------------------------------------------------------------
发现差距还是要大的多,而且瓶颈还是在于全表扫描。
这个时候查看等待事件,发现还有buffer busy waits,查看了sga的设置情况,竟然还真是有问题。
SQL> show parameter sga

NAME          TYPE         VALUE
------------- ------------ --------
lock_sga      boolean      FALSE
pre_page_sga  boolean      FALSE
sga_max_size  big integer  24G
sga_target    big integer  12032M
这个sga占用了近24G,但是实际使用了才一半,所以使用不够充分,所以简单评估之后调整到了20G,以备不时之需。
SQL> alter system set sga_target=20G;
System altered.
但是调整之后,除去系统级的影响,发现性能提升并不是很大。那么注意力还是到了这条sql语句上。而且通过addm,sql tuning advisor也给不出建议来,看来还得自己分析分析了。
来仔细回顾一下这条语句,为什么走了全表扫描。结合执行计划,其实谓词信息就很明显了。
   6 - access("USER_ID"=:1 AND "PLATFORM"=:2)
   8 - filter("TW"."CNT">0)
主要问题就在于filter的部分。
那么来回顾一下这条语句。为什么需要设定CNT>0这么个条件。
MERGE INTO UC_OPENPLATFORM_USER t
                   USING (SELECT count(*) CNT from UC_OPENPLATFORM_USER where
USER_ID=:1 and PLATFORM=:2) tw
                   ON (tw.CNT>0)
                   WHEN MATCHED THEN UPDATE SET t.NAME=:3, t.UPDATE_DATE=SYSDATE
where USER_ID=:4 and PLATFORM=:5
                   WHEN NOT MATCHED THEN INSERT(USER_ID, PLATFORM, NAME,
CREATE_DATE, UPDATE_DATE) VALUES(:6, :7, :8, SYSDATE, SYSDATE)
自己琢磨了下原因,子表tw输出的是表中数据的返回结果,因为user_id是唯一性约束,所以如果匹配则值返回一条结果,如果不匹配则则返回0
根据下面的match条件进行update或者insert操作。整个语句的意思其实就是一句话,根据user_id来匹配,如果找到相关的记录就修改,如果没有就增加一条。
那么为什么需要加CNT呢,我觉得开发同学还是考虑到如果没有匹配的值,那么如果tw根据user_id来和t关联,就没法匹配了。  因为tw就是空值,肯定执行不了insert的部分。
我们来做个简单的例子,可能就会更容易理解。
首先创建一个表test含有id和name字段。插入一条记录id=100
CREATE TABLE TEST(ID NUMBER,NAME VARCHAR(100));
INSERT INTO TEST VALUES(100,'TEST');
如果使用id=100有匹配的数据,那么可能会执行update
MERGE INTO TEST t
  USING (SELECT ID from TEST where ID=100 ) tw
  ON (tw.ID=T.ID)
  WHEN MATCHED THEN UPDATE SET t.NAME='AAA_MATCHED'
  where ID= 100
  WHEN NOT MATCHED THEN INSERT(ID, NAME) VALUES(100,'BBB_NOT_MATCHED') ;
1 row merged.
SQL> select *from test;
        ID NAME
---------- --------------------
       100 AAA_MATCHED
那么如果不匹配呢,我们给一个id为1000,肯定匹配不到。     
MERGE INTO TEST t
  USING (SELECT ID from TEST where ID=1000 ) tw
  ON (tw.ID=T.ID)
  WHEN MATCHED THEN UPDATE SET t.NAME='AAA_MATCHED'
  where ID= 1000
  WHEN NOT MATCHED THEN INSERT(ID, NAME) VALUES(100,'BBB_NOT_MATCHED') ; 
0 rows merged.
这个时候问题来了,压根不会insert
所以根据这个情况,开发可能会有这种顾虑所以才会考虑通过CNT>0来得到这种匹配,可能他们也觉得没有办法。
其实可以动个小脑筋,我们直接使用一个union all来给出一个dummy的数据,保证是不会匹配的id
比如id根据业务是需要为证书或者字符,那么给一个负数肯定是不糊匹配的。
MERGE INTO TEST t
  USING (SELECT ID from TEST where ID=1000 union all select -1 id from dual ) tw
  ON (tw.ID=T.ID)
  WHEN MATCHED THEN UPDATE SET t.NAME='AAA_MATCHED'
  where ID= 1000
  WHEN NOT MATCHED THEN INSERT(ID, NAME) VALUES(100,'BBB_NOT_MATCHED') ;  
1 rows merged.
这个时候就达到了预期的结果,而且是按照id来关联的,避免了全表扫描。
那么真实环境中的sql语句级可以简单调整一下
MERGE INTO UC_OPENPLATFORM_USER t
                   USING (SELECT USER_ID from UC_OPENPLATFORM_USER where
USER_ID=:1 and PLATFORM=:2  union all select '-999' from dual) tw
                   ON (tw.USER_ID=T.USER_ID)
                   WHEN MATCHED THEN UPDATE SET t.NAME=:3, t.UPDATE_DATE=SYSDATE
where USER_ID=:4 and PLATFORM=:5
                   WHEN NOT MATCHED THEN INSERT(USER_ID, PLATFORM, NAME,
CREATE_DATE, UPDATE_DATE) VALUES(:6, :7, :8, SYSDATE, SYSDATE)    
这样就可以满足需求了,而且全表扫描也没有了。
----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                   | Rows  | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT                |                        |     2 |   338 |     8   (0)|
|   1 |  MERGE                         |   UC_OPENPLATFORM_USER |       |       |            |
|   2 |   VIEW                         |                        |       |       |            |
|   3 |    NESTED LOOPS OUTER          |                        |     2 |   312 |     8   (0)|
|   4 |     VIEW                       |                        |     2 |   204 |     4   (0)|
|   5 |      UNION-ALL                 |                        |       |       |            |
|*  6 |       INDEX UNIQUE SCAN        | OPNE_USER_ID_IDX       |     1 |    20 |     2   (0)|
|   7 |       FAST DUAL                |                        |     1 |       |     2   (0)|
|   8 |     TABLE ACCESS BY INDEX ROWID|   UC_OPENPLATFORM_USER |     1 |    54 |     2   (0)|
|*  9 |      INDEX UNIQUE SCAN         | OPENPLATFORM_USER      |     1 |       |     1   (0)|
----------------------------------------------------------------------------------------------
这样就达到了初步效果,所以下一步就是建议开发同学来这么修改了。因为还不能动态修改,所以这个问题就是万事俱备只欠东风了。
通过这个案例可以发现,其实很多问题还是需要去分析原因,为什么要写成那样,是出于什么样的考虑,可能出发点是好的,但是结果把问题从一个极端逮到了另外一个极端。

时间: 2024-08-01 18:59:13

merge语句导致的CPU使用率过高的优化的相关文章

merge语句导致的CPU使用率过高的优化(二)

之前分享过一篇关于merge语句导致的CPU使用率过高优化的案例.的http://blog.itpub.net/23718752/viewspace-1819471/ 后续的跟进没有补充,也"秀"一张图,红色的火焰是原来的系统负载,右边的部分是最近的逻辑读情况,不过惭愧的是,这个不是优化的效果,因为应用的高峰期已经处理完了,后面的sql调用频率极低,所以感觉不到任何的压力.所以通过这个图也可以看出,给一张差别巨大的图也不一定是系统优化的效果,也可能是其它外在因素. 那么既然要说跟进,后

电脑CPU温度过高 cpu使用率较高怎么办

cpu使用率高是网民经常遇到的问题,CPU使用率高其实就是你运行的程序占用的CPU资源,说明你的机器在这个时间上运行了很多程序.长期使用会让CPU长时间处于高热状态会对影响cpu寿命产生点影响,CPU使用率过高怎么办呢? cpu使用率高的原因和解决办法: 一.电脑正在运行大型的应用程序,例如大型的处理软件.3D网络游戏等等1.退出当前大型程序,等待cpu使用率恢复正常. 2.查看电脑配置是否满足运行该程序的最低配置,如果确实是电脑配置不行的话,那么就建议网友将电脑硬件进行升级了. 3.如果是软件

电脑CPU使用率过高怎么办?

  cpu使用率高是网民经常遇到的问题,CPU使用率高其实就是你运行的程序占用的CPU资源,说明你的机器在这个时间上运行了很多程序.长期使用会让CPU长时间处于高热状态会对影响cpu寿命产生点影响,CPU使用率过高怎么办呢?首先我们来看看使cpu使用率高的原因,好对症下药. cpu使用率高的原因和解决办法: 一.电脑正在运行大型的应用程序,例如大型的处理软件.3D网络游戏等等 1.退出当前大型程序,等待cpu使用率恢复正常. 2.查看电脑配置是否满足运行该程序的最低配置,如果确实是电脑配置不行的

cpu使用率过高怎么解决

  cpu使用率过高怎么解决 CPU使用率过高是什么原因 1.电脑运行了大型的程序,例如大型的游戏,3D网络游戏等等,这种情况通常是正常的,这类程序需要较多的CPU资源,网友将大型的程序退出一段时间,CPU使用率基本上就恢复正常了; 2.电脑如果中毒,例如下载者类的病毒,这类病毒会在后台下载大量的恶意程序运行,试想一下,电脑突然运行了几十个程序,CPU使用率必然会突然飙升,用户会觉得很卡.这种情况就需要大家下载杀毒软件或者木马专杀工具进行扫描,清除完病毒.木马之后CPU使用率就会降低了; 3.C

如何解决cpu使用率过高的问题

  CPU使用率是什么? CPU使用率其实就是你运行的程序占用的CPU资源,表示你的机器在某个时间点的运行程序的情况.使用率越高,说明你的机器在这个时间上运行了很多程序,反之较少.使用率的高低与你的CPU强弱有直接关系. CPU使用率过高是什么原因? 可能原因1.电脑运行了大型的程序,例如大型的游戏,3D网络游戏等等,这种情况通常是正常的,这类程序需要较多的CPU资源,网友将大型的程序退出一段时间,CPU使用率基本上就恢复正常了; 可能原因2.电脑如果中毒,例如下载者类的病毒,这类病毒会在后台下

cpu使用率忽高忽低怎么办?

  首先我们打开任务管理器,然后切换到cpu行列,对占用cpu的出程序逐一排查,查看哪个cpu程序占用的cpu忽高忽低的情况.找到占用cpu不稳定的进程将其结束掉(不是系统进程的前提下),如果发现时系统进程不能结束的话,那就系统的问题了. 系统的问题包括驱动安装不正常,或者系统被病毒木马入侵的情况,还有一种情况就是杀毒软件对电脑的监控占用大量的cpu导致不稳定的情况. 解决方法:先用杀毒软件杀下毒,最好是重新安装一个正版系统,这样就不会出现系统导致占用cpu使用率不稳定的情况.然后在减少开机启动

Win7系统CPU使用率忽高忽低怎么办?

  步骤如下: 1.首先要明白在哪里查看CPU使用率,我们右键桌面的底部工具栏,然后选择启动任务管理器. 2.在弹出的Windows任务管理器选项卡中选择性能选项,在这里就可以查看电脑的CPU使用情况了. 3.然后我们看到电脑的CPU使用率忽高忽低或者偏高了,这种情况十有八九是开了某个占CPU的软件,但是通常我们也确定不了哪个软件占CPU,不是说软件体积越大就越占CPU,不是这样的. 4.我们点击性能项目右下角的资源监视器,如下图. 5.在资源监视器选项卡下我们可以看到很直观地观察到哪个软件最占

bash脚本-阿里云CPU使用率偏高,由于某个守护进程,求现象解释

问题描述 阿里云CPU使用率偏高,由于某个守护进程,求现象解释 昨天发现我阿里云的cpu莫名偏高,参见提问问了阿里云的技术支持,木有回答到点上,他们说是内存太高,但是我开了swap分区之后,也没有见到cpu使用率降低. 经过测试,发现,这个现象适合我写的一个进程守护脚本相关,谁能解释一下?脚本如下:#!/bin/bash while truedo process=ps -ef|grep /opt/tomcat7|grep -v grep if [ -z ""${process}&quo

merge语句导致的性能问题紧急优化

晚上正在休息的时候,突然收到一封报警邮件. 报警内容: CPU utilization is too high ------------------------------------ 报警级别: PROBLEM ------------------------------------ 监控项目: CPU idle time:59.11 % ------------------------------------ 这个报警信息已经非常明确,CPU使用率很紧张了.这台服务器上运行着Oracle和M