Oracle rac进阶管理专家指导系列文档
Oracle恢复目录的管理使用简要
I. 使用恢复目录存储RMAN备份记录
- Oracle 官方建议把恢复目录建议于独立的数据库中。如果把恢复目录与其他一些数据混杂在某库中,若该库失败则恢复目录一起丢失,这将导致恢复异常困难。
- 在恢复目录中登记某个库被称作注册(registration).可以在恢复目录中注册多个目标库。举例来说,你可以注册数据库 prod1,prod2,和prod3在一个单独的由用户catowner拥有的目录中,而该目录位于一个叫catdb的数据库中。 因为RMAN通过DBID即数据库的身份证来分辨各个库。每个在恢复目录中注册过的目标库都有一个唯一的DBID.
- 恢复目录主要包括以下RMAN的使用情况信息:
l 数据文件和归档日志的备份集和备份片
l 数据文件的拷贝
l 归档日志及其拷贝
l 目标库中的表空间和数据文件
l 储存的脚本
l RMAN的永久性配置
- 恢复目录保存了目标库控制文件中重要的RMAN操作原数据。同步恢复目录保证与控制文件中当前信息同步。
- RMAN 创建快照控制文件,即临时控制文件,当每次需要做全局同步时。快照临时文件保证了RMAN同步时的一致性读。数据库服务进程保证同时只有一个快照临时文件的存在,这对于保证RMAN操作不受其他进程干扰是必要的。
- 丢失恢复目录将导致严重的恢复问题。如何备份恢复目录可参考一般数据库的备份方式。
- 关于恢复目录的兼容性,可以通过查询恢复目录用户模式下的rcver表了解参与恢复目录使用端的版本号,示例:
SQL> SELECT * FROM rcver;
VERSION
------------
08.01.05.00
09.00.01.00
10.02.01.00
只要是8i之后版本一般不存在兼容性问题。
II 管理恢复目录
创建恢复目录
管理恢复目录中的目标库记录
同步恢复目录
恢复目录模式下的控制文件管理
备份恢复目录
导入和导出恢复目录
增强恢复目录可用性
查询恢复目录视图
更新恢复目录
删除恢复目录
- 创建恢复目录,创建恢复目录分成三步:
- 配置恢复目录所在数据库
- 创建恢复目录拥有者
- 创建恢复目录本身
配置恢复目录数据库
若使用恢复目录,RMAN要求维护恢复目录所在模式。恢复目录储存在当前模式的默认表空间中,注意SYS不能是恢复目录的拥有者。我们强烈建议恢复目录数据库使用归档模式。同时必须分配足够的空间给恢复目录所在模式,恢复目录所占用的空间取决于使用恢复目录的目标数据库的数量。适当地为恢复目录库规划容量是必要的。应当保证恢复目录库和目标数据库的不占用同一磁盘。
创建目录拥有者
在合理配置恢复目录库后,我们来创建目录拥有者
使用目录库上的SYS帐号登录
假定当前有一个tool表空间来保存目录
使用temp临时表空间为用户默认临时表空间
如下步骤:
CONNECT SYS/oracle@catdb AS SYSDBA
SQL> CREATE USER rman IDENTIFIED BY cat
TEMPORARY TABLESPACE temp
DEFAULT TABLESPACE tools
QUOTA UNLIMITED ON tools;
同时我们要授予 recovery_catalog_owner 权限给用户,该角色拥有管理创建恢复目录的权限。
SQL> GRANT RECOVERY_CATALOG_OWNER TO rman;
创建恢复目录
在创建恢复目录用户后,使用RMAN建立恢复目录,操作如下:
$ rman
RMAN> CONNECT CATALOG rman/cat@catdb --以目录用户连接恢复目录库
RMAN> create catalog — 建议恢复目录
当然也可以指定使用的表空间:
RMAN> create catalog tablespace users;
成功建立恢复目录后,可以查询目录下已经存在的目录使用的基表。
SQL>select table_name from user_tables;
2. 管理恢复目录中的目标库记录
ü 在恢复目录中注册目标数据库
ü 在恢复目录中注销目标数据库
ü 在恢复目录中重置数据库
ü 在恢复目录中移除已删除的记录
在恢复目录中注册目标数据库
首先确定恢复目录库已经打开,从目标库主机登录:
$ rman TARGET / CATALOG rman/cat@catdb
若目标库未启动,首先启动到加载模式:
RMAN> STARTUP MOUNT;
注册目标库:
RMAN> REGISTER DATABASE;
RMAN会自动在恢复目录中记录目标库的各种信息,将目标库控制文件中的
元信息复制到恢复目录中,可以使用以下命令确认注册情况:
RMAN> REPORT SCHEMA;
Report of database schema
File Size(MB) Tablespace RB segs Datafile Name
---- ---------- ---------------- ------- -------------------
1 307200 SYSTEM NO /oracle/oradata/trgt/system01.dbf
2 20480 UNDOTBS YES /oracle/oradata/trgt/undotbs01.dbf
3 10240 CWMLITE NO ...
在恢复目录中登记备份文件
若有备份文件未在控制文件或恢复目录中存在对应的记录,则需要登记该文件,此处的(control file 为目标数据库control file)。
示例:
RMAN> CATALOG DATAFILECOPY '/disk1/old_datafiles/01_01_2003/users01.dbf';
RMAN> CATALOG ARCHIVELOG '/disk1/arch_logs/archive1_731.dbf',
'/disk1/arch_logs/archive1_732.dbf';
RMAN> CATALOG BACKUPPIECE '/disk1/backups/backup_820.bkp';
在恢复目录中登记多个目标库
可以在一个恢复目录中注册多个目标库,前提是目标库的DBID唯一。
在恢复目录中注销目标库
可以使用命令: unregister database 在RMAN中注销目标数据库。当数据库被
注销,所有的RMAN记录都会丢失,所以要小心操作。
在恢复目录中移除已经删除的记录
在9i之后版本,RMAN在删除备份文件的同时会删除在恢复目录中的对应物记
录,而9i以前版本则只将对应物记录标志为delete.可以通过运行脚本
prgrmanc.sql来删除对应物记录,该脚本储存在($ORACLE_HOME/rdbms/admin)
目
录下。示例如下:
% sqlplus rman/cat@catdb
SQL> @?/rdbms/admin/prgrmanc.sql删过期备份信息
同步恢复目录
当恢复目录当前状态晚于数据库控制文件中的备份信息时,则需要使用同步恢复
目录,这种情况只会出现在一段时间使用恢复目录而一段时间不使用恢复目录的
情况下,造成的时间段差异。RMAN会在您做某些操作时自动完成同步,例如
Backup命令,当然你也可以手动同步: resync catalog .
管理控制文件
数据库参数CONTROL_FILE_RECORD_KEEP_TIME
决定了控制文件中记录可能被复
用的最短自然天数,因此你保证恢复目录在此期间完成同步,否则可能控制文件
中的记录丢失,则需要手动登记备份文件。CONTROL_FILE_RECORD_KEEP_TIME有
效期内需要定期同步。
备份恢复目录
备份恢复目录数据库十分重要,若恢复目录数据库丢失则所有的备份信息将丢
失,导致恢复十分困难。
备份恢复目录数据库与一般的数据库没有大的区别,以下为注意事项:
恢复目录数据库因该运行在归档模式下
使用备份策略冗余量大于一
在不同的介质上备份
不使用恢复目录记录备份信息
使用控制文件自动备份,rman中可以自动完成
结构图:
更新恢复目录
若您使用的恢复目录版本低于使用的客户端,则您需要更新恢复目录。举例来说
当前您使用了8.1版的客户端RMAN,而恢复目录是8.0版本的,则需要更新。
当恢复目录版本高于您使用的客户端,则upgrade catalog报错。更新操作实例如
下:
sqlplus> connect sys/oracle@catdb as sysdba;
sqlplus> grant TYPE to rman;
% rman TARGET / CATALOG rman/cat@catdb
UPGRADE CATALOG;
recovery catalog owner is rman
enter UPGRADE CATALOG command again to confirm catalog upgrade
UPGRADE CATALOG;
recovery catalog upgraded to version 09.02.00
DBMS_RCVMAN package upgraded to version 09.02.00
DBMS_RCVCAT package upgraded to version 09.02.00
删除恢复目录
当恢复目录不在需要时可以在所在数据库中彻底删除目录结构和数据,删除将丢
失所有注册过的备份信息,操作要小心。示例操作:
% rman TARGET / CATALOG rman/cat@catdb
Issue the DROP
CATALOG
command twice to confirm:
DROP CATALOG;
recovery catalog owner is rman
enter DROP CATALOG command again to confirm catalog removal
DROP CATALOG;
延迟块清除介绍
在Oracle中数据锁(这里主要指TX类型行锁)实际上是数据的属性,存储在块首部,称之为事务槽(ITL)。COMMIT操作的职责包括释放块上的锁,实际的释放方式即清除块上相应的事务槽,但这里存在一个性能的考量。设想一个UPDATE大量数据的操作,因为执行时间较长,一部分已修改的块已被缓冲池flush out写至磁盘,当UPDATE操作完成执行COMMIT操作时,则需要将那些已写至磁盘的数据块重新读入,这将消耗大量I/O,并使COMMIT操作十分缓慢;为了解决这一矛盾,Oracle使用了延迟块清除的方案,对待存在以下情况的块COMMIT操作不做块清除:
在更新过程中,被缓冲池flush out写至磁盘的块
若更新操作涉及的块超过了块缓冲区缓存的10%时,超出的部分块。
虽然COMMIT放弃对这些块的块清除(block cleanout)操作,但COMMIT操作仍会修改回滚段的段头,回滚段的段头包括了段中的事务的字典,COMMIT操作将本事务转化为非ACTIVE状态。
当下一次操作如SELECT,UPDATE,INSERT或DELETE访问到这些块时可能需要在读入后完成块清除,这样的操作称之为块延迟清除(deferred block cleanout);块延迟清除通过事务槽上的回滚段号,槽号等信息访问回滚段头的事务字典,若事务不再活跃或事务过期则完成清除块上的事务槽,事务槽清除后继续执行相应的操作。
块延迟清除的影响在SELECT操作过程中体现的最为明显。总结来说块延迟清除是COMMIT操作的一个延续,始终是一种十分轻微的操作,且该种操作是行级的,不会使段(Segment)的属性有所改变。
重做日志时间戳说明
首先创建一个包括序列号与时间戳的表,通过对该表插入当前时间戳并记录插入操作的开始时间,进行中时间,与结束时间,以便与重做日志中的时间戳对比。
表的定义如下:
create table tim (tn int,itime timestamp); |
使用以下匿名块插入数据:
declare stime timestamp; dtime timestamp; etime timestamp;
begin for i in 1 .. 10 loop stime := systimestamp; insert into tim values (i, systimestamp); etime := systimestamp; select itime into dtime from tim where tn = i; dbms_output.put_line(‘start time: ‘ || to_char(stime,’HH24:MI:SS:FF’) || ‘ doing time: ‘ || to_char(dtime,’HH24:MI:SS:FF’) || ‘ end time:’ || to_char(etime,’HH24:MI:SS:FF’));
dbms_lock.sleep(2.9); commit; end loop; end; |
其中stime,dtime,etime,分别记录了每次插入的开始时间,进行中时间,以及结束的时间,操作结束后输出以上时间,之后休眠2.9秒,COMMIT之后继续循环。该过程输出如下:
start time: 15:10:06:986235000 doing time: 15:10:06:987493000 end time:15:10:06:987962000 start time: 15:10:09:894372000 doing time: 15:10:09:894505000 end time:15:10:09:894738000 start time: 15:10:12:796921000 doing time: 15:10:12:797057000 end time:15:10:12:797293000 start time: 15:10:15:698497000 doing time: 15:10:15:698642000 end time:15:10:15:698856000 start time: 15:10:18:601077000 doing time: 15:10:18:601222000 end time:15:10:18:601451000 start time: 15:10:21:502664000 doing time: 15:10:21:502803000 end time:15:10:21:503044000 start time: 15:10:24:405294000 doing time: 15:10:24:405435000 end time:15:10:24:405673000 start time: 15:10:27:307828000 doing time: 15:10:27:307979000 end time:15:10:27:308193000 start time: 15:10:30:209477000 doing time: 15:10:30:209619000 end time:15:10:30:209865000 start time: 15:10:33:112033000 doing time: 15:10:33:112173000 end time:15:10:33:112397000 |
可以看到每次循环内开始时间,进行中时间,结束时间的差值很小,证明插入是在瞬间完成的。
之后我们查看tim表中的内容:
select tn,to_char(itime,‘HH24:MI:SS:FF’) ITIME ,dump(itime,16) HEX_ITIME from tim;
|
ITIME即插入操作中的时间,HEX_ITIME为ITIME在数据块中的16进制存放格式,用以与重做日志中的16进制数据对比。
我们dump当前的重做日志,该日志包括了方才所做的DML操作:
alter system dump logfile ‘/u01/oradata/orcl/redo01.log’; |
查看dump所产生的跟踪文件,可以发现以下记录:
REDO RECORD – Thread:1 RBA: 0x000026.0000000c.0010 LEN: 0x02a0 VLD: 0x0d
SCN: 0x0000.000b727b SUBSCN: 1 06/14/2009 15:10:09 CHANGE #1 TYP:1 CLS: 1 AFN:1 DBA:0x0040ebf2 OBJ:51447 SCN:0x0000.000b7279 SEQ: 1 OP:13.5 KTSFRBFMT (block format) redo: Segobjd: 0x0000c8f7 type: 1 itls: 2 CHANGE #2 TYP:0 CLS: 1 AFN:1 DBA:0x0040ebf2 OBJ:51447 SCN:0x0000.000b727b SEQ: 1 OP:13.6 KTSFRBLNK (block link modify) redo: Opcode: LSET (lock set) Next dba: 0x0040ebf3 itli: 0 CHANGE #3 TYP:0 CLS: 1 AFN:1 DBA:0x0040ebf2 OBJ:51447 SCN:0x0000.000b727b SEQ: 2 OP:13.6 KTSFRBLNK (block link modify) redo: Opcode: LWRT (lock write) Next dba: 0x00000000 itli: 0 CHANGE #4 TYP:0 CLS: 4 AFN:1 DBA:0x0040ebf1 OBJ:51447 SCN:0x0000.000b726a SEQ: 1 OP:13.7 KTSFRGRP (fgb/shdr modify freelist) redo: Opcode: HWMMV (move hwm) NBK: 1 Opcode: LUPD_LLIST (link a list) Slot no: 0, Count: 1 Flag: = 1 xid or slot0 ccnt: 0x0000.000.00000001 Head: 0x0040ebf2 Tail: 0x0040ebf2 CHANGE #5 TYP:0 CLS: 1 AFN:1 DBA:0x0040ebf2 OBJ:51447 SCN:0x0000.000b727b SEQ: 3 OP:11.2 KTB Redo op: 0x01 ver: 0x01 op: F xid: 0x0009.02b.0000015d uba: 0x00808df4.0163.24 KDO Op code: IRP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x0040ebf2 hdba: 0x0040ebf1 itli: 1 ispac: 0 maxfr: 4863 tabn: 0 slot: 0(0x0) size/delt: 18 fb: –H-FL– lb: 0x1 cc: 2 null: — col 0: [ 2] c1 02 col 1: [11] 78 6d 06 0e 10 0b 07 3a db f2 88 |
在以上重做记录(REDO RECORD)的CHANGE #5中包含了插入TIMESTAMP “col 1: [11] 78 6d 06 0e 10 0b 07 3a db f2 88”即表中第一条数据的ITIME列为15:10:06:987493,而该条重做记录的时间戳为:15:10:09,要晚于插入的进行中时间,与插入结束时间。
REDO RECORD – Thread:1 RBA: 0x000026.0000000e.0010 LEN: 0x01e4 VLD: 0x0d
SCN: 0x0000.000b727d SUBSCN: 1 06/14/2009 15:10:12 CHANGE #1 TYP:2 CLS: 1 AFN:1 DBA:0x0040ebf2 OBJ:51447 SCN:0x0000.000b727b SEQ: 5 OP:11.2 KTB Redo op: 0x01 ver: 0x01 op: F xid: 0x0003.026.00000141 uba: 0x00806194.0257.19 KDO Op code: IRP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x0040ebf2 hdba: 0x0040ebf1 itli: 2 ispac: 0 maxfr: 4863 tabn: 0 slot: 1(0x1) size/delt: 18 fb: –H-FL– lb: 0x2 cc: 2 null: — col 0: [ 2] c1 03 col 1: [11] 78 6d 06 0e 10 0b 0a 35 51 10 28 |
以上为后一条重做记录,该记录的CHANGE #1中包含了插入TIMESTAMP” col 1: [11] 78 6d 06 0e 10 0b 0a 35 51 10 28”即ITIME列为15:10:09:894505,而该条重做记录的时间戳为:15:10:12。
以上实验可以证明,重做日志中记录的时间戳并不是SQL语句提交的时间,也晚于SQL语句完成的时间,实际为重做记录由缓存形式写至在线日志文件的时间。即SQL语句的完成时间一般都要早于重做日志中相应记录的时间戳。由于LGWR进程至少3秒会写出一次的特性,重做日志中的时间戳最早为语句的结束时间,最晚为语句结束后的三秒。
同样的查询每次都产生大量物理读的调优示例
12月中旬用户反映综合传输网管库上的一个查询影响迟缓,具体现象表现为当多个用户在应用界面上同时点下查询后,结果返回耗时长,影响正常业务的运作。经过初步分析该操作主要的等待事件在db file sequential read上,为了进一步明确问题,我们在系统的高峰时段使用性能报告工具抓取了统计信息,以下为top3等待事件:
Event Waits Timeouts Time (s) (ms) —————————- ———— ———- ———- —— enqueue 542 402 1,406 2595 db file sequential read 446,099 0 391 1 db file scattered read 156,634 0 209 1 可以看到db file sequential read事件仅次于数据库队列事件为主要的数据库性能瓶颈,以下列出缓存占用较高的典型SQL: Selecta.objectid,a.emsalarm_time,a.emsend_time,c.label_cn,c.alias,a.alarm_name,a.alm_devinfo from traph c,alarm_to_traph b,current_alarm a where a.cuid=b.related_alarm_cuid and b.related_traph_cuid=c.cuid and (c.ext_ids=’,8,’ or c.ext_ids=’,9,’ or c.ext_ids=’,12,’ or c.ext_ids=’,19,’ or c.ext_ids=’,25,’) and a.emsend_time is null and a.emsalarm_time > to_date(‘2008-11-19′,’yyyy-MM-dd’) 经过进一步追踪我们发现以上SQL正是应用界面点击查询所做的操作,这就明确了此次优化的主要目的,即分析并尽可能降低该语句所产生的物理读和逻辑读,保证应用的正常运行。 |
具体分析 |
该句查询语句涉及到三个表的连接,因为应用设计使用的是基于RULE的优化模式,故执行计划倾向于使用索引来代替全表扫描,在表与表的连接方式上倾向使用嵌套循环即(NESTED LOOP),具体执行计划如下图:
经过查询缓存池中各个表的具体缓存状况发现,表ALARM_TO_TRAPH上的索引ALARM_TO_TRAPH_INDEX1与索引TRAPH_CUID均已被缓存,而在该执行计划中需要做全表扫描操作的CURRENT_ALARM则只有部分块被缓存,充分说明了引起物理读需求的正是对CURRENT_ALARM的全表扫描操作,为了进一步证实这一点,我们做了一次10046事件的trace,该事件可以记录SQL语句执行过程中详细的等待事件以及相关信息,在trace中发现大量db file sequential read等待时间,摘录如下: 以上记录中p1,p2对应了数据文件顺序读具体发生的文件号和块号,经过查询确定以上相关文件号和数据块号具对应于表CURRENT_ALARM,至此本次性能问题的主要原因已十分明确,即CURRENT_ALARM表未被完全缓存,引起问题的可能有两种:1.表中存在大量的chained rows即链式行;2.数据库高速缓存在系统高峰时段存在瓶颈。 对于前一种可能性,我们首先分析了表的结构,CURRENT_ALARM表包括96个列,其中包括大量varchar2(255)类型的长列,理论单行长度可能达到14K字节,的确可能引起严重的链式行,为了进一步证实,我们对该表做了一次链式行扫描,操作如下: ANALYZE TABLE CURRENT_ALARM LIST CHAINED ROWS INTO chained_rows; 该分析操作会将表上的链式行记录到临时表chained_rows中,查询chained_rows发现没有记录,说明表CURRENT_ALARM上没有链式行的问题。其后我们通过函数计算CURRENT_ALARM表上的行长度,发现最长的一行占用1367个字节,就目前来说仍不至于发生严重的链式行问题。 经过以上分析问题已经定位到了高峰时段数据库高速缓存的使用状况上了,通过在早晨,上班高峰时段以及下班时段的进一步观察,我们发现在早晨上班之前,buffer_cache中大约有600个free块可以立即使用,在该时段做上述查询不存在物理读的操作,而在上班高峰时段free块的数量下降到个位数乃至为零,即需要加载数据块时没有可以立即使用的空闲块,同时因为其他查询更为活跃,CURRNET_ALARM表上的数据块在读入后迅速被刷出,没有被缓存住以便于下次的查询,导致了性能问题的最终产生。 |
总结
由于数据库高速缓存在高峰时段没有空闲块导致需要全表扫描的表未被完全缓存,进而在多用户同时查询的情况下产生大量的物理读等待,影响了正常业务的运行。针对该问题提出以下建议:
- 增大SGA总量,增大数据库高速缓存即DB_CACHE_SIZE的空间,并扩展高速缓存的keep池,将表CURRENT_ALARM保存在keep池中,保证不被刷出。
- 定期分析CURRENT_ALARM表的行扩展情况,从而避免可能的链式行的产生。
- 在可能的情况下,为上述查询语句加上执行计划暗示即ALL_ROWS的hit,使其在连接方式上倾向于使用哈希连接,可以降低三分之一的逻辑读。
- 增大数据库使用内存总量的同时可能引发CPU的使用量有一定上升,需要密切关注主机的负载情况。
[gview file=”http://askmac.cn/wp-content/uploads/resource/BUFFPOOL.pdf”]
绑定变量介绍
Oracle在执行SQL语句时,普遍存在以下几个步骤:
- 当SQL语句首次执行,Oracle将确认该句语句的语法是否正确(语法解析Syntax parse)并进一步确认语句相关表和列的存在性等因素(语义解析semantic parse)以及优化器决定执行计划等步骤。整个过程称之为硬解析,硬解析消耗大量的CPU时间和系统资源。硬解析过多会有效降低系统性能。
- 若之前已进行过硬解析,且解析后的分析树和执行计划仍存在于共享池中,则同样的SQL仅需要软解析。软解析将输入的SQL语句转换为哈希代码,同共享池内哈希链表上的已有记录进行对比,找出对应的游标信息,使用已有的执行计划执行。
- 绑定变量,将实际的变量值代入SQL语句中。
- 执行SQL语句,查询语句将返回结果集。
不使用绑定变量的SQL语句,Oracle无法将它们视为相同的,如以下两句语句:
select * from emp where empno=1234
select * from emp where empno=5678 |
因为自由变量的不同,Oracle认为以上是2句不同的语句,则当第一条被硬解析后,第二条SQL执行时仍无法避免硬解析。实际在以上不使用绑定变量的情况中,只要自由变量有所改变则需要一次硬解析。这是强烈建议使用绑定变量的主要原因,使用绑定变量的语句变量的实际值仅在SQL执行的最后阶段被代入。如以下语句:
select * from emp where empno=:x |
该语句使用绑定值:x替代自由变量,在应用中语句可能以预编译或普通编译的方式存在,仅在执行阶段代入变量值,多次执行仅需要一次硬解析,较不使用绑定变量情况性能大大提升。
同时过多的硬解析还会引发共享池碎片过多的问题。因为每当需要硬解析一个SQL或者PLSQL语句时,都需要从shared pool中分配一块连续的空闲空间来存放解析结果。Oracle首先扫描shared pool查找空闲内存,如果没有发现大小正好合适的空闲chunk,就查找更大的chunk,如果找到比请求的大小更大的空闲chunk,则将它分裂,多余部分继续放到空闲列表中。因为过多的硬解析加剧了内存段分配的需求,这样就产生了碎片问题。系统经过长时间运行后,就会产生大量小的内存碎片。当请求分配一个较大的内存块时,尽管shared pool总空闲空间还很大,但是没有一个单独的连续空闲块能满足需要。这时,就可能产生 ORA-4031错误。
通常我们可以通过以下SQL语句将系统中非绑定变量的语句找出:
SELECT substr(sql_text,1,40) “SQL”,
count(*) , sum(executions) “TotExecs” FROM v$sqlarea WHERE executions < 5 –-语句执行次数 GROUP BY substr(sql_text,1,40) HAVING count(*) > 30 –-所有未共享的语句的总的执行次数 ORDER BY 2; |
以上语句在实际使用中substr函数截取到的字符串长度需要视乎实际情况予以变化。
对于非绑定变量且短期内无法修改的应用,Oracle存在参数cursor_sharing可以改善其表现。cursor_sharing默认为exact,对使用自由变量的语句不做额外处理;当设为force时,非绑定变量的SQL语句被进一步处理以达到共享SQL的目的,但以上处理步骤同样要消耗一定的CPU时间;当设为similar时,若数据库存在语句相关统计信息则其表现如exact,若无统计信息则表现为force。cursor_sharing参数是Oracle针对无法修改的非绑定变量应用所提出的折中方案,但cursor_sharing为force值时存在一定SQL引发bug或语句无效的情况,且额外的处理操作同样需要消耗一定量的CPU时间和系统资源。故针对系统性能的最优方案往往是直接修改应用代码,使用绑定变量特性。
UNDO表空间监控说明
在Oracle 10g版本中可以使用V$UNDOSTAT视图用于监控实例中当前事务使用UNDO表空间的情况。视图中的每行列出了每隔十分钟从实例中收集到的统计信息。每行都表示了在过去7*24小时里每隔十分钟UNDO表空间的使用情况,事务量和查询长度等信息的统计快照。
UNDO表空间的使用情况会因事务量变化而变化,一般我们在计算时同时参考UNDO表空间的平均使用情况和峰值使用情况。
以下SQL语句用于计算过去7*24小时中UNDO表空间的平均使用量:
select ur undo_retention, dbs db_block_size, ((ur * (ups * dbs)) + (dbs * 24)) / 1024 / 1024 as “M_bytes” from (select value as ur from v$parameter where name = ‘undo_retention’), (select (sum(undoblks) / sum(((end_time – begin_time) * 86400))) ups from v$undostat), (select value as dbs from v$parameter where name = ‘db_block_size’) |
以下SQL语句则按峰值情况计算UNDO表空间所需空间:
select ur undo_retention, dbs db_block_size, ((ur * (ups * dbs)) + (dbs * 24)) / 1024 / 1024 as “M_bytes” from (select value as ur from v$parameter where name = ‘undo_retention’), (select (undoblks / ((end_time – begin_time) * 86400)) ups from v$undostat where undoblks in (select max(undoblks) from v$undostat)), (select value as dbs from v$parameter where name = ‘db_block_size’) |
需要注意因RAC情况下一般存在2个UNDO表空间,视乎实际情况分别在2个实例中执行以上查询。
一般来说为了尽可能维护日常业务的正常运行,我们建议按照峰值情况估算和分配UNDO表空间的大小,虽然这样存在存储空间上的浪费,但是可以避免UNDO表空间不足所带来的问题。
同时我们也可以使用DBA_UNDO_EXTENTS视图实时监控UNDO表空间的使用情况:
select sum(bytes / 1024 / 1024), status, tablespace_name from dba_undo_extents group by status, tablespace_name; |
该查询将返回以STATUS分组的各状态回滚信息所使用的空间量,一般存在三种STATUS状态:EXPIRED,UNEXPIRED,ACTIVE。ACTIVE表示目前仍活跃的事务相关回滚信息,UNEXPIRED表示虽然事务已经结束但回滚信息保留的时间仍未超过实例参数UNDO_RETENTION所设定的值,EXPIRED表示回滚信息保留时间已超过UNDO_RETENTION所设定的值。
在UNDO表空间未启用guarantee选项的情况下(当前使用情况),新事务的回滚空间分配遵循以下依据:
a) 寻找不存在ACTIVE区间的回滚段,若没有则创建一个新的回滚段,若空间不允许生成新段,则返回错误。
b) 如果有一个回滚段被选中,但是其中空闲的空间并不足以存储该事务的回滚信息,那么它将尝试创建区间,如果表空间上没有空间,那么将会进入下一步。
c) 如果创建新区间失败,它将会搜索其他回滚段中的EXPIRED区间并重用。
d) 如果其他回滚段中没有EXPIRED区间可使用,那么它会继续搜索其他回滚段中UNEXPIRED区间并重用,注意事务不会重用本回滚段中的UNEXPIRED区间,故UNEXPIRED的回滚空间仅部分可以为Oracle重用;若仍得不到所需则返回错误。
当我们观察到ACTIVE回滚信息所占用空间很大时,说明系统目前运行的事务繁忙。因目前未启用UNDO表空间的guarantee选项,故EXPIRED的全部回滚空间与UNEXPIRED的部分回滚空间可以为Oracle复用,在实时监控时主要观察ACTIVE状态回滚信息使用的空间即可。
在系统相关业务不变的情况下,我们通过计算UNDO表空间的峰值使用情况即可最大程度完善UNDO表空间的配置;而当系统处于业务调整阶段,如新的业务加入或业务时段调整情况下,则需要进一步实时监控UNDO表空间使用情况,以满足动态调整需求。
undo自动调优介绍
Oracle 10gr2的后续版本中添加了撤销(UNDO)信息最短保留时间段自动调优的特性,不再仅仅依据参数UNDO_RETENTION的设定,其调优原则如下:
l 当撤销表空间(UNDO TABLESPACE)大小固定,Oracle将根据表空间的大小和实际的系统负载动态调整撤销信息保存时间,该最短保存时间的具体长短基于撤销表空间大小的一定比例值公式换算后获得;它总是比设定的UNDO_RETENTION大,当撤销表空间大量空闲情况下可能远远大于UNDO_RETENTION。
l 当撤销表空间设定为自动扩展空间情况下,Oracle将动态调整撤销信息最短保留时间为该时段最长查询时间(MAXQUERYLEN)加上300秒或参数UNDO_RETENTION间的较大者,即MAX((MAXQUERYLEN+300),UNDO_RENTION);同样的,该最短保存时间可能远远大于设定的UNDO_RETENTION。
在自动调整情况下,实际的撤销信息最短保留时间可以通过查询V$UNDOSTAT视图上的TUNED_UNDORETENTION列获得。
在无法就撤销表空间做相应修改的情况,我们可以通过修改隐式参数” _UNDO_AUTOTUNE”为FALSE关闭该自动调优特性。以上设定生效后,V$UNDOSTAT视图上TUNED_UNDORETENTION列不再更新,且撤销信息最短保留时间固定为参数UNDO_RETENTION的设定值。该参数可以不用重启数据库而动态设置生效。
Oracle Supplemental 补全日志介绍
Oracle补全日志(Supplemental logging)特性因其作用的不同可分为以下几种:最小(Minimal),支持所有字段(all),支持主键(primary key),支持唯一键(unique),支持外键(foreign key)。包括LONG,LOB,LONG RAW及集合等字段类型均无法利用补全日志。
最小(Minimal)补全日志开启后可以使得logmnr工具支持链式行,簇表和索引组织表。可以通过以下SQL检查最小补全日志是否已经开启:
SELECT supplemental_log_data_min FROM v$database; |
若结果返回YES或IMPLICIT则说明已开启最小补全日志,当使用ALL,PRIMARY,UNIQUE或FOREIGN补全日志时最小补全日志默认开启(即检查结果为IMPLICIT)。
一般情况下我们在使用逻辑备库时启用主键和惟一键的补全日志,而有时表上可能没有主键,惟一键或唯一索引;我们通过以下实验总结这种情况下Oracle的表现。
首先建立相关的测试表:
alter database add supplemental log data (primary key,unique index) columns ;
create table test (t1 int , t2 int ,t3 int ,t4 int ); alter table test add constraint pk_t1 primary key (t1); –添加主键 随后使用循环插入一定量的数据 update test set t2=10; commit; — 更新数据 |
使用LOGMNR工具分析之前的操作,可以看到REDO中记录的SQL形式如下:
update “SYS”.”TEST” set “T2” = ’10’ where “T1” = ’64’ and “T2” = ’65’ and ROWID = ‘AAAMiSAABAAAOhiAA/’; |
其中where字句后分别记录了主键值,被修改字段的值和原行的ROWID。
现在我们将原表上的主键去掉来观察。
alter table test drop constraint pk_t1 ;
update test set t2=11; commit; — 更新数据 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update “SYS”.”TEST” set “T2” = ’11’ where “T1” = ‘1’ and “T2” = ’10’ and “T3” = ‘3’ and “T4” = ‘4’ and ROWID = ‘AAAMiSAABAAAOhiAAA’; |
当没有主键的情况下,where子句后记录了所有列值和ROWID。
以下实验在存在唯一索引情况下的表现
create unique index pk_t1 on test(t1); update test set t2=15; commit; 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update “SYS”.”TEST” set “T2” = ’15’ where “T1” = ‘9’ and “T2” = ’11’ and “T3” = ’11’ and “T4” = ’12’ and ROWID = ‘AAAMiSAABAAAOhiAAI’; 以上是t1列有唯一索引但不限定not null的情况,下面我们加上not null限制 alter table test modify t1 not null; update test set t2=21; commit; 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update “SYS”.”TEST” set “T2” = ’21’ where “T1” = ‘2’ and “T2” = ’15’ and ROWID = ‘AAAMiSAABAAAOhiAAB’; |
如以上SQL所示,在存在唯一索引的情况下where子句后仍记录了所有列和ROWID;在存在唯一索引和非空约束的情况下表现与存在主键的情况一致。
当某个表上的列数量较多时且没有主键或唯一索引和非空约束的情况下,开启补全日志可能导致重做日志总量大幅提高。
首先建立一个存在250列的表:
Drop table test; create table test ( t1 varchar2(5), t2 varchar2(5), t3 varchar2(5), t4 varchar2(5), …t250 varchar2(5))
insert into test values (‘TEST’,’TEST’ ……); commit; –将255个列填入数据 alter database drop supplemental log data (primary key,unique index) columns; –关闭补全日志 set autotrace on; update test set t2=’BZZZZ’ where t1=’TEST’; commit; 可以从自动跟踪信息中看到,本条更新产生了516的重做量。 alter database add supplemental log data (primary key,unique index) columns; –重新开启补全日志 update test set t2=’FSDSD’ where t1=’TEST’; 跟踪信息显示产生了3044的重做量。 |
补全日志因作用域的不同又可分为数据库级的和表级的。表级补全日志又可以分为有条件的和无条件的。有条件限制的表级补全日志仅在特定列被更新时才会起作用,有条件限制的表级补全日志较少使用,这里我们不做讨论。
下面我们来观察无条件限制表级补全日志的具体表现:
alter database drop supplemental log data (primary key,unique index) columns;
alter table test add supplemental log data (primary key,unique index) columns; update test set t2=’ZZZZZ’; commit; 使用LOGMNR工具查看redo中的SQL: 可以发现where子句之后包含了所有列值。 delete test; commit; 使用LOGMNR工具查看redo中的SQL: delete from “SYS”.”TEST” where “T1” = ‘TEST’ and “T2” = ‘ZZZZZ’ and “T3” = ‘TEST’ and “T4” = ‘TEST’ and “T5” …… delete操作同样在where子句之后包含了所有列值。 又我们可以针对表上字段建立特定的补全日志组,以减少where子句后列值的出现。 alter table test drop supplemental log data (primary key,unique index) columns; –关闭表上原先的补全日志 alter table test add supplemental log group test_lgp (t1 ,t2,t3,t4,t5,t6,t12,t250) always; –创建补全日志组 update test set t2=’XXXXX’ ; commit; 使用LOGMNR工具查看redo中的SQL: update “SYS”.”TEST” set “T2” = ‘XXXXX’ where “T1” = ‘TEST’ and “T2” = ‘TEST’ and “T3” = ‘TEST’ and “T4” = ‘TEST’ and “T5” = ‘TEST’ and “T6” = ‘TEST’ and “T12” = ‘TEST’ and “T250” = ‘TEST’ and ROWID = ‘AAAMieAABAAAOhnAAA’; 如上所示重做日志中正确地显示了UPDATE操作中用户指定的字段值。 delete test; 使用LOGMNR工具查看redo中的SQL: delete from “SYS”.”TEST” where “T1” = ‘TEST’ and “T2” = ‘XXXXX’ and “T3” = ‘TEST’ …… delete操作在重做日志中仍然保留了所有列值。 |
针对字段较多的表,我们在能够以多个列保证数据唯一性且非空的情况下(即应用概念上的主键)来指定表上的补全日志组,以减少update操作时所产生的重做日志,而对于delete操作则无法有效改善。