DPA在电信计费系统故障诊断案例

3月20日中午,电信客户的计费系统故障,所有操作响应缓慢,应用无法正常运行。现场DBA已经迅速将应用连接转移到RAC集群的另一节点,恢复应用服务,我们使用DPA来回看事故现场,此时离故障发生,已经一小时了…… 回溯负载情况,如下图   问题最严重时,实例堵塞了近700个连接操作…针对此时间段,进一步点击、下钻分析当时系统发生了什么 系统先是每分钟4~5次堵塞(像是咳嗽),到了13:40后,就处于长时间“梗”住的状态,对于外部业务来说,数据库实例已不可用了。   利用DPA,继续下钻,查看堵塞原因: 1654#会话导致80个会话被堵塞,这80个会话的等待事件是 sga:allocation forcing component growth,这80个会话中的一些会话又堵塞了一些其他会话,如下图 266号会话堵塞了3个会话,这三个会话的等待事件都是cursor:pin s wait on x,这个等待事件一般都是跟sql parse相关,所以,现场工程师开始查看oracle 自己的ADDM报告,说是解析有问题,建议应用分析解决问题…被误导了   至此,DPA下钻后清晰显示,堵塞的根源是1654号会话,DPA显示是MMAN进程,它堵塞了80个会话,这些会话又堵塞了更多其他会话,是一个3级的堵塞关系,幸好DPA以“树型”显示方式,把这种多层级堵塞关系显示的非常清晰。 结论:Oracle MMAN进程进行过于频繁的SGA内存组件(本例中是share pool)resize操作,导致了问题…   这张图中显示的share pool内部情况,看官能看出异常么?   “划一条线1美元,知道在哪里划线9999美元”。 至此,后续我们如何解决这个问题的就不再讨论了,解决问题从来不是难事,难的是发现问题。 后记: 从DPA的负载雷达上,我们看到在系统出现问题之前的一小段时间内,系统负载不正常的降的很低,其后负载飙升上去…… 那段时间负载异常降低,说明了什么? 应用程序发生了什么…… 这些从Database Performance Analyzing层面,就无从知晓了。

利用DPA诊断Oracle锁等待问题

10:50分左右,REMS2系统发出系统性能告警,1套RAC的Active Session数量超过了200……收到告警之后,甲方DBA专家进入战位,查找原因。可是——现在的Active Session数量已经恢复了,往事不可追啊……REMS2 DPA出场~查看故障时间段,Active Session变化情况: 大概从10:48左右,Active Session持续增高,到10:53之后,由大概200左右,迅速恢复正常。我们使用DPA的BST History功能模块,查看问题最严重时,系统的Active Session都在干什么…… 可以发现,多个会话,在执行SQL ID为appfxqvj6scuv的SQL,因为无法获取enq:TM而被堵塞,这些会话本身又堵塞了其他的会话。Oracle中,一个DML操作,除了会以独占模式持有行级别的TX锁外,还需要持有表级别TM锁的共享模式,以防止在DML过程中,表的结构定义被修改(关于Oracle Enqueue、Lock机制此处不做过多描述)。现在这些会话无法获取到共享模式的TM锁,那说明什么? 说明TM锁被别的会话以独占模式持有,还没有释放……到这里,已经很清晰了,有人在业务高峰时期做了DDL操作,那么是改了表结构、还是建了索引呢?使用DPA的Top SQL模块,检查一下该时间段的SQL操作,如果有DDL操作,应为肯定耗时较长,会被记录下来…… 再次轻松几分钟诊断故障原因! 哦耶

Oracle活动会话异常增高诊断-续

……之前我们利用DPA-Lite,已经定位到LGWR进程在特定时刻,堵塞了几十、上百个会话,导致Active Session数量在短时间内出现高峰……之后是各种分析、诊断…… 到底是IO性能出现问题,还是Commit量太大,导致LGWR出现延迟,却是一时间无法分辨。甚至,由于REDO LOG是放在高端存储的SSD上的,SSD有无问题…… 每次“故障”也不过就10秒,想精确定位问题何其困难!我们还是通过DPA-Lite来仔细检查,看看能不能找出一些端倪……5月10日,16时左右,问题再现: 此时间段内,Active Session高峰多次出现;16:06:56秒,LGWR堵塞了68个会话; 我们仔细看一看都堵塞了哪些会话,有何特点…… 特点是: HTZT用户,通过JDBC连接的应用,在等待Commit; 当前操作的对象ID都是 131629872和131629864 通过DPA-Lite查询另外一个时间点: 再次确认了这个现象!我们再看看更早之前发生Active Session高峰时的现象(5月8日16时): 仍然是HTZT用户操作OBJECT ID 131629872 131629864时等待Commit……我们查询数据库,确认这两个对象,发现是一张LOG(日志)表和该表上的一个索引,这个表以BLOB方式保存日志,目前数据量极大——超过300G。使用DPA-Lite 查看对应时间段的Top Object: 问题时间点,对于该表索引的操作,在数据库中占用资源排名第一! 存储已经用的最高端的了,解决问题就从这个日志表入手吧……日志么,都是用来事后查询、分析使用的,我觉得存在文件系统里,利用Hadoop之类分析是最好不过的,核心数据库高端存储上存个几百GB的日志,还弄出这种问题来,赶紧迁走……一想到拿着这些证据,一会儿让应用开发那帮哥们哑口无言我就觉得兴奋!

REMS在性能诊断、分析场景下的实用

下午4点半,rems2发出cpu负载高的警报——使用率超过50%。不是演习、不是测试,真正的核心系统:两台aix上的一套Oracle 10g RAC。 很明显16:30之后,cpu负载上升,一直到17:00,使用率在50%处波动~ 同时网络流量,内存,交换分区都很平稳: 检查数据库对应时段的指标,逻辑IO明显的高阶运行,激活会话数升到35左右,物理IO与用户调用无对应跳阶: 接下来,很自然的就是Top Session显示,Top SQL分析了~ 很遗憾,咱是生产核心系统,这些都不允许贴出来了😄  好的监控、性能分析工具,随着你的心意、方向,有如低扭强大的机械马达——让你御风而行

Oracle Case insensitive index searches

有几种方法来编写’case insensitive’的SQL语句——SQL需要避免全表扫描。如果是Oracle10g R2之前版本,我们需要:1. 在查询中转换数据,使其大小写不敏感(case insensitive),FBI(Function Base Index): create index upper_full_name on customer ( upper(full_name)); select full_name from customer where upper(full_name) = ‘SAM HENDRY’;2. 在存储数据(insert或者update)时,使用触发器完成case insensitive转换(比如使用to_lower,to_upper 内建函数)。3. 使用 alter session命令 alter session set NLS_COMP=ANSI; alter session set NLS_SORT=GENERIC_BASELETTER; select * from customer where full_name = ‘Sam Hendry' 在Oracle 10g R2版本中,引入了使用BINARY_CI BINARY_AI排序来进行case insensitive查询的功能。 初始化参数设置: NLS_SORT=binary_ci NLS_COMP=ansi 创建索引:

Fingerprints of Latch——使用V$LATCH_MISSES调优Latch Wait

Latch等待应该说并不是一件坏事情。毕竟,设计Latch的目的就是用来让你等待的。Oracle使用Latch来保护SGA区中的关键数据结构不会在一个进程正在访问、修改时被另外一个进程访问,避免SGA区关键数据结构出现Corruption。暂时的waiting要比内存corruption更让人接受,不是么?不过,如果经常性的出现两个或多个进程等待同一个Latch,这就是一个问题了。因为,当Latch被释放(free)时,正在等待此Latch的多个进程将争抢Latch的所有权,而且只能有一个进程如愿获得Latch的所有权;而其他参与争抢的进程将消耗无效的CPU时间去spin、wait。随着参与争抢Latch的进程数量增加,将会造成严重的性能问题;而如果这个问题能够得到解决,这个性能问题会迅速的消失。一般来讲,共有三种不同的latch争用问题:1. 高CPU占用可能会触发latch争用问题。试想,如果一个已经获得latch所有权的进程不能迅速的获得CPU使用时间片,对这个latch的大量等待当然就不可避免了。为了避免由于CPU饥饿(CPU starvation)造成的latch争用问题,你应该保证你的系统不会持续较长时间的出现CPU占用率超过85%的压力高峰(当然,在某些系统中高CPU使用率是安全的)。很多系统问题(check for run-away process、paging)都会造成CPU的过度使用,所以说,在诊断一个Oracle性能问题的时候,你应该首先确保他确实是Oracle问题——而不是OS操作系统问题。2. Oracle预期latch被间断的、简短的占用。一些Latch争用性能问题的出现恰恰是因为一个Latch被超出预期的长时间占用。这种类型的问题通常是由于SGA区中的某个访问受latch保护的链表增长的太长(比如Shared Pool的碎片太多、buffer cache中的hash chains太长等等)。 这种类型的latch争用问题对_spin_count初始化参数的设置非常敏感。如果spin循环的时间恰恰短于这种类型的latch能够被获取的时间,这时尝试获取latch的进程就会停止spin,转入sleep状态;这种情况下,如果适当增加_spin_cout参数,使得spin循环的时间能够赶上占用Latch进程释放latch的时间,就可以避免进程sleep。当然,最好的调节方法是减少latch占用的时间,而不是增加latch spin的时间。3. Latch争用的性能问题也可能并不是由于Latch hold时间太长,而仅仅是因为获取Latch占用权的请求太过频繁了。最典型的一个例子是因为commit太过频繁而造成的’redo allocation’latch的争用。 这种情况下,最好的解决办法当然是避免太过频繁的latch request;但是很多情况下,最有效的解决方案也是最不可能的方案。通常,我们可以通过增加相应的child latch数量来缓解这个问题(比如 cache buffer lru latch),增加_spin_count对这种类型的性能问题没有帮助。如果我们不能降低latch request的频率(比如修改应用commit的频率),而且这个latch也不属于默认的long latch类别(默认好像只有两个latch是long latch: shared pool 、library cache);因为long latch默认支持latch posting特性,我们可以通过设置初始化参数_latch_wait_posting为2来强制对所有的latch类型都支持latch wait posting。 ——对于这一点,我心存疑虑,latch wait posting毫无疑问对某些latch会有负面影响,或者会过多的消耗资源;不然Oracle为什么只默认支持shared pool、libarary cache的wait posting呢;所以除非十分必要且经过测试,这种手段还是谨慎使用了。很显然的,要想恰当的解决latch争用造成的性能问题,首先我们得判断它是属于哪种类型的latch争用。以上3中类型中,CPU starvation应该是最容易判断的;其他两种类型的latch可以通过v$latch_misses加以区分。这个视图中包含了request或者hold每一个latch对应的Oracle Kernel位置。SQL> desc v$latch_misses Name Null? Type ----------------------------------------- -------- ---------------------------- PARENT_NAME VARCHAR2(50) WHERE VARCHAR2(64) NWFAIL_COUNT NUMBER

一篇ORA-4031处理服务文档

背景: **保险核心业务系统数据库使用Oracle 10.2.0.5 版本 RAC(Real Application Cluster)集群,集群构建在IBM Aix系统上,包含两个节点:【node1,node2】。 在较长一段时间内,node2会每间隔20天左右,出现ORA-04031错误,导致该节点数据库系统不再可用,需重启该节点上Oracle 实例方能解决问题。 ORA-04031错误解释: 04031, 00000, "unable to allocate %s bytes of shared memory (\"%s\",\"%s\",\"%s\",\"%s\")"// *Cause: More shared memory is needed than was allocated in the shared// pool or Streams pool.// *Action: If the shared pool is out of memory, either use the// DBMS_SHARED_POOL package to pin large

Oracle数据库细粒度审计(Fine-Grained-Audit)应用

Oracle数据库细粒度审计(Fine-Grained-Audit)应用 设计目标: 在实际的业务场景中,经常有审计“某人、某时间段、查看了某条敏感数据”这样的需求,Oracle本身提供了DB Vault、Audit Vault组件。 本文讨论使用细粒度审计,实现医院“防统方”功能: 背景: 医生开药后会从对应厂商医药代表处收取回扣,而收取回扣,需要统计处方中的药品、数量、医生信息。现阶段“纪委”部门严禁“统方”——对处方用药进行统计——操作。 期望实现对“可疑操作”的审计记录功能,后续对“可疑”记录进行进一步分析,进而准确确认是否为“统方”操作。 所以,技术实现上分两个阶段: 可疑操作记录 可疑操作分析架构设计应具备可扩展性: 规则可定义 能够适用其他“敏感数据”审计需求 阶段一: 可疑操作记录 概述: 通过Oracle FGA,记录可疑操作。 FGA技术背景: DBMS_FGA.ADD_POLICY( object_schema VARCHAR2, object_name VARCHAR2, policy_name VARCHAR2, audit_condition VARCHAR2, audit_column VARCHAR2, handler_schema VARCHAR2, handler_module VARCHAR2, enable BOOLEAN, statement_types VARCHAR2, audit_trail BINARY_INTEGER IN DEFAULT, audit_column_opts BINARY_INTEGER IN DEFAULT); Oracle FGA核心的操作就是这个存储过程的调用: 我们需要指定object_schema, object_name来限定是对哪个表(对象)来做审计;使用audit_condition(SQL 逻辑表达式,但是有一定限制,如不能使用子查询,不能使用rownum伪列等)来限制“行”级别的敏感数据——只有符合此条件的,才触发审计操作。使用audit_column来指定“敏感”列,只有涉及敏感列的操作才被审计,结合audit_column_opts,灵活定义敏感列(其实就是Any还是All的关系)audit_trail来指定审计结果存放的“位置”,也决定审计记录的数据内容(特别是是否记录SQL,以及绑定变量),这里最需要特别指出的是,这里的设置不依赖于数据库级别的audit_trail 初始化参数。handler_schema 和 handler_module指定“敏感”操作发生时,调用的存储过程,用来自定义一些处理操作。

SQL Script: DB Usage History

Declare         v_BaselineSize        number(20);        v_CurrentSize        number(20);        v_TotalGrowth        number(20);        v_Space                number(20);        cursor usageHist is                 select a.snap_id,                        SNAP_TIME,                        sum(TOTAL_SPACE_ALLOCATED_DELTA) over ( order by a.SNAP_ID) ProgSum                 from                         (select SNAP_ID,                                sum(SPACE_ALLOCATED_DELTA) TOTAL_SPACE_ALLOCATED_DELTA                         from DBA_HIST_SEG_STAT                         group by SNAP_ID                         having sum(SPACE_ALLOCATED_TOTAL) <> 0                         order by 1 ) a,                        (select distinct SNAP_ID,                                to_char(END_INTERVAL_TIME,'DD-Mon-YYYY HH24:Mi') SNAP_TIME                         from DBA_HIST_SNAPSHOT) b                 where a.snap_id=b.snap_id;Begin         select sum(SPACE_ALLOCATED_DELTA) into v_TotalGrowth from DBA_HIST_SEG_STAT;        select sum(bytes) into v_CurrentSize from dba_segments;        v_BaselineSize := v_CurrentSize - v_TotalGrowth ;         dbms_output.put_line('SNAP_TIME           Database Size(MB)');         for row in usageHist loop                 v_Space := (v_BaselineSize + row.ProgSum)/(1024*1024);                dbms_output.put_line(row.SNAP_TIME || '           ' || to_char(v_Space) );        end loop;end;