love Oracle

记录Oracle点滴 mail: oracledb2#139.com

Oracle 11g 新特性之 Adaptive Cursor Sharing

Posted by admin on 三月 17th, 2013

Oracle 11g 之前版本的数据库中,某些数据分布不均匀的查询会由于绑定变量窥视的问题,导致优化器选择并非最优的执行计划。11g 中引入自适应游标共享的新概念,可以很好的解决这个问题。

下面通过一个具体的例子,详细的介绍这个新特性。

create table acs_test (col1 varchar2(1),col2 varchar2(1),col3 varchar2(1));

insert into acs_test values ('A','A','X');

insert into acs_test values ('B','B','X');

 

begin

  for i in 1..20 loop

    insert into acs_test  values ('A','C','X');

  end loop;

end;

/

 

begin

  for i in 1..20 loop

    insert into acs_test  values ('B','D','X');

  end loop;

end;

/

 

begin

  for i in 1..958 loop

    insert into acs_test  values ('C','D','X');

  end loop;

end;

/

 

create index acs_test_idx on acs_test(col1,col2);

 

execute dbms_stats.gather_table_stats(NULL,'acs_test',method_opt=>'FOR ALL COLUMNS SIZE 254');

此时,acs_test 表数据分布如下:

SQL> select col1, count(*) from acs_test group by col1 order by col1;

 

C   COUNT(*)

- ———-

A  21

B  21

C 958

 

SQL> select col2,count(*) from acs_test group by col2 order by col2;

 

C   COUNT(*)

- ———-

A   1

B   1

C  20

D 978

各列的选择度分别如下:

COL1

 

A -> 21/1000 = 0.021

B -> 21/1000 = 0.021

C -> 958/1000 = 0.958

 

COL2

 

A -> 1/1000 = 0.001

B -> 1/1000= 0.001

C -> 20/1000 = 0.020

D -> 978/1000 = 0.978

首先我们以 col1='A' 和 col2='A' 为条件,进行测试。

SQL> var b1 varchar2(128);

SQL> var b2 varchar2(128);

 

SQL> exec :b1 := 'A';

 

SQL> exec :b2 := 'A';

 

PL/SQL procedure successfully completed.

 

SQL> select * from acs_test where col1=:B1 and col2=:B2;

 

C C C

- – -

A A X

 

SQL> select child_number, executions, buffer_gets, is_bind_sensitive bs, is_bind_aware ba,plan_hash_value

     from v$sql

     where sql_id = '19sxt3v07nzm4';

 

CHILD_NUMBER EXECUTIONS BUFFER_GETS B B PLAN_HASH_VALUE

———— ———- ———– – - —————

        0        1       65 Y N      2647430641

 

SQL> select * from v$sql_cs_statistics where sql_id='19sxt3v07nzm4';

 

ADDRESS  HASH_VALUE SQL_ID      CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS   CPU_TIME

——– ———- ————- ———— ——————- – ———- ————– ———– ———-

2C6AD734 3229253220 19sxt3v07nzm4           0      1498426793 Y        1           2      65        0

从上述的输出可以看出,该查询产生了一次硬解析,我们在测试中使用了绑定变量,因此 is_bind_sensitive 值为 Y。但是 is_bind_aware 值为 N。因为 SQL 才执行了一次,数据库中不存在可以与之想比较的谓词选择度。

接下来再以 col1='C' 和 col2='D' 为条件,进行测试。

SQL> exec :b1 := 'C';

 

SQL> exec :b2 := 'D';

 

PL/SQL procedure successfully completed.

 

SQL> select * from acs_test where col1=:B1 and col2=:B2;

由于该语句是首次执行,bind_aware 未生效,仍然使用的之前产生的执行计划,执行次数加一,buffer_gets 数量从之前的 65 上升到 198。

SQL> select child_number, executions, buffer_gets, is_bind_sensitive bs, is_bind_aware ba,plan_hash_value

     from v$sql

     where sql_id = '19sxt3v07nzm4';

 

CHILD_NUMBER EXECUTIONS BUFFER_GETS B B PLAN_HASH_VALUE

———— ———- ———– – - —————

        0        2      198 Y N      2647430641

再次执行后,情况发生了变化,bind_aware 生效,生成了新的子执行计划。

CHILD_NUMBER EXECUTIONS BUFFER_GETS B B PLAN_HASH_VALUE

———— ———- ———– – - —————

        0        2      198 Y N      2647430641

        1        1      133 Y Y      2647430641

 

SQL> select * from v$sql_cs_statistics where sql_id='19sxt3v07nzm4';

 

ADDRESS  HASH_VALUE SQL_ID      CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS   CPU_TIME

——– ———- ————- ———— ——————- – ———- ————– ———– ———-

2C6AD734 3229253220 19sxt3v07nzm4           1      3075073894 Y        1        1916       133        0

2C6AD734 3229253220 19sxt3v07nzm4           0      1498426793 Y        1           2      65        0

bind_aware 生效后,v$sql_cs_selectivity 视图也能够查询到相关的谓词统计信息。

SQL> select child_number,predicate,range_id,low,high

     from v$sql_cs_selectivity

     where sql_id = '19sxt3v07nzm4';

 

CHILD_NUMBER PREDICATE   RANGE_ID       LOW      HIGH

———— ———– ———- ———- ———-

        1 =B1          0   0.861750   1.053250

        1 =B2          0   0.879750   1.075250

再以 col1='A' 和 col2='A' 为条件,进行测试。

SQL> exec :b1 := 'A';

 

SQL> exec :b2 := 'A';

 

PL/SQL procedure successfully completed.

 

SQL> select * from acs_test where col1=:B1 and col2=:B2;

这次又产生了一个新的子执行计划,由于此时的游标处于 bind aware 状态,通过计算绑定变量选择度的取值范围来确定是否能够和 child 1 共享游标。

SQL> select child_number, executions, buffer_gets, is_bind_sensitive bs, is_bind_aware ba,plan_hash_value

     from v$sql

     where sql_id = '19sxt3v07nzm4';  2    3

 

CHILD_NUMBER EXECUTIONS BUFFER_GETS B B PLAN_HASH_VALUE

———— ———- ———– – - —————

       0          2      198 Y N      2647430641

       1          1      133 Y Y      2647430641

       2          1        4 Y Y      2647430641

当查询条件 col1='A' 和 col2='A' 时,其选择度分别为 COL1 A -> 21/1000 = 0.021 和 COL2 A -> 1/1000 = 0.001,上下各浮动 10%,取值分别为:

                                       child1 low child1 high

col1(a)  0.0189 0.0231     0.861750     1.053250

col2(a)  0.0009 0.0011   0.879750     1.075250

可以看出,选择度并不在 child1 的取值范围区间,因此产生了新的执行计划。

SQL> select child_number,predicate,range_id,low,high

     from v$sql_cs_selectivity

     where sql_id = '19sxt3v07nzm4'

     order by 1,3,2;

 

CHILD_NUMBER PREDICATE   RANGE_ID       LOW      HIGH

———— ————- ———- ———- ———-

        1 =B1          0   0.861750   1.053250

        1 =B2          0   0.879750   1.075250

        2 =B1          0   0.018900   0.023100

        2 =B2          0   0.000900   0.001100

        2 =B1          1   0.861750   1.053250

        2 =B2          1   0.879750   1.075250

比较意外的是,child2 生成了两个 range,而 range1 的数据同 child1 的 range0 完全相同。其实从上面的执行计划 HASH 值可以看出,虽然产生了三个不同版本的子游标,但是其执行计划都是一样的 2647430641,所以 Oracle 在 child2 的子游标中加了两个范围,这样不论是 col1='A' 和 col2='A' 还是 col1='C' 和 col2='D',都使用 child2 而不再使用其他的游标。

CHILD_NUMBER EXECUTIONS BUFFER_GETS B B PLAN_HASH_VALUE

———— ———- ———– – - —————

        0        2      198 Y N      2647430641

        1        1      133 Y Y      2647430641

        2        4      274 Y Y      2647430641

Tags: , ,
Posted in 概念解析 | No Comments »

Linux平台利用软raid迁移数据

Posted by admin on 三月 10th, 2013

 

操作系统级别的数据迁移在生产环境中经常会用到,可以采用的技术方案也很多。这里介绍一种Linux平台下,通过软raid的方式进行数据迁移的方案。

1. 创建目标逻辑卷

[root]# pvcreate /dev/sdh

  Physical volume "/dev/sdh" successfully created

 

[root]# vgcreate vgdata1 /dev/sdh

  Volume group "vgdata1" successfully created

 

[root]# lvcreate -L 2700g vgdata1 -n oradatalv1

  Logical volume "oradatalv1" created

2. 关闭数据库

[root@qzdb ~]# su – oracle

[qzdb@/home/oracle]$sqlplus '/as sysdba'

 

SQL*Plus: Release 11.2.0.3.0 Production on Tue Feb 26 20:16:30 2013

 

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

 

 

Connected to:

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 – 64bit Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options

 

SQL> alter system switch logfile;

 

System altered.

 

SQL> shutdown immediate;

Database closed.

Database dismounted.

ORACLE instance shut down.

2. 创建raid0,加入需同步的源和目标设备

[root]# df -k

Filesystem           1K-blocks      Used Available Use% Mounted on

/dev/mapper/VolGroup00-LogVol00

                      10321208   3129740   6667180  32% /

/dev/sda1               101086     17328     78539  19% /boot

none                   4074600         0   4074600   0% /dev/shm

/dev/mapper/VolGroup00-LogVol05

                      30963708  25055140   4335704  86% /oracle

/dev/mapper/VolGroup00-LogVol04

                      10321208     55904   9741016   1% /tmp

/dev/mapper/VolGroup00-LogVol02

                      30963708   7520388  21870456  26% /usr

/dev/mapper/VolGroup00-LogVol03

                      20642428   3530860  16062992  19% /var

/dev/mapper/vgarch-archlv

                     666751664 352254308 280628352  56% /arch

/dev/mapper/vgdata-oradatalv

                     2683518368 2165217288 381986200  86% /oradata

[root]# umount /dev/mapper/vgdata-oradatalv

[root]# mdadm –create /dev/md0 –level=1 –raid-devices=2 /dev/mapper/vgdata-oradatalv /dev/mapper/vgdata1-oradatalv1

mdadm: /dev/mapper/vgdata-oradatalv appears to contain an ext2fs file system

    size=-1568669696K  mtime=Mon Feb  4 22:29:56 2013

mdadm: largest drive (/dev/mapper/vgdata1-oradatalv1) exceed size (2726297536K) by more than 1%

Continue creating array? y

mdadm: array /dev/md0 started.

raid创建后,可以通过下列命令查看同步状态

[root]# cat /proc/mdstat

Personalities : [raid1] 

md0 : active raid1 dm-8[1] dm-7[0]

      2726297536 blocks [2/2] [UU]

      [>....................]  resync =  0.1% (4142720/2726297536) finish=219.0min speed=207136K/sec

unused devices: <none>

也可以将/dev/md0设备挂载起来,继续为应用系统提供服务

mount /dev/md0 /oradata

3. 同步完成后,指定源设备为fault状态,并删除源设备

[root@qzdb ~]# mdadm –manage –set-faulty /dev/md0 /dev/mapper/vgdata-oradatalv

mdadm: set /dev/mapper/vgdata-oradatalv faulty in /dev/md0

[root@qzdb ~]# mdadm /dev/md0 -r /dev/mapper/vgdata-oradatalv

mdadm: hot removed /dev/mapper/vgdata-oradatalv

4. 卸载/dev/md0设备,重新挂载

[root@qzdb ~]# mdadm –stop /dev/md0

[root@qzdb ~]# 

[root@qzdb ~]# mount /dev/mapper/vgdata1-oradatalv1 /oradata

[root@qzdb ~]# df -k

Filesystem           1K-blocks      Used Available Use% Mounted on

/dev/mapper/VolGroup00-LogVol00

                      10321208   3129744   6667176  32% /

/dev/sda1               101086     17328     78539  19% /boot

none                   4074600         0   4074600   0% /dev/shm

/dev/mapper/VolGroup00-LogVol05

                      30963708  25065184   4325660  86% /oracle

/dev/mapper/VolGroup00-LogVol04

                      10321208     55900   9741020   1% /tmp

/dev/mapper/VolGroup00-LogVol02

                      30963708   7520388  21870456  26% /usr

/dev/mapper/VolGroup00-LogVol03

                      20642428   3531892  16061960  19% /var

/dev/mapper/vgarch-archlv

                     666751664 352258008 280624652  56% /arch

/dev/mapper/vgdata1-oradatalv1

                     2683518368 2165217288 381986200  86% /oradata

5. 删除整个raid

mdadm –stop /dev/md0

mdadm –remove /dev/md0

该方案通过Linux平台下的软raid方式,将数据从/dev/mapper/vgdata-oradatalv迁移到/dev/mapper/vgdata1-oradatalv1,理想状态下,只在创建raid(第二步)和重新挂载新设备(第五步)影响业务系统的使用,停机时间非常短。

当然,在没有业务系统更新的时候,同步速率会快很多。我在真实的生产环境中测试,Oracle 11.2.0.3的数据库dataguard备库,数据库完全关闭的情况下,同步速率可达70m/s,数据库mount状态大概55-65m/s,在日志同步时,速率最低会降到5m/s左右,影响还是很大的。

6. 其他相关操作

mdadm /dev/md0 –fail /dev/mapper/t0-lv0 –remove /dev/mapper/t0-lv0

mdadm /dev/md0 –fail /dev/mapper/t1-lv1 –remove /dev/mapper/t1-lv1

 

mdadm –misc –zero-superblock /dev/mapper/t0-lv0

mdadm –misc –zero-superblock /dev/mapper/t1-lv1

 

 

[root@qzdb ~]# mdadm –detail /dev/md0

/dev/md0:

        Version : 00.90.01

  Creation Time : Tue Feb 26 20:24:00 2013

     Raid Level : raid1

     Array Size : 2726297536 (2600.00 GiB 2791.73 GB)

    Device Size : -1568669760

   Raid Devices : 2

  Total Devices : 2

Preferred Minor : 0

    Persistence : Superblock is persistent

 

    Update Time : Wed Feb 27 00:53:59 2013

          State : clean

 Active Devices : 2

Working Devices : 2

 Failed Devices : 0

  Spare Devices : 0

 

           UUID : 273091c3:abf9ba28:f92fd7c1:d56664ea

         Events : 0.2

 

    Number   Major   Minor   RaidDevice State

       0     253        7        0      active sync   /dev/dm-7

       1     253        8        1      active sync   /dev/dm-8

 

Tags: ,
Posted in 备份恢复, 操作系统 | No Comments »

AIX 虚拟内存分页替换算法

Posted by admin on 二月 5th, 2013

内存管理是操作系统的核心任务之一,作为操作系统内核的一部分,为操作系统内核和各执行程序组织、分配和回收内存,跟踪当前的内存使用状况。内存管理和操作系统内核结合非常紧密,因此每个操作系统都有自己独特的内存管理方式。

AIX 操作系统内存管理器(AIX VMM)是一种基于分页的虚拟内存管理器,一个分页就是一个固定大小的数据块,既可以位于内存中(即映射到物理内存中的某个位置)、也可以位于磁盘中(即从物理内存中替换到分页空间或者文件系统)。

AIX 根据需要将分页映射到实际内存。如果应用程序引用了某个分页,而该分页并没有映射到实际内存中,那么系统将产生一个缺页。为了解决缺页(page fault),AIX内核会将所引用的分页加载到实际内存中的某个位置。如果所引用的分页是一个新的分页(也就是说,位于先前从未引用过的进程数据堆中的分页),那么“加载”所引用的分页只需要用零来填充一个实际内存位置(也就是说,提供一个填满零的分页)。如果所引用的分页是一个预先存在的分页(也就是说,文件中的某个分页、或者先前换出的某个分页),那么加载所引用的分页需要从磁盘(分页空间或者磁盘文件系统)中将该分页读入到实际内存中的某个位置。

随着系统将更多的分页添加到实际内存中,实际内存中空闲位置(可以包含分页)的数目将会减少。也可以将空闲位置的数目称为空闲分页框架的数目。当空闲分页框架的数目达到某个较低的值时,AIX 内核就必须清空实际内存中的某些位置,以便重用新的分页。这个过程也称为分页替换。AIX 其中一个后台守护进程 lrud 负责在内存分页中进行扫描,并回收某些分页以便于为实际内存腾出空间。

分页替换守护进程可以根据系统内存的使用情况和可调参数,选择不同类型的分页进行回收。

分页类型分为工作存储分页和永久存储分页。工作存储分页包括程序运行时的数据,如进程数据、堆栈信息、共享内存、内核数据等。永久存储分页则是一些包含永久数据的分页。简单来说,工作存储分页数据在系统重启后将不复存在,而永久存储分页重启后仍然存在。

为了帮助分页替换守护进程更好地选择用来进行替换的分页,AIX 将分页分为分为计算性分页和非计算性分页。计算性分页是一些用于文本、数据、堆栈和进程的共享内存的分页。非计算性分页是一些包含正在进行读取和写入的文件的文件数据的分页。

所有的工作存储分页都是计算性分页;永久存储分页既可以是计算性的、也可以是非计算性的。如果一个文件包含某个进程的可执行文本,那么系统会将该文件视为计算性的,并且将该文件中的所有永久存储分页都标记为计算性的。如果该文件不包含可执行文本,那么系统会将该文件视为非计算性的,并且将该文件中的所有永久存储分页都标记为非计算性的。

minperm 和 maxperm 是两个最基本的分页替换可调参数,用于指出 AIX 内核应该使用多少内存来缓存非计算性的分页。在缺省情况下,maxperm 是一个“非严格的”限制,这意味着在某些情况下可以超出这个限制。将 maxperm 设定为非严格的限制,这允许在具有可用空闲内存的时候,可以在内存中缓存更多的非计算性文件。通过将 strict_maxperm 可调参数设置为 1,就可以使 maxperm 限制成为“严格”的限制。当 maxperm 是严格限制的时候,即使有可供使用的空闲内存,内核也不允许非计算性分页的数目超出 maxperm 的限制。

非计算性分页的数目称为 numperm:vmstat –v 命令可以显示系统的 numperm 值所占系统实际内存的百分比。

root#/>vmstat -v

              1949696 memory pages

              1854872 lruable pages

                22427 free pages

                    2 memory pools

               661330 pinned pages

                 80.0 maxpin percentage

                  5.0 minperm percentage

                 15.0 maxperm percentage

                  0.2 numperm percentage

                 3952 file pages

                  0.0 compressed percentage

                    0 compressed pages

                  0.2 numclient percentage

                 15.0 maxclient percentage

                 3952 client pages

                    0 remote pageouts scheduled

                   53 pending disk I/Os blocked with no pbuf

              4341872 paging space I/Os blocked with no psbuf

                 2228 filesystem I/Os blocked with no fsbuf

                   25 client filesystem I/Os blocked with no fsbuf

                    0 external pager filesystem I/Os blocked with no fsbuf

                    0 Virtualized Partition Memory Page Faults

                 0.00 Time resolving virtualized partition memory page faults

分页替换算法

当 numperm >= maxperm ,AIX 分页替换守护进程严格地选择非计算性分页进行操作;

 

当 numperm <= minperm ,AIX 分页替换守护进程将选择计算性分页和非计算性分页进行操作;

 

当 minperm < numperm <= maxperm ,可替换的分页类型取决于 lru_file_repage 可调参数将控制:

 

当 lru_file_repage 可调参数设置为 1 时,AIX 内核可以选择计算性的和非计算性的分页进行分页替换;

当 lru_file_repage 可调参数设置为 0 时,AIX 内存只选择非计算性的分页进行分页替换。

在大多数的客户环境中,最理想的方式是始终让内核只选择非计算性的分页进行操作,因为与对非计算性的分页进行分页相比,对计算性的分页进行分页通常会对进程产生更大的性能开销。也是为什么 IBM 推荐将 lru_file_repage 参数设置为 0 的原因。

除了 minperm 和 maxperm 可调参数之外,还有一个 maxclient 可调参数。maxclient 可调参数指定应该用于缓存非计算性客户端分页的最大内存量的限制。非计算性客户端分页的数目称为 numclient,非计算性客户端分页是非计算性永久存储分页总数的子集。当 numclient 达到 maxclient 限制时,AIX 内核将采用特殊的、仅客户端的模式开始分页替换。在这种仅客户端的模式中,AIX 分页替换守护进程将严格地选择客户端分页进行操作。 

 

本文根据 《AIX 分页替换技术入门》整理。

Tags:
Posted in 操作系统 | No Comments »

通过脚本读取Goldengate discard文件

Posted by admin on 一月 13th, 2013

创建类

CREATE OR REPLACE TYPE DiscardRecord

AS OBJECT

(

    message_type          VARCHAR2(7)

  , message               VARCHAR2(120)

  , message_date          DATE

  , description           VARCHAR2(500)

  , line_number           NUMBER

  , oracle_error          VARCHAR2(10)

  , error_number          NUMBER(5)

  , source_object_owner   VARCHAR2(30)

  , source_object_name    VARCHAR2(30)

  , target_object_owner   VARCHAR2(30)

  , target_object_name    VARCHAR2(30)

  , error_operation       VARCHAR2(20)

  , error_object_owner    VARCHAR2(30)

  , error_object_name     VARCHAR2(30)

  , error_action          VARCHAR2(30)

  , error_column          VARCHAR2(120)

  , error_value           VARCHAR2(200)

  , pk_table_name         VARCHAR2(30)

  , operation_seqno       NUMBER

  , operation_rba         NUMBER

)

/

 

CREATE OR REPLACE TYPE DiscardTable

   AS TABLE OF DiscardRecord

/

创建目录

CREATE OR REPLACE DIRECTORY GGDiscard AS '/ggs/dirrpt';

创建函数

CREATE OR REPLACE FUNCTION read_discard (

      DiscardName   VARCHAR2

) RETURN DiscardTable PIPELINED IS

 

    FileHandle    UTL_FILE.FILE_TYPE;

 

    DiscardRec    DISCARDRECORD;

    DiscardExtra  DISCARDRECORD;

 

    FileBuffer    VARCHAR2(32767);

    PartLine      VARCHAR2(32767);

 

    OutputRow     BOOLEAN;

    ErrorRow      BOOLEAN;

    FindingColumn BOOLEAN;

 

    LineNumber    NUMBER := 0;

 

    /* Cursors */

 

    CURSOR get_cons_cols_curs (

          ConstraintOwner VARCHAR2

        , ConstraintName  VARCHAR2

    ) IS

        SELECT column_name

        FROM   dba_cons_columns

        WHERE  owner           = ConstraintOwner

        AND    constraint_name = ConstraintName

        AND    position        = 1;

 

    CURSOR get_pk_table_curs (

          ConstraintOwner VARCHAR2

        , ConstraintName  VARCHAR2

    ) IS

        SELECT pk.table_name

        FROM dba_constraints fk

           , dba_constraints pk

        WHERE pk.owner           = fk.r_owner

        AND   pk.constraint_name = fk.r_constraint_name

        AND   fk.owner           = ConstraintOwner

        AND   fk.constraint_name = ConstraintName;

 

    CURSOR get_pk_col_curs (

          TableOwner VARCHAR2

        , TableName  VARCHAR2

    ) IS

        SELECT col.column_name

        FROM   dba_cons_columns col

             , dba_constraints  pk

        WHERE  pk.owner           = TableOwner

        AND    pk.table_name      = TableName

        AND    pk.owner           = col.owner

        AND    pk.constraint_name = col.constraint_name

        AND    pk.constraint_type = 'P'

        AND    col.position       = 1;

 

    /* Procedures */

 

    PROCEDURE OpenDiscard

    IS

    BEGIN

        FileHandle := UTL_FILE.FOPEN('GGDISCARD',DiscardName||'.dsc','R');

    END OpenDiscard;

 

    PROCEDURE ReadDiscard

    IS

    BEGIN

        UTL_FILE.GET_LINE(FileHandle,FileBuffer);

 

        LineNumber := LineNumber + 1;

    END ReadDiscard;

 

    PROCEDURE CloseDiscard

    IS

    BEGIN

        UTL_FILE.FCLOSE(FileHandle);

    END CloseDiscard;

 

    PROCEDURE InitialiseRecord (

        Discard_INOUT IN OUT DISCARDRECORD

    ) IS

    BEGIN

        Discard_INOUT := DISCARDRECORD(

                 NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

                ,NULL

            );

    END InitialiseRecord;

 

BEGIN

    OpenDiscard;

 

    ErrorRow      := FALSE;

    FindingColumn := FALSE;

 

    InitialiseRecord(DiscardRec);

 

    LOOP

        ReadDiscard;

 

        WHILE FileBuffer IS NULL

        LOOP

                ReadDiscard;

        END LOOP;

 

        /* Read first 2 words */

 

        IF FileBuffer = '*'

        THEN

            PartLine := '*';

        ELSE

            PartLine := SUBSTR(FileBuffer,1,INSTR(FileBuffer,' ',2,2)-1);

        END IF;

 

        OutputRow := FALSE;

 

        CASE PartLine

            WHEN 'Oracle GoldenGate'

            THEN

                IF ErrorRow

                THEN

                    PIPE ROW (DiscardRec);

                    InitialiseRecord(DiscardRec);

                    ErrorRow := FALSE;

                END IF;

 

                DiscardRec.message_type := 'INFO';

                DiscardRec.message      :=  SUBSTR(FileBuffer,19,INSTR(FileBuffer,' ',19,1)-19)

                                           ||' Process '

                                           ||SUBSTR( FileBuffer

                                                    ,INSTR(FileBuffer,'for Oracle process')+19

                                                    ,INSTR(FileBuffer,',')-(INSTR(FileBuffer,'for Oracle process')+19));

                — DiscardRec.message      := 'Capture Process '||SUBSTR(FileBuffer,47,INSTR(FileBuffer,',')-47);

                DiscardRec.description  := SUBSTR( FileBuffer,INSTR(FileBuffer,',')+2

                                                  ,INSTR(FileBuffer,':') – (INSTR(FileBuffer,',')+2));

                DiscardRec.message_date := TO_DATE( SUBSTR(FileBuffer,INSTR(FileBuffer,':')+2)

                                                   ,'YYYY-MM-DD HH24:MI:SS');

                DiscardRec.line_number  := LineNumber;

 

                OutputRow := TRUE;

            WHEN 'Process Abending'

            THEN

                IF ErrorRow

                THEN

                    PIPE ROW (DiscardRec);

                    InitialiseRecord(DiscardRec);

                    ErrorRow := FALSE;

                END IF;

 

                DiscardRec.message_type := 'WARNING';

                DiscardRec.message      := PartLine;

                DiscardRec.message_date := TO_DATE(SUBSTR(FileBuffer,INSTR(FileBuffer,':')+2),'YYYY-MM-DD HH24:MI:SS');

                DiscardRec.line_number  := LineNumber;

 

                OutputRow := TRUE;

            WHEN 'Current time:'

            THEN

                IF ErrorRow

                THEN

                    PIPE ROW (DiscardRec);

                        InitialiseRecord(DiscardRec);

                END IF;

 

                DiscardRec.message_type := 'ERROR';

                DiscardRec.message_date := TO_DATE(SUBSTR(FileBuffer,INSTR(FileBuffer,':')+2),'YYYY-MM-DD HH24:MI:SS');

                DiscardRec.line_number  := LineNumber;

 

                ErrorRow  := TRUE;

                OutputRow := FALSE;

            WHEN ' Error text'

            THEN

                DiscardRec.oracle_error := SUBSTR(FileBuffer,INSTR(FileBuffer,', ')+2,INSTR(FileBuffer,':')-(INSTR(FileBuffer,', ')+2));

                DiscardRec.error_number := TO_NUMBER(SUBSTR(DiscardRec.oracle_error,INSTR(DiscardRec.oracle_error,'-')+1));

                DiscardRec.message      := SUBSTR(FileBuffer,INSTR(FileBuffer,':')+2,INSTR(FileBuffer,',',1,2)-(INSTR(FileBuffer,':')+2));

                DiscardRec.description  := SUBSTR(FileBuffer,INSTR(FileBuffer,'SQL '));

 

                OutputRow := FALSE;

            WHEN 'Operation failed'

            THEN

                DiscardRec.operation_seqno := SUBSTR( FileBuffer,INSTR(FileBuffer,'seqno ')+6

                                                     ,INSTR(FileBuffer,' rba ')-(INSTR(FileBuffer,'seqno ')+6));

                DiscardRec.operation_rba   := SUBSTR(FileBuffer,INSTR(FileBuffer,' rba ')+5);

 

                OutputRow := FALSE;

            WHEN 'OCI Error'

            THEN

                DiscardRec.oracle_error := SUBSTR(FileBuffer,INSTR(FileBuffer,' ',1,2)+1,INSTR(FileBuffer,':')-(INSTR(FileBuffer,' ',1,2)+1));

                DiscardRec.error_number := TO_NUMBER(SUBSTR(DiscardRec.oracle_error,INSTR(DiscardRec.oracle_error,'-')+1));

                DiscardRec.message      := SUBSTR(FileBuffer,INSTR(FileBuffer,':')+2,INSTR(FileBuffer,',')-(INSTR(FileBuffer,':')+2));

                DiscardRec.description  := SUBSTR(FileBuffer,INSTR(FileBuffer,'SQL '));

 

                CASE DiscardRec.error_number

                    WHEN 1 THEN

                        PartLine := SUBSTR( DiscardRec.message,INSTR(DiscardRec.message,'(')+1

                                           ,INSTR(DiscardRec.message,')')-(INSTR(DiscardRec.message,'(')+1));

                        DiscardRec.error_object_owner := SUBSTR(PartLine,1,INSTR(PartLine,'.')-1);

                        DiscardRec.error_object_name  := SUBSTR(PartLine,INSTR(PartLine,'.')+1);

                    WHEN 2291 THEN

                        PartLine := SUBSTR( DiscardRec.message,INSTR(DiscardRec.message,'(')+1

                                           ,INSTR(DiscardRec.message,')')-(INSTR(DiscardRec.message,'(')+1));

                        DiscardRec.error_object_owner := SUBSTR(PartLine,1,INSTR(PartLine,'.')-1);

                        DiscardRec.error_object_name  := SUBSTR(PartLine,INSTR(PartLine,'.')+1);

                    ELSE

                        NULL;

                END CASE;

 

                OutputRow := FALSE;

            WHEN 'Discarding record'

            THEN

                DiscardRec.error_action := SUBSTR( FileBuffer,INSTR(FileBuffer,' ',1,4)+1

                                                  ,INSTR(FileBuffer,' ',1,5)-(INSTR(FileBuffer,' ',1,4)+1));

                DiscardRec.error_number := TO_NUMBER(SUBSTR(FileBuffer,INSTR(FileBuffer,' ',-1)));

                DiscardRec.oracle_error := 'ORA-'||TO_CHAR(DiscardRec.error_number,'FM09999');

 

                OutputRow := FALSE;

            WHEN 'Problem replicating'

            THEN

                PartLine := SUBSTR( FileBuffer,INSTR(FileBuffer,' ',1,2)+1

                                   ,INSTR(FileBuffer,' to ')-(INSTR(FileBuffer,' ',1,2)+1));

                DiscardRec.source_object_owner := SUBSTR(PartLine,1,INSTR(PartLine,'.')-1);

                DiscardRec.source_object_name  := SUBSTR(PartLine,INSTR(PartLine,'.')+1);

 

                PartLine := SUBSTR(FileBuffer,INSTR(FileBuffer,' to ')+4);

 

                DiscardRec.target_object_owner := SUBSTR(PartLine,1,INSTR(PartLine,'.')-1);

                DiscardRec.target_object_name  := SUBSTR(PartLine,INSTR(PartLine,'.')+1);

 

                OutputRow := FALSE;

            WHEN 'Mapping problem'

            THEN

                DiscardRec.error_operation := UPPER(SUBSTR( FileBuffer,INSTR(FileBuffer,' ',-1,4)+1

                                                           ,INSTR(FileBuffer,' ',-1,3)-(INSTR(FileBuffer,' ',-1,4)+1)));

 

                OutputRow := FALSE;

                WHEN 'Record not'

                THEN

                DiscardRec.message := FileBuffer;

 

                OutputRow := FALSE;

            WHEN '*'

            THEN

                IF FindingColumn

                THEN

                    FindingColumn := FALSE;

                    OutputRow     := TRUE;

                ELSE

                    /* Gather some information for specific messages */

 

                    CASE DiscardRec.error_number

                        WHEN 1      /* Unique constraint violation */

                        THEN

                                /* Find the first column for the key and then be able to find it's value in the text */

 

                                OPEN get_cons_cols_curs(DiscardRec.error_object_owner,DiscardRec.error_object_name);

 

                                FETCH get_cons_cols_curs

                                INTO  DiscardRec.error_column;

 

                                CLOSE get_cons_cols_curs;

 

                        WHEN 1403   /* Row not found */

                        THEN

                                /* Find the PK of the table and then find it's value – if possible */

 

                                DiscardRec.error_object_owner := DiscardRec.source_object_owner;

                                DiscardRec.error_object_name  := DiscardRec.source_object_name;

 

                                OPEN get_pk_col_curs(DiscardRec.error_object_owner,DiscardRec.error_object_name);

 

                                FETCH get_pk_col_curs

                                INTO  DiscardRec.error_column;

 

                                CLOSE get_pk_col_curs;

 

                        WHEN 2291   /* Integrity constraint voilation */

                        THEN

                                /* Find the fk table name , column name and then be able to find the value of the offending row */

                                /* Get the column name */

 

                                OPEN get_cons_cols_curs(DiscardRec.error_object_owner,DiscardRec.error_object_name);

 

                                FETCH get_cons_cols_curs

                                INTO  DiscardRec.error_column;

 

                                CLOSE get_cons_cols_curs;

 

                                /* Get primary key table name */

 

                                OPEN get_pk_table_curs(DiscardRec.error_object_owner,DiscardRec.error_object_name);

 

                                FETCH get_pk_table_curs

                                INTO  DiscardRec.pk_table_name;

 

                                CLOSE get_pk_table_curs;

                        ELSE

                            NULL;

                    END CASE;

 

                    FindingColumn := TRUE;

                END IF;

            ELSE

                IF FindingColumn

                THEN

                    IF DiscardRec.error_column IS NOT NULL

                    THEN

                        IF DiscardRec.error_column = SUBSTR(FileBuffer,1,INSTR(FileBuffer,' = ')-1)

                        THEN

                            DiscardRec.error_value := SUBSTR(FileBuffer,INSTR(FileBuffer,' = ')+3);

                        END IF;

                    END IF;

                ELSE

                    /* Found something unusual so let's output an extra record */

 

                    InitialiseRecord(DiscardExtra);

                    DiscardExtra.message_type := 'UNKNOWN';

                    DiscardExtra.message      := SUBSTR(FileBuffer,1,120);

                    DiscardExtra.description  := SUBSTR(FileBuffer,120);

                    DiscardExtra.line_number  := LineNumber;

 

                    PIPE ROW (DiscardExtra);

                END IF;

        END CASE;

 

        IF OutputRow

        THEN

            PIPE ROW (DiscardRec);

            InitialiseRecord(DiscardRec);

                ErrorRow := FALSE;

        END IF;

    END LOOP;

EXCEPTION

    WHEN NO_DATA_FOUND THEN

        /* Output last row if not output */

 

        IF DiscardRec.message_type IS NOT NULL

        THEN

            PIPE ROW(DiscardRec);

        END IF;

 

        CloseDiscard;

END read_discard;

/

读取trail文件

select LINE_NUMBER

     , ORACLE_ERROR

     , SOURCE_OBJECT_NAME

     , ERROR_OPERATION

     , ERROR_COLUMN

     , ERROR_VALUE

     , PK_TABLE_NAME

from table(read_discard('resa01sb'));

Tags:
Posted in 备份恢复 | 1 Comment »

Oracle 11g 新特性之 Cardinality Feedback

Posted by admin on 九月 1st, 2012

Cardinality是Oracle优化器在计算代价时的重要指标,反应了SQL语句从数据表、结果集或者索引返回数据的多少,DBA在进行调优的时候可以通过Cardinality值分析SQL语句执行计划是否合理。通常,优化器使用统计信息计算Cardinality,但是Oracle统计信息不会实时更新,在频繁的DML操作或者因为各种原因导致统计信息和实际偏差较大时,会导致优化器计算出错误的值。

在Oracle 11.2之前,DBA通常在调优SQL语句时使用提示GATHER_PLAN_STATISTICS(或者设置 STATISTICS_LEVEL = ALL),对比执行计划中的A-ROWS和E-ROWS,以分析实际数据行数和优化器计算的数据行数是否存在较大偏差。想象一下,假如我们是要对一条运行时长超过24小时的SQL语句进行调优,这种方式可谓费时费力。

为了解决这类问题,Oracle 11g中对于SQL引擎架构进行了优化,通过执行结果的反馈,优化器能够确定编译阶段所做出的选择是否正确。主要通过以下的两点来实现:

  • 在SQL语句执行过程中,或者结束后更新更新相应的游标信息;
  • 查询优化过程中使用执行阶段收集的信息

通过这两个步骤建立了SQL编译器组件和执行组件的反馈循环,而这两点的实现则通过Oracle 11.2中引入的cardinality feedback新特性来实现。

Cardinality Feedback

Cardinality Feedback是Oracle 11gr2引入的新特性,SQL语句执行阶段比较相关对象的cardinality信息和实际信息,如果统计值和实际值偏差较大,执行阶段收集到的实际值将会存储起来。在该SQL语句再次执行时,优化器会利用新的值重新生成更优的执行计划。SQL语句第一次执行时,发现表的Cardinality同实际发生较大的偏差,在该语句再次执行时,发现之前产生的执行计划并非最优,因此没有进行SQL重用,而是重新生成了一个新的执行计划。

Cardinality Feedback受控于隐患参数"_OPTIMIZER_USE_FEEDBACK",默认值为true。在表缺少统计信息,或者统计信息不准确时,都会用到Cardinality Feedback,执行计划的Note部分会提示这一点;在11.2以后的V$SQL_SHARED_CURSOR中新增USE_FEEDBACK_STATS字段,用以标识是否使用Cardinality Feedback特性。

SQL> select * from table(dbms_xplan.display_cursor('fcb5u7n0r523m',null,null));

 

SQL_ID fcb5u7n0r523m, child number 1
————————————-
SELECT NVL(AVG((VALUE*1000.0)/DURATION),0),
NVL((((SUM(DURATION)/(60*1000))/60)*100),0) FROM
MGMT_SYSTEM_PERFORMANCE_LOG A, MGMT_PERFORMANCE_NAMES B WHERE :B1 =
B.DISPLAY_NAME AND A.JOB_NAME = B.JOB_NAME AND A.TIME >
(SYSDATE-(1/24)) AND A.IS_TOTAL='Y' AND A.DURATION > 0

 

Plan hash value: 300980605

 

————————————————————————————————————–
| Id  | Operation                     | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————————————————–
|   0 | SELECT STATEMENT              |                          |      |      |    3 (100)|          |
|   1 |  SORT AGGREGATE               |                          |    1 |   62 |          |          |
|   2 |   NESTED LOOPS               |                          |      |      |          |          |
|   3 |    NESTED LOOPS               |                          |    1 |   62 |    3   (0)| 00:00:01 |
|*  4 |     TABLE ACCESS BY INDEX ROWID| MGMT_SYSTEM_PERFORMANCE_LOG |    1 |   20 |    2   (0)| 00:00:01 |
|*  5 |      INDEX SKIP SCAN          | MGMT_SYSTEM_PERF_LOG_IDX_01 |    1 |      |    1   (0)| 00:00:01 |
|*  6 |     INDEX UNIQUE SCAN          | MGMT_PERFORMANCE_NAMES_PK   |    1 |      |    0   (0)|          |
|*  7 |    TABLE ACCESS BY INDEX ROWID | MGMT_PERFORMANCE_NAMES      |    1 |   42 |    1   (0)| 00:00:01 |
————————————————————————————————————–

 

Predicate Information (identified by operation id):
—————————————————

 

   4 – filter(("A"."DURATION">0 AND "A"."IS_TOTAL"='Y'))
   5 – access("A"."TIME">SYSDATE@!-.0416666666666666666666666666666666666667)
       filter("A"."TIME">SYSDATE@!-.0416666666666666666666666666666666666667)
   6 – access("A"."JOB_NAME"="B"."JOB_NAME")
   7 – filter("B"."DISPLAY_NAME"=:B1)

 

Note
—–
   – cardinality feedback used for this statement

 

36 rows selected.

Tags: ,
Posted in 概念解析 | No Comments »

Oracle 海量数据管理(一)

Posted by admin on 八月 22nd, 2012

随着数据的爆炸性增长,海量数据库也越来越多见。Oracle 在海量数据方面也推出了很多的新特性,比如在 Oracle 10g 中出现的 bigfile tablespace,数据文件的增大必然给管理带来更多的挑战,于是伴随着版本的不断更新,也出现了一些新的特性,用于优化海量数据的管理。

本文总结几个海量数据管理方面的新特性。

1. bigfile tablespace

bigfile tablespace自Oracle 10g引入,该类表空间只能包含一个数据文件,将普通表空间中用于表示数据文件的3个字节也用来标识数据块,因此能够支持的单文件大小比传统表空间大3个数量级,最大能够支持到4G个数据块的寻址空间。

2k    4294967295 * 2k    = 8 TB
4k    4294967295 * 4k    = 16 TB
8k    4294967295 * 8k    = 32 TB
16k   4294967295 * 16k   = 64 TB
32k   4294967295 * 32k   = 128 TB

2. dbv增强

为了便于对bigfile表空间超大数据文件的管理,从Oracle 10G开始,dbv能够支持块级验证,对于大的数据文件可以通过分块的方式,达到并行验证的目的。

[ora11g@oel6 ~]$ dbv start=1 end=200 file=+DAT/ora11g/datafile/system.277.791300349

DBVERIFY: Release 11.2.0.3.0 – Production on Wed Aug 22 08:10:48 2012

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

DBVERIFY – Verification starting : FILE = +DAT/ora11g/datafile/system.277.791300349

DBVERIFY – Verification complete

Total Pages Examined         : 200
Total Pages Processed (Data) : 37
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 8
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 149
Total Pages Processed (Seg)  : 1
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 6
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 0 (0.0)


[ora11g@oel6 ~]$ dbv userid=sys/oracle start=201 file=+DAT/ora11g/datafile/system.277.791300349

DBVERIFY: Release 11.2.0.3.0 – Production on Wed Aug 22 08:11:03 2012

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

DBVERIFY – Verification starting : FILE = +DAT/ora11g/datafile/system.277.791300349

DBVERIFY – Verification complete

Total Pages Examined         : 93240
Total Pages Processed (Data) : 61251
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 12949
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 3461
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 15579
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 0 (0.0)

3. 备份分段

在11g之前的官方文档中提到,一个数据文件只能包含在一个备份片中,这种说法在11g中已经不适用了。11g rman 支持将数据文件分段进行备份,这个特性能够显著提升大的数据文件备份效率。

RMAN> run {
   allocate channel d1 device type disk;
   allocate channel d2 device type disk;
   backup datafile 1 section size 500m;
   release channel d1;
   release channel d2;
   }

released channel: ORA_DISK_1
allocated channel: d1
channel d1: SID=55 device type=DISK

allocated channel: d2
channel d2: SID=57 device type=DISK

Starting backup at 21-AUG-12
channel d1: starting full datafile backup set
channel d1: specifying datafile(s) in backup set
input datafile file number=00001 name=+DAT/ora11g/datafile/system.277.791300349
backing up blocks 1 through 64000
……
channel d1: backup set complete, elapsed time: 00:00:01
Finished backup at 21-AUG-12

released channel: d1

released channel: d2

RMAN> list backup of datafile 1;

List of Backup Sets
===================

BS Key  Type LV Size       Device Type Elapsed Time Completion Time
——- —- — ———- ———– ———— —————
6       Full    617.83M    DISK        00:00:13     21-AUG-12     
  List of Datafiles in backup set 6
  File LV Type Ckp SCN    Ckp Time  Name
  —- — —- ———- ——— —-
  1       Full 1793856    21-AUG-12 +DAT/ora11g/datafile/system.277.791300349

  Backup Set Copy #1 of backup set 6
  Device Type Elapsed Time Completion Time Compressed Tag
  ———– ———— ————— ———- —
  DISK        00:00:13     21-AUG-12       NO         TAG20120821T085838

    List of Backup Pieces for backup set 6 Copy #1
    BP Key  Pc# Status      Piece Name
    ——- — ———– ———-
    6       1   AVAILABLE   +DAT/ora11g/backupset/2012_08_21/nnndf0_tag20120821t085838_0.281.791888319
    7       2   AVAILABLE   +DAT/ora11g/backupset/2012_08_21/nnndf0_tag20120821t085838_0.329.791888327

4. 分区表增强

Oracle 11g对分区表进行了增强,能够支持更多的分区表类型,分区表的管理更加便捷,功能也更加强大。

http://www.muzijiang.cn/index.php/2010/04/11g_new_feature_reference_partition/
http://www.muzijiang.cn/index.php/2010/04/11g_new_feature_partition_enhancement/
http://www.muzijiang.cn/index.php/2010/04/oracle11g_new_feature_interval_partition/
http://www.muzijiang.cn/index.php/2010/04/11g_new_feature_system_partition/

Tags: , ,
Posted in 概念解析 | No Comments »

ogg利用token定位事务

Posted by admin on 八月 17th, 2012

在前面的一篇故障处理博客中,介绍了使用 NODYNSQL 等参数在 Goldengate 日志中打印出 SQL 语句,从而帮助我们分析故障原因。但是这种方式适合单语句的分析,如果一个事务里包含多个 DML 操作则有些不方便。

这里再介绍一种通过 Goldengate Token 在 trail 文件中记录相应的事务 SCN,再通过 SCN 进行相应的分析。

源端设置 

extract ext1
userid ggs@STDBY, password ggs
rmthost redhat64, mgrport 1921
rmttrail  /u01/app/oracle/ggs/rt
table  rman.s, tokens (tk-scn = @getenv("oratransaction","scn")); 

在复制表上模拟一个事务。 

SQL> insert into s values ('a','1');

1 row created.

SQL> insert into s values ('b',2);

1 row created.

SQL> insert into s values ('c',3);

1 row created.

SQL> delete s where id=1;

1 row deleted.

SQL> commit;

Commit complete. 

当目标端出现问题时,可以通过 SCN 定位到相应的事务。 

Logdump 28 >open /u01/app/oracle/ggs/rt000019
Current LogTrail is /u01/app/oracle/ggs/rt000019
Logdump 29 >ghdr on
Logdump 30 >detail data
Logdump 31 >usertoken detail
Logdump 34 >n
___________________________________________________________________
Hdr-Ind    :     E  (x45)     Partition  :     .  (x04) 
UndoFlag   :     .  (x00)     BeforeAfter:     A  (x41) 
RecLength  :    18  (x0012)   IO Time    : 2012/08/17 19:34:33.000.047  
IOType     :     5  (x05)     OrigNode   :   255  (xff)
TransInd   :     .  (x00)     FormatType :     R  (x52)
SyskeyLen  :     0  (x00)     Incomplete :     .  (x00)
AuditRBA   :        119       AuditPos   : 534544
Continued  :     N  (x00)     RecCount   :     1  (x01)

2012/08/17 19:34:33.000.047 Insert               Len    18 RBA 1053
Name: RMAN.S
After  Image:                                             Partition 4   GU b  
 0000 0005 0000 0001 6100 0100 0500 0000 0131      | ……..a……..1 
Column     0 (x0000), Len     5 (x0005) 
 0000 0001 61                                      | ….a 
Column     1 (x0001), Len     5 (x0005) 
 0000 0001 31                                      | ….1 
 
User tokens:   15 bytes
tk-scn              : 2952727 

此处的 tk-scn 标识出事务的 SCN 号,我们可以通过这个 SCN 还原事务。 

SQL> select undo_sql from flashback_transaction_query where commit_scn=2952727;

 

UNDO_SQL

——————————————————————————–

insert into "RMAN"."S"("DUMMY","ID") values ('a','1');
delete from "RMAN"."S" where ROWID = 'AAAThDAAEAAABiFAAC';
delete from "RMAN"."S" where ROWID = 'AAAThDAAEAAABiFAAB';
delete from "RMAN"."S" where ROWID = 'AAAThDAAEAAABiFAAA'; 

不过要注意的是,这里借用的是 FLASHBACK_TRANSACTION_QUERY 视图,生成的语句是 UNDO 语句,因此实际事务是反过来的。
 

Tags:
Posted in 备份恢复, 概念解析 | No Comments »

Oracle Direct NFS

Posted by admin on 七月 1st, 2012

由于配置简单、成本低廉,新兴的集群NAS(Networked-Attached Storage)还能够提供DAS所不能提供的高可用性,因此NAS系统在企业中的使用越来越广泛。

NAS设备通常通过NFS协议同客户端进行连接,标准的NFS客户端由操作系统提供,没有提供Oracle数据库基于IO访问的优化。Oracle 11g数据库软件中集成了Direct NFS客户端,使得Oracle能够优化Oracle数据库和NFS服务器之间的I/O,提供比传统NFS更多的优势。

这里简单记录一下配置过程。

服务器端的配置同普通NFS配置基本相同,首先确认Oracle属主的GID和UID

[root@sf2 ~]# id oracle
uid=1101(oracle) gid=1000(oinstall) groups=1031(dba),1021(asmdba),1000(oinstall)

创建NFS输出目录

[root@sf2 ~]# mkdir /dnfs
[root@sf2 ~]# chown 1101:1000 /dnfs

添加NFS条目到/etc/exports

/dnfs *(rw,sync,all_squash,anonuid=1101,anongid=1000)

重启NFS服务,确认输出

[root@sf2 ~]# service nfs start
Starting NFS services:                                     [  OK  ]
Starting NFS quotas:                                       [  OK  ]
Starting NFS daemon:                                       [  OK  ]
Starting NFS mountd:                                       [  OK  ]

 

[root@sf2 ~]# showmount -e
Export list for sf2:
/dnfs *

 

[root@sf2 ~]# exportfs -v
/dnfs     <world>(rw,wdelay,root_squash,all_squash,no_subtree_check,anonuid=1101,anongid=1000)

客户端挂载NFS目录

[root@test ~]# mkdir /data1
[root@test ~]# chown oracle:dba /data1

[root@test ~]# mount -t nfs sf2:/dnfs /data1

[root@test ~]# mount
sf2:/dnfs on /data1 type nfs (rw,addr=172.30.12.201)

添加挂载条目到/etc/fstab

sf2:/dnfs /data1 nfs rw,bg,hard,nointr,rsize=32768,wsize=32768,tcp,actimeo=0,vers=3,timeo=600 0 0

操作系统配置完成,这部分配置和普通NFS配置没有什么区别。

开启数据库的Direct NFS支持

[oracle@test ~]$ cd $ORACLE_HOME/lib
[oracle@test ~]$ mv libodm11.so libodm11.so_bak
[oracle@test ~]$ ln -s libnfsodm11.so libodm11.so

Oracle 11gR2以后可以直接开启DNFS支持。

[oracle@test ~]$ make -f ins_rdbms.mk dnfs_on

重启数据库实例后,告警日志会记录相关的信息。

Oracle instance running with ODM: Oracle Direct NFS ODM Library Version 2.0

创建基于NFS的表空间

SQL> create tablespace dnfs datafile '/data1/dnfs01.dbf' size 10m;

Tablespace created.

 

SQL> create table dnfs (id number) tablespace dnfs;

Table created.

可以通过下列方式确认DNFS是否正常。

SQL> select * from v$dnfs_servers;

 ID SVRNAME  DIRNAME   MNTPORT    NFSPORT WTMAX    RTMAX
———- ——————– ——————– ———- ———- ———- ———-
  1 sf2   /dnfs      966       2049     0        0

 

[oracle@test trace]$ ps -ef|grep dbw
grid      3426     1  0 Jun28 ?        00:00:00 asm_dbw0_+ASM
oracle   17432 16958  0 13:56 pts/1    00:00:00 grep dbw
oracle   22001     1  0 04:03 ?        00:00:11 ora_dbw0_ORCL

 

[root@test ~]# lsof -p 22001

COMMAND   PID   USER   FD   TYPE     DEVICE      SIZE    NODE NAME
……
oracle  22001 oracle   30uW  REG       0,25  10493952 4378987 /data1/dnfs01.dbf (sf2:/dnfs)

DNFS和KNFS性能上的对比可以参考:

http://glennfawcett.wordpress.com/2009/12/14/direct-nfs-vs-kernel-nfs-bake-off-with-oracle-11g-and-solaris-and-the-winner-is/

此外,Oracle提供几个event 19392, 19394, 19396用于诊断DNFS的配置。

Tags: ,
Posted in 操作系统, 概念解析 | 1 Comment »

通过隐含参数查看视图底层定义

Posted by admin on 六月 9th, 2012

视图被广泛的应用于各类应用程序,以简化各类查询语句,增加查询语句的可读性。但是大量的引用视图,给SQL语句的调优带来不小的挑战。自Oracle 10gR2开始,提供了一个隐含参数,帮助我们可以方便的看到视图引用的底层表。

创建两张测试表,并基于这两张表创建视图。

SQL> create table t1 (id number);

 

Table created.

 

SQL> create view v1 as select * from t1;

 

View created.

 

SQL> create table t2 (id number, name varchar2(20), salary number);

 

Table created.

 

SQL> create view v2 as select id, salary from t2;

 

View created.

 

设置"_dump_qbc_tree"参数为1,执行查询。

SQL> alter session set "_dump_qbc_tree"=1;

 

Session altered.

 

SQL> select * from v1, v2 where v1.id=v2.id;

 

no rows selected

 

现在可以来看看后台产生的trace文件:

[root@delta21 udump]# more orcl_ora_3243.trc

 

/oracle/admin/orcl/udump/orcl_ora_3243.trc
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 – 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /oracle/product/10.2.0.4/db
System name: Linux
Node name: delta21
Release: 2.6.18-164.el5
Version: #1 SMP Tue Aug 18 15:51:48 EDT 2009
Machine: x86_64
Instance name: orcl
Redo thread mounted by this instance: 1
Oracle process number: 15
Unix process pid: 3243, image: oracle@delta21 (TNS V1-V3)

*** 2012-06-09 13:38:35.928
*** ACTION NAME:() 2012-06-09 13:38:35.927
*** MODULE NAME:(SQL*Plus) 2012-06-09 13:38:35.927
*** SERVICE NAME:(SYS$USERS) 2012-06-09 13:38:35.927
*** SESSION ID:(143.105) 2012-06-09 13:38:35.927
QCSDMP: ——————————————————-
QCSDMP:  SELECT: (qbc=0x2b93a2d1d9b0)
QCSDMP:    V1.ID
QCSDMP:    V2.ID
QCSDMP:    V2.SALARY
QCSDMP:  FROM:
QCSDMP:    .V2
QCSDMP:      VQB:
QCSDMP:        SELECT: (qbc=0x2b93a2d150e0)
QCSDMP:          .ID
QCSDMP:          .SALARY
QCSDMP:        FROM:
QCSDMP:          .T2
QCSDMP:    .V1
QCSDMP:      VQB:
QCSDMP:        SELECT: (qbc=0x2b93a2d14908)
QCSDMP:          .ID
QCSDMP:        FROM:
QCSDMP:          .T1

 

通过trace文件中可以看出,之前基于视图的查询语句还原成了基于表的SQL语句。通过查看视图的执行计划也能够验证这一点:

SQL> explain plan for
  2  select * from v1, v2 where v1.id=v2.id;

 

Explained.

 

SQL> select * from table(dbms_xplan.display);

 

PLAN_TABLE_OUTPUT
——————————————————————————–
Plan hash value: 1838229974

—————————————————————————
| Id  | Operation    | Name | Rows  | Bytes | Cost (%CPU)| Time   |
—————————————————————————
|   0 | SELECT STATEMENT   |   | 1 |    39 | 5  (20)| 00:00:01 |
|*  1 |  HASH JOIN    |   | 1 |    39 | 5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| T1   | 1 |    13 | 2   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T2   | 1 |    26 | 2   (0)| 00:00:01 |
—————————————————————————

PLAN_TABLE_OUTPUT
——————————————————————————–
Predicate Information (identified by operation id):
—————————————————

   1 – access("ID"="ID")

Note
—–
   – dynamic sampling used for this statement

 

19 rows selected.

 

类似的方法也可以用于查看数据库底层视图的定义。

Tags:
Posted in 概念解析 | No Comments »

你最后悔的是什么

Posted by admin on 六月 6th, 2012

比利时一家杂志,曾对全国60岁以上的老人做了一次问卷。

调查的题目是:你最后悔的是什么?并列出十几项生活中容易后悔的事情,供被调查者选择。

结果

……

第一名:75%的人后悔,年轻时努力不够,以至事业无成;
第二名:70%的人后悔,年轻时错误地选择了职业;
第三名:62%的人后悔,对子女教育不够或方法不当;
第四名:57%的人后悔,没有好好珍惜自己的伴侣;
第五名:49%的人后悔,锻炼身体不足;
只有11%的人后悔,没有赚到更多的钱。

——如果不想老来空怅惘,从现在起,你会选择怎么过?

Posted in 信笔涂鸦 | No Comments »