Read the rest of this entry
performance
提起Oracle的Hint,几乎每一个DBA都知道这一强大工具。在Oracle中,Hint可以用来改变SQL的执行计划、固定SQL的执行计划。Oracle内部的很多特性也依赖于Hint,比如Outline、Profile等。
但是在日常工作中,很多开发人员或DBA,对Hint的使用仍然存在一些错误的方式。下面将列举主要的2种。(本文不讨论Hint的滥用即过度使用问题)。
1. NOLOGGING的不正确使用。
很知道,在进行数据处理时,如果不产生日志或只产生少量的日志,将会有明显的、甚至是巨大的效率提升。下面有几条不同的SQL:
INSERT INTO T1 NOLOGGING;
INSERT INTO T1 SELECT * FROM T2 NOLOGGING;
INSERT /*+ NOLOGGING */ INTO T1 VALUES ('0');
INSERT /*+ NOLOGGING */ INTO T1 SELECT * FROM T2;
DELETE /*+ NOLOGGING */ FROM T1;
UPDATE /*+ NOLOGGING */ T1 SET A='1';
实际上,上述所有的SQL没有一个能够实现“不产生”日志的数据更改操作。第1-2条SQL语句虽然没有将NOLOGGING写为Hint的形式,但是也是很的错误写法,一并列在此处。事实上,NOLOGGING并不是Oracle的一个有效的Hint,而是一个SQL关键字,通常用于DDL语句中。这里NOLOGGING相当于给SELECT的表指定了一个别名为“NOLOGGING”。下面是NOLOGGING的一些正确用法:
CREATE TABLE T1 NOLOGGING AS SELECT * FROM T2; CREATE INDEX T1_IDX ON T1(A) NOLOGGING; ALTER INDEX T1_IDX REDUILD ONLINE NOLOGGING; ALTER TABLE T1 NOLOGGING;
上述SQL中,最后一条SQL只是将表的LOGGING属性改为"NO"。而之前的几条SQL能够有效地减少DDL操作时减少的日志量。
在DML操作中,只有下面一种方式能够在大数据量时仍然只会产生极少量的日志:
INSERT /*+ APPEND */ INTO T1 SELECT * FROM T2;
也就是使用append hint。但是这个hint要达到目的,需要以下几个条件:

使用INSERT /*+ APPEND */ INTO .. SELECT .. FROM形式的INSERT SQL。
如果是在归档模式下,需要将表的LOGGING属性置为NO。
表空间或的FORCE LOGGING属性为NO。注意在非归档模式下也是可以设置FORCE LOGGING的。
这里提到的insert语句中的append hint,对于索引,仍然会产生日志,也就是说append hint对索引是没有效果的。
另外,DDL中使用的nologging关键字和inset语句中使用的append hint,并不是说完全不产生日志,只是对表的数据块的数据部分的更改不会有日志产生,但是SQL执行过程中数据字典的更改、空间分配等递归SQL、段头和位图块的更改、将数据块标记为unrecoverable等仍然会产生少量日志。
2. Hint的不正确写法。
这是一个比较不容易发现的问题。下面几条SQL,哪一条SQL的append hint会生效:
1. INSERT /*+ append,parallel(t1) */ INTO T1 SELECT * FROM T2; 2. INSERT /*+ parallel(t1), append */ INTO T1 SELECT * FROM T2; 3. INSERT /*+ this is append */ INTO T1 SELECT * FROM T2; 4. INSERT /*+ this append */ INTO T1 SELECT * FROM T2;
要回答这个问题,请先看下面的测试(测试环境:10.2.0.1 for Windows):
SQL> INSERT /*+ append,parallel(t1) */ INTO T1 SELECT * FROM T2;
已创建55640行。
统计信息
----------------------------------------------------------
12304 redo size
SQL> COMMIT;
SQL> INSERT /*+ parallel(t1), append */ INTO T1 SELECT * FROM T2;
已创建55640行。
统计信息
----------------------------------------------------------
5739584 redo size
SQL> COMMIT;
SQL> INSERT /*+ this is append */ INTO T1 SELECT * FROM T2;
已创建55640行。
统计信息
----------------------------------------------------------
5746604 redo size
SQL> COMMIT;
SQL> INSERT /*+ this append */ INTO T1 SELECT * FROM T2;
已创建55640行。
统计信息
----------------------------------------------------------
12052 redo size
SQL> COMMIT;
从上面的输出可以看到,通过insert语句执行产生的redo size判断,4条SQL语句中,1和4这2条SQL中的append hint起了作用,而2和3这2条SQL中的append hint没有起作用。我们看看第1和第2条SQL,只不过是parallel和append换了个位置,结果就截然不同;而第3和第4条SQL,只是一个多了"is"这个词,另一个没有,其结果也完全不同。这里有什么玄机吗?
这里就需要了解Oracle在解析SQL时,是怎样解析hint的。
Oracle在解析hint,从左到右进行,如果遇到一个词是oracle关键字或者说是保留字,将忽略这个词以及之后的所有词。如果遇到的一个词即不是关键字也不是hint,就忽略该词。如果遇到的一个词是有效的hint,那么就会保留该hint。
Oracle的保留字或者说是关键词(虽然二者在意义不一样,但这里不将其区分),可以通过视图v$reserved_words来查询。"is"正是一个关键词,甚至连","(逗号)也是一个关键词。这样,上面的第2和第3条SQL,Oracle解析时当遇到","和"is"时,就忽略了后面的所有hint。在第4条SQL中,this并不是一个关键词,所以append hint有效。基于这个原理,下面的一条SQL中的hint也是不起作用的:
INSERT /*+ NOLOGGING APPEND */ INTO T1 SELECT * FROM T2;
在9.2.0.8和11.2.0.2这2个版本下进行同样的测试,结果完全一样。
为了避免这样的情况,在SQL中书写hint时,在/*+ */和--+这2种结构内只写hint,而不要写逗号,或者是其他的注释。如果要对SQL写注释,在专门的注释结构中写入。比如/* test comment */。如果与hint混写注释,虽然当时没有关键词在里面,但随着版本升级,很可能会加入新的关键词。
另外,一些很常见的hint形式,比如/*+ parallel(t,8) */,/*+ index(t,t_idx) */,虽然当前没有问题,但标准的写法应该是:
/*+ parallel(t 8) */,/*+ index(t t_idx) */
--end end.
hint
客户一套运行在Oracle 10.2.0.5 RAC上的系统,间歇性地出现性能问题。其性能现象为前台反映性能缓慢,从系统上看CPU利用率大幅增加,load增加。这种性能问题通常在出现几分钟后自动恢复正常。
从AWR中的TOP 5等待来看:
Top 5 Timed Events Avg %Total ~~~~~~~~~~~~~~~~~~ wait Call Event Waits Time (s) (ms) Time Wait Class ------------------------------ ------------ ----------- ------ ------ ---------- latch: cache buffers lru chain 774,812 140,185 181 29.7 Other gc buffer busy 1,356,786 61,708 45 13.1 Cluster latch: object queue header ope 903,456 55,089 61 11.7 Other latch: cache buffers chains 360,522 49,016 136 10.4 Concurrenc gc current grant busy 112,970 19,893 176 4.2 Cluster -------------------------------------------------------------
可以看到,TOP 5中,有3个是latch相关的等待,而另外2个则是跟RAC相关的等待。
如果再查看更细的等待数据,可以发现其他问题:
Avg %Time Total Wait wait Waits Event Waits -outs Time (s) (ms) /txn ---------------------------- -------------- ----- ----------- ------- --------- latch: cache buffers lru cha 774,812 N/A 140,185 181 1.9 gc buffer busy 1,356,786 6 61,708 45 3.3 latch: object queue header o 903,456 N/A 55,089 61 2.2 latch: cache buffers chains 360,522 N/A 49,016 136 0.9 gc current grant busy 112,970 25 19,893 176 0.3 gcs drm freeze in enter serv 38,442 97 18,537 482 0.1 gc cr block 2-way 1,626,280 0 15,742 10 3.9 gc remaster 6,741 89 12,397 1839 0.0 row cache lock 52,143 6 9,834 189 0.1
从上面的数据还可以看到,除了TOP 5等待,还有"gcs drm freeze in enter server mode“以及"gc remaster"这2种比较少见的等待事件,从其名称来看,明显与DRM有关。那么这2种等待事件与TOP 5的事件有没有什么关联?。MOS文档"Bug 6960699 - "latch: cache buffers chains" contention/ORA-481/kjfcdrmrfg: SYNC TIMEOUT/ OERI[kjbldrmrpst:!master] [ID 6960699.8]”提及,DRM的确可能会引起大量的"latch: cache buffers chains"、"latch: object queue header operation"等待,虽然文档没有提及,但不排除会引起”latch: cache buffers lru chain“这样的等待。
为了进一步证实性能问题与DRM相关,使用tail -f命令监控LMD后台进程的trace文件。在trace文件中显示开始进行DRM时,查询v$session视图,发现大量的 "latch: cache buffers chains" 、"latch: object queue header operation"等待事件,同时有"gcs drm freeze in enter server mode“和"gc remaster"等待事件,同时系统负载升高,前台反映性能下降。而在DRM完成之后,这些等待消失,系统性能恢复到正常。
看起来,只需要关闭DRM就能避免这个问题。怎么样来关闭/禁止DRM呢?很多MOS文档提到的方法是设置2个隐含参数:
_gc_affinity_time=0 _gc_undo_affinity=FALSE
不幸的是,这2个参数是静态参数,也就是说必须要重启实例才能生效。
实际上可以设置另外2个动态的隐含参数,来达到这个目的。按下面的值设置这2个参数之后,不能完全算是禁止/关闭了DRM,而是从”事实上“关闭了DRM。
_gc_affinity_limit=250 _gc_affinity_minimum=10485760
甚至可以将以上2个参数值设置得更大。这2个参数是立即生效的,在所有的节点上设置这2个参数之后,系统不再进行DRM,经常一段时间的观察,本文描述的性能问题也不再出现。
下面是关闭DRM之后的等待事件数据:
Top 5 Timed Events Avg %Total
~~~~~~~~~~~~~~~~~~ wait Call
Event Waits Time (s) (ms) Time Wait Class
------------------------------ ------------ ----------- ------ ------ ----------
CPU time 15,684 67.5
db file sequential read 1,138,905 5,212 5 22.4 User I/O
gc cr block 2-way 780,224 285 0 1.2 Cluster
log file sync 246,580 246 1 1.1 Commit
SQL*Net more data from client 296,657 236 1 1.0 Network
-------------------------------------------------------------
Avg
%Time Total Wait wait Waits
Event Waits -outs Time (s) (ms) /txn
---------------------------- -------------- ----- ----------- ------- ---------
db file sequential read 1,138,905 N/A 5,212 5 3.8
gc cr block 2-way 780,224 N/A 285 0 2.6
log file sync 246,580 0 246 1 0.8
SQL*Net more data from clien 296,657 N/A 236 1 1.0
SQL*Net message from dblink 98,833 N/A 218 2 0.3
gc current block 2-way 593,133 N/A 218 0 2.0
gc cr grant 2-way 530,507 N/A 154 0 1.8
db file scattered read 54,446 N/A 151 3 0.2
kst: async disk IO 6,502 N/A 107 16 0.0
gc cr multi block request 601,927 N/A 105 0 2.0
SQL*Net more data to client 1,336,225 N/A 91 0 4.5
log file parallel write 306,331 N/A 83 0 1.0
gc current block busy 6,298 N/A 72 11 0.0
Backup: sbtwrite2 4,076 N/A 63 16 0.0
gc buffer busy 17,677 1 54 3 0.1
gc current grant busy 75,075 N/A 54 1 0.3
direct path read 49,246 N/A 38 1 0.2
那么,这里不得不提的是,什么是DRM?DRM对系统来说有什么好处?本文不再详述,因为下面的2篇文档已经描述得比较清楚,有兴趣的朋友可以参考:
MOS文档:DRM - Dynamic Resource management [ID 390483.1]
Object Remastering In RAC
关于本文所使用的2个隐含参数,在上述第二篇文档中也有详细描述。
--END---
rac
当一个存储过程所依赖(引用的)对象发生某些更改时,会使得存储过程失效(invalidated),比如存储过程依赖(引用)的表增加减少了列、存储过程依赖(引用)的存储过程被重新编译。本文将介绍一种特殊的会引起存储过程失效的情况。
下面通过测试来演示与DB LINK相关的存储过程失效的情况:
1. 在TEST用户下创建到db1的DB LINK:
SQL> create database link to_db connect to perfstat identified by xxx using 'db1'; 链接已创建。
2.在TEST2用户下创建到db2的DB LINK,DB LINK的名称仍然为to_db,但实际上连接的与TEST用户下to_db这个DB LINK连接的并不是同一个:
SQL> create database link to_db connect to perfstat identified by xxx using 'db2'; 链接已创建。
3. 在TEST用户下创建存储过程TEST_P1:
SQL> create or replace procedure test_p1 2 is 3 v_dbid number; 4 begin 5 select dbid into v_dbid from stats$snapshot@to_db where rownum<2; 6 end; 7 / 过程已创建。 SQL> select object_id,status from user_objects where object_name='TEST_P1'; OECT_ID STATUS ---------- ---------- 18443 VALID
4. 在TEST2用户下创建存储过程TEST2_P1,这里存储过程TEST2_P1的代码与TEST用户下存储过程TEST_P1的代码明显不同。这两个存储过程的共同点是都引用了STATS$SNAPSHOT@TO_DB这个远程表。由于TEST和TEST2用户下TO_DB连接的是不同的,因此这2个存储过程引用的STATS$SNAPSHOT@TO_DB位于不同的上,也就是说是不同的表:
SQL> create or replace procedure test2_p1 2 is 3 v_snap_time date; 4 begin 5 select snap_time into v_snap_time from stats$snapshot@to_db where rownum<2; 6 end; 7 / 过程已创建。 SQL> select object_id,status from user_objects where object_name='TEST2_P1'; OECT_ID STATUS ---------- ---------- 18445 VALID
5. 在TEST用户下查看存储过程TEST_P1的状态,发现其状态为INVALID,而实际上这个时候这个存储过程以及其引用的对象没有任何变更:
SQL> select object_id,status from user_objects where object_name='TEST_P1'; OECT_ID STATUS ---------- ---------- 18443 INVALID
6. 如果重新编译TEST.TEST_P1,那么其状态会成为VALID,但是这个时候TEST2.TEST2_P1则又莫名其妙地变成为INVALID:
SQL> alter procedure test.test_p1 compile;
过程已更改。
SQL> select object_id,owner,object_name,status from dba_objects
where object_name in ('TEST_P1','TEST2_P1');
OECT_ID OWNER OECT_NAME STATUS
---------- --------------- ------------------------------ ----------
18443 TEST TEST_P1 VALID
18445 TEST2 TEST2_P1 INVALID
7. 在TEST2用户下执行存储过程TEST2_P1,然后再次检查存储过程状态:
SQL> exec test2_p1
PL/SQL 过程已成功完成。
SQL> select object_id,owner,object_name,status from dba_objects
2 where object_name in ('TEST_P1','TEST2_P1');
OECT_ID OWNER OECT_NAME STATUS
---------- --------------- ------------------------------ ----------
18443 TEST TEST_P1 INVALID
18445 TEST2 TEST2_P1 VALID
可以看到,TEST.TEST_P1失效了,而TEST2.TEST2_P1又正常了。
在这里就可以提出问题了:为什么创建了TEST2.TEST2_P1这个存储过程之后,TEST.TEST_P1就失效了?为什么TEST.TEST_P1重新编译之后TEST2.TEST2_P1又失效了?为什么TEST2.TEST2_P1重新执行(有一个隐式的编译过程)后,TEST.TEST_P1又失效了?
其实上以上三个问题可以归纳为一个问题:为什么TEST.TEST_P1和TEST2.TEST2_P1这2个存储过程,在其中一个状态正常的情况下,另一个为失效状态?
Read the rest of this entry
dblink
我给《DBA 手记III》投的一篇稿子,名为《一次由隐含参数引起性能问题的处理》,这篇文章描述了一套关键的系统,由于"_kghdsidx_count"这个参数设置为1,导致了严重的性能问题。从故障现象上看是大量的library cache latch的等待,以及shared pool latch的等待,但是前者的等待时间比后者长得多。在文章中,我提到,在当时我推断,由于"_kghdsidx_count"这个隐含参数设置为1,导致shared pool只有1个subpool,引起了shared pool latch的严重竞争,进而引起了library cache cache的更为严重的竞争,这个竞争的过程如下:
由于"_kghdsidx_count"=1,使得shared pool latch只有1个child latch。而library cache latch的child latch数量跟CPU数量有关,最大值为67,编号为child #1-#67。
会话1持有shared pool latch。
会话2解析SQL语句,首先持有了library cache latch的child latch,假设这里为child #1,然后去请求shared pool latch。很显然,这时候被会话1持有,那么会话2就会等待shared pool latch。
会话3解析SQL语句,首先需要请求library cache latch,如果请求的library cache child latch刚好是#1,那么由于会话2持有了这个child latch,就会等待library cache latch。
因此,实际上会话1和会话2的shared pool latch的竞争引起了会话3的library cache latch的等待。如果并发数不是太高,那么shared pool latch的竞争看上去就会比library cache latch的竞争多一些。但是如果有几百个活动会话,这个时候,就会有大量的会话首先等待library cache latch,因为在解析SQL时是首先需要获取library cache latch再获取shared pool latch。由于大量的软解析,甚至不需要获取shared pool latch,同时一个大型的OLTP系统中,某几条相同的SQL并发执行的概率很高,这样会使很多会话同时请求同一library cache child latch;另外,在解析过程中,可能会多次请求library cache latch和shared pool latch,而前者请求和释放的次数会比后者多得多;这样大量的会话在获取library cache latch时处于等待状态,从现象上看就比shared pool latch的等待多得多。
而本文主要表达的是,怎么来验证在解析时,Oracle进程在持有了library cache latch的情况下去请求shared pool latch,而不是在请求shared pool时不需要持有library cache latch。
由于这个验证过程过于internal,所以没有在《DBA手记III》中描述出来。这里写出来,供有兴趣的朋友参考。
验证上面的这一点,有2个方法。下面以测试过程来详细描述。
测试环境 :Oracle 10.2.0.5.1 for Linux X86.
方法一:使用oradebug。
1. 将的"_kghdsidx_count"参数值设为1,并重启,以保证只有一个shared pool child latch。
2. 使用sqlplus连接到,假设这个会话是session-1,查询当前的SID:
SQL> select sid from v$mystat where rownum=1;
SID
----------
159
同时获得当前连接的spid为2415。
3. 新建一个连接到,假设会话是session-2,查询shared pool latch的地址,并使用oradebug将这个地址对应的值置为1,以表示该latch已经被持有:
SQL> select addr,latch#,level#,child#,name,gets from v$latch_children where name='shared pool'; ADDR LATCH# LEVEL# CHILD# NAME GETS -------- ---------- ---------- ---------- -------------------------------------------------- ---------- 200999BC 216 7 1 shared pool 34949 20099A20 216 7 2 shared pool 6 20099A84 216 7 3 shared pool 6 20099AE8 216 7 4 shared pool 6 20099B4C 216 7 5 shared pool 6 20099BB0 216 7 6 shared pool 6 20099C14 216 7 7 shared pool 6 SQL> oradebug poke 0x200999BC 4 1 BEFORE: [200999BC, 200999C0) = 00000000 AFTER: [200999BC, 200999C0) = 00000001
4. 在session-1会话中执行下面的SQL:
SQL> select owner from t where object_id=3003;
正如预料之中的反映,这个会话hang住。
5. 在session-2中,对session-1的进程作process dump。(注意这个时候不能查询v$session_wait、v$latchholder等视图)
SQL> oradebug setospid 2415 Oracle pid: 15, Unix process pid: 2415, image: oracle@xty (TNS V1-V3) SQL> oradebug dump processstate 10 Statement processed. SQL> oradebug tracefile_name /oracle/app/oracle/admin/xty/udump/xty_ora_2415.trc
然后从/oracle/app/oracle/admin/xty/udump/xty_ora_2415.trc这个TRACE文件中可以找到下面的信息:
Read the rest of this entry
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-30053-20.html
没钱的不是就要自宫
2001年的算老旧
做为老百姓不建议