分区键与分区本地索引

Posted by 老熊 on 7月 13th, 2010

关于全局索引和本地索引的优缺点,分别应该在什么情况下使用,这方面的资料很多,本文不作讨论。本文讨论一种特殊情况,即建立在分区键之上的本地索引。9i也算是很老的Oracle版本了,只是很多系统包括很多大型的核心的系统都在用,因此本文介绍建立在分区键列上的本地索引存在的问题。下面是一些测试:

SQL> create table t1 ( a int, b varchar2(300)) partition by range(a)
  2  (
  3   partition p01 values less than (1000),
  4   partition p02 values less than (2000),
  5   partition p03 values less than (3000),
  6   partition p04 values less than (4000),
  7   partition p05 values less than (5000),
  8   partition p06 values less than (6000),
  9   partition p07 values less than (7000),
 10   partition p08 values less than (8000),
 11   partition p09 values less than (9000),
 12   partition p10 values less than (10000),
 13   partition p11 values less than (11000),
 14   partition p12 values less than (12000),
 15   partition p13 values less than (13000),
 16   partition p14 values less than (14000),
 17   partition p15 values less than (15000),
 18   partition p16 values less than (16000),
 19   partition p17 values less than (17000),
 20   partition p18 values less than (18000),
 21   partition p19 values less than (19000),
 22   partition p20 values less than (20000)
 23  )
 24  /

表已创建。

SQL> insert into t1 select rownum,lpad('x',200,'x') from dual connect by rownum<20000;

已创建19999行。

SQL> commit;

提交完成。

SQL> insert /*+ append */ into t1 select * from t1;

已创建19999行。

SQL> commit;

提交完成。

SQL> insert /*+ append */ into t1 select * from t1;

已创建39998行。

SQL> commit;

提交完成。

SQL> insert /*+ append */ into t1 select * from t1;

已创建79996行。

SQL> commit;

提交完成。

SQL> insert /*+ append */ into t1 select * from t1;

已创建159992行。

SQL> commit;

提交完成。

SQL> insert /*+ append */ into t1 select * from t1;

已创建319984行。

SQL> commit;

提交完成。

首先建立一个测试范围分区表,分区键列是”a”,共20个分区,在这个测试表中生成约64万行数据。下面在列a上建本地索引并收集统计信息:

SQL> create index t1_idx on t1(a) local;

索引已创建。

SQL> exec dbms_stats.gather_table_stats(user,'T1',
    method_opt=>'for all columns size 1',cascade=>true);

PL/SQL 过程已成功完成。
SQL> @sosi

Please enter Name of Table Owner (Null = TEST):
Please enter Table Name to show Statistics for: t1

***********
Table Level
***********

Table                   Number                 Empty
Name                   of Rows   Blocks       Blocks
--------------- -------------- -------- ------------
T1                     639,968   18,880            0

Column                    Column                       Distinct            Number       Number
Name                      Details                        Values   Density Buckets        Nulls
------------------------- ------------------------ ------------ --------- ------- ------------
A                         NUMBER(22)                     19,999   .000050       1            0
B                         VARCHAR2(300)                       1  1.000000       1            0

                              B
Index                      Tree     Leaf       Distinct         Number      Cluster
Name            Unique    Level     Blks           Keys        of Rows       Factor
--------------- --------- ----- -------- -------------- -------------- ------------
T1_IDX          NONUNIQUE     1    1,390         19,999        639,968      639,968

Index           Column                     Col Column
Name            Name                       Pos Details
--------------- ------------------------- ---- ------------------------
T1_IDX          A                            1 NUMBER(22)

***************
Partition Level
***************

  Part Partition               Number                 Empty
   Pos Name                   of Rows   Blocks       Blocks
------ --------------- -------------- -------- ------------
     1 P01                     31,968      944            0
     2 P02                     32,000      944            0
     3 P03                     32,000      944            0
     4 P04                     32,000      944            0
     5 P05                     32,000      944            0
     6 P06                     32,000      944            0
     7 P07                     32,000      944            0
     8 P08                     32,000      944            0
     9 P09                     32,000      944            0
    10 P10                     32,000      944            0
    11 P11                     32,000      944            0
    12 P12                     32,000      944            0
    13 P13                     32,000      944            0
    14 P14                     32,000      944            0
    15 P15                     32,000      944            0
    16 P16                     32,000      944            0
    17 P17                     32,000      944            0
    18 P18                     32,000      944            0
    19 P19                     32,000      944            0
    20 P20                     32,000      944            0
                                    B
Index           Partition        Tree     Leaf       Distinct         Number
Name            Name            Level     Blks           Keys        of Rows
--------------- --------------- ----- -------- -------------- --------------
T1_IDX          P01                 1       67            999         31,968
T1_IDX          P02                 1       67          1,000         32,000
T1_IDX          P03                 1       67          1,000         32,000
T1_IDX          P04                 1       67          1,000         32,000
T1_IDX          P05                 1       67          1,000         32,000
T1_IDX          P06                 1       67          1,000         32,000
T1_IDX          P07                 1       67          1,000         32,000
T1_IDX          P08                 1       67          1,000         32,000
T1_IDX          P09                 1       67          1,000         32,000
T1_IDX          P10                 1       67          1,000         32,000
T1_IDX          P11                 1       72          1,000         32,000
T1_IDX          P12                 1       72          1,000         32,000
T1_IDX          P13                 1       72          1,000         32,000
T1_IDX          P14                 1       72          1,000         32,000
T1_IDX          P15                 1       72          1,000         32,000
T1_IDX          P16                 1       72          1,000         32,000
T1_IDX          P17                 1       72          1,000         32,000
T1_IDX          P18                 1       72          1,000         32,000
T1_IDX          P19                 1       72          1,000         32,000
T1_IDX          P20                 1       72          1,000         32,000

下面执行查询:
Read the rest of this entry »

怎样保持Oracle数据库SQL性能的稳定性

Posted by 老熊 on 7月 7th, 2010

有客户遇到SQL性能不稳定,突然变差导致系统性能出现严重问题的情况。对于大型的系统来说,SQL性能不稳定,有时突然变差,这是常常遇到的问题。这也是一些DBA的挑战。

对于使用Oracle数据库的应用系统,有时会出现运行得好好的SQL,性能突然变差。特别是对于OLTP类型系统执行频繁的核心SQL,如果出现性能问题,通常会影响整个数据库的性能,进而影响整个系统的正常运行。对于个别的SQL,比如较少使用的查询报表之类的SQL,如果出现问题,通常只影响少部分功能模块,而不会影响整个系统。

那么应该怎么样保持SQL性能的稳定性?

SQL的性能变差,通常是在SQL语句重新进行了解析,解析时使用了错误的执行计划出现的。下列情况是SQL会重新解析的原因:

  • 1. SQL语句没有使用绑定变量,这样SQL每次执行都要解析。
  • 2. SQL长时间没有执行,被刷出SHARED POOL,再次执行时需要重新解析。
  • 3. 在SQL引用的对象(表、视图等)上执行了DDL操作,甚至是结构发生了变化,比如建了一个索引。
  • 4. 对SQL引用的对象进行了权限更改。
  • 5. 重新分析(收集统计信息)了SQL引用的表和索引,或者表和索引统计信息被删除。
  • 6. 修改了与性能相关的部分参数。
  • 7. 刷新了共享池。
  • 8. 当然重启数据库也会使所有SQL全部重新解析。

SQL重新解析后,跟以前相比,性能突然变差,通常是下列原因:

  • 1. 表和索引的优化统计信息被删除,或者重新收集后统计信息不准确。重新收集统计信息通常是由于收集策略(方法)不正确引起。比如对分区表使用analyze命令而不是用dbms_stats包、收集统计信息时采样比例过小等等。Oracle优化器严重依赖于统计信息,如果统计信息有问题,则很容易导致SQL不能使用正确的执行计划。
  • 2. SQL绑定变量窥探(bind peeking),同时绑定变量对应的列上有直方图;或者绑定变量的值变化范围过大、分区数据分布极不均匀:
    • 1) 绑定变量的列上有直方图:
      假如表orders存储所有的订单,state列有3种不同的值:0表示未处理,1表示处理成功完成,2表示处理失败。State列上有一个索引,表中绝大部分数据的state列为1,0和2占少数。有下面的SQL:

      select * from orders where state=:b1
      

      这里:b1是变量,在大多数情况下这个值为0,则应该使用索引,但是如果SQL被重新解析,而第一次执行时应用传给变量b1值为1,则不会使用索引,采用全表扫描的方式来访问表。对于绑定变量的SQL,只在第一次执行时才会进行绑定变量窥探,并以此确定执行计划,该SQL后续执行时全部按这个执行计划。这样在后续执行时,b1变量传入的值为0的时候,仍然是第一次执行时产生的执行计划,即使用的是全表扫描,这样会导致性能很差。

    • 2) 绑定变量的值变化范围过大:
      同样假如orders表有一列created_date表示一笔订单的下单时间,orders表里面存储了最近1年的数据,有如下的SQL:

      Select * from orders where created_date >=:b1;
      

      假如大多数情况下,应用传入的b1变量值为最近几天内的日期值,那么SQL使用的是created_date列上的索引,而如果b1变量值为5个月之前的一个值,那么就会使用全表扫描。与上面描述的直方图引起的问题一样,如果SQL第1次执行时传入的变量值引起的是全表扫描,那么将该SQL后续执行时都使用了全表扫描,从而影响了性能。

    • 3) 分区数据量不均匀:
      对于范围和列表分区,可能存在各个分区之间数据量极不均匀的情况下。比如分区表orders按地区area进行了分区,P1分区只有几千行,而P2分区有200万行数据。同时假如有一列product_id,其上有一个本地分区索引,有如下的SQL:

      select * from orders where area=:b1 and product_id =:b2
      

      这条SQL由于有area条件,因此会使用分区排除。如果第1 次执行时应用传给b1变量的值正好落在P1分区上,很可能导致SQL采用全表扫描访问,如前面所描述的,导致SQL后续执行时全部使用了全表扫描。

  • 3. 其他原因,比如表做了类似于MOVE操作之后,索引不可用,对索引进行了更改。当然这种情况是属于维护不当引起的问题,不在本文讨论的范围。

综上所述,SQL语句性能突然变差,主要是因为绑定变量和统计信息的原因。注意这里只讨论了突然变差的情况,而对于由于数据量和业务量的增加性能逐步变差的情况不讨论。
为保持SQL性能或者说是执行计划的稳定性,需要从以下几个方面着手:

  • 1. 规划好优化统计信息的收集策略。对于Oracle 10g来说,默认的策略能够满足大部分需求,但是默认的收集策略会过多地收集列上的直方图。由于绑定变量与直方图固有的矛盾,为保持性能稳定,对使用绑定变量的列,不收集列上的直方图;对的确需要收集直方图的列,在SQL中该列上的条件就不要用绑定变量。统计信息收集策略,可以考虑对大部分表,使用系统默认的收集策略,而对于有问题的,可以用DBMS_STATS.LOCK_STATS锁定表的统计信息,避免系统自动收集该表的统计信息,然后编写脚本来定制地收集表的统计信息。脚本中类似如下:

    exec dbms_stats.unlock_table_stats…
    exec dbms_stats.gather_table_stats…
    exec dbms_stats.lock_table_stats…
    
  • 2. 修改SQL语句,使用HINT,使SQL语句按HINT指定的执行计划进行执行。这需要修改应用,同时需要逐条SQL语句进行,加上测试和发布,时间较长,成本较高,风险也较大。
  • 3. 修改隐含参数” _optim_peek_user_binds”为FALSE,修改这个参数可能会引起性能问题(这里讨论的是稳定性问题)。
  • 4. 使用OUTLINE。对于曾经出现过执行计划突然变差的SQL语句,可以使用OUTLINE来加固其执行计划。在10g中DBMS_OUTLN.CREATE_OUTLINE可以根据已有的执行正常的SQL游标来创建OUTLINE。如果事先对所有频繁执行的核心SQL使用OUTLINE加固执行计划,将最大可能地避免SQL语句性能突然变差。
    注:DBMS_OUTLN可以通过$ORACLE_HOME/rdbms/admin/dbmsol.sql脚本来安装。
  • 5. 使用SQL Profile。SQL Profile是Oracle 10g之后的新功能,此处不再介绍,请参考相应的文档。

除此之外,可以调整一些参数避免潜在的问题,比如将”_btree_bitmap_plans”参数设置为FALSE(这个参数请参考互联网上的文章或Oracle文档)。

而在实际工作中,通过使用定制的统计信息收集策略,以及在部分系统上使用OUTLINE,系统基本上不会出现已有的SQL性能突然变差的情况。当然也有维护人员操作不当引起的SQL性能突然变差,比如建了某个索引而没有收集统计信息,导致SQL使用了新建的索引,而该索引并不适合于那条SQL;维护人员意外删除了表个索引的统计信息。

记一次RAC安装不能选择节点的问题处理

Posted by 老熊 on 6月 17th, 2010

很久没有更新BLOG了,无论出于什么样的原因,我都十分汗颜。向各位关注老熊一亩三分地的朋友们说声对不起了。

本文讲述端午节期间一位朋友在AIX 6.1上安装Oracle 10g RAC时死活不出现节点选择界面的问题的处理过程,希望对一些朋友有所帮助。在正文开始之前,向在端午期间仍然奋战在DBA工作一线的朋友们致敬^_^

对这个问题,是通过朋友的QQ远程协助处理的,因此在下面的过程描述中缺乏一些界面或者说是代码。不过我尽量将处理的思路描述清楚。

众所周知,在安装10g RAC时,需要先安装CRS,即Oracle Cluster,再安装Oracle RDBMS Software。安装这2部分都应该会出现选择安装节点的界面,本文描述的问题是出现在RDBMS部分出现的。下面是当时具体的处理过程:

  • 安装界面不出现节点选择界面,很多时候是由于/etc/hosts配置不当,以及rsh或ssh配置不当所导致。而之前CRS已经安装妥当,同时crs_stat命令检查CRS的各个资源也运行正常,使用crsctl check命令检查cluster也显示正常。那么hosts和rsh等问题应该是不会存在的,否则CRS也不能正确安装和运行。虽然如此,我还是检查了一下这2方面,没有发现任何问题。
  • 在OUI安装界面,在选择Oracle HOME时,发现主机上已经有2个HOME,分别是/oracle/product/10.2.0/crs和/oracle/product/10.2.0/cluster,此系统安装的朋友解释是在安装CRS时,出现了异常,然后重新安装到另一个目录成功。crs那个目录是废弃的HOME,cluster那个是正在运行的CRS的HOME。
  • 对于这样的问题,我习惯的做法是,先检查能够容易想到的,如果根据经验和能够想到的可能出现的问题进行排查后,仍然不能解决问题,那么我就会参考文档,按文档一步一步去检查。在此期间,我参考了Metalink上的文档《Minimum Software Versions and Patches Required to Support Oracle Products on IBM Power Systems [ID 282036.1]》、《RAC Assurance Support Team RAC Starter Kit and Best Practices (AIX) [ID 811293.1]》、《Oracle Database on Unix AIX,HP-UX,Linux,Mac OS X,Solaris,Tru64 Unix Operating Systems Installation and Configuration Requirements Quick Reference (8.0.5 to 11.2) [ID 169706.1]》三篇文章。同时也参考了《Oracle Database 10gR2 RAC on AIX Install Guide》(本文可以自MOS 811293.1文章中的连接下载)。根据文档重点考虑到了AIX 6.1这个新版本,同时应用了文档中提到的rootpre.sh补丁。问题仍然没有解决。

其实根据安装的经验,安装RAC时问题通常会出现在CRS安装部分,而RDBMS这部分很少有遇到问题。接下来:

  • 在安装的时候还发现有java的报错,在网上搜索一番这个错误,未果。这个错误与安装问题可能有很大的关联,但是不知道问题出现在哪里,是JAVA的问题?是安装程序的问题?还是缺少什么组件?或者是操作系统的问题,不得而知。
  • 根据MOS文档《How to trace OUI Note:269837.1》,开启安装程序的跟踪:
    ./runInstaller -J-DTRACING.ENABLED=true -J-DTRACING.LEVEL=2
    从结果来看,安装程序获取了ocr的路径,同时获取了其他一些属性。在此之后,就报出了JAVA的错误。

在问题的整个处理过程中,我一直在思考一个问题,那就是安装程序OUI是怎么来判断应该安装RAC还是安装非RAC的软件。我想到的几个方面:

  • 从/etc/hosts来判断,我很快否决了这个想法,这个不太可靠。
  • RAC的运行离不开Cluster软件,因此检查是否有cluster,cluster中有些什么样的节点,这是一个可行的办法。以前在AIX上安装Oracle9i时,如果有HA软件在运行,在安装时则会自动出现节点的选择并安装RAC软件。这是我认为的比较可靠的,并且应该是Oracle应当会采用的方法。但这里一个关键的问题是,现在CRS运行正常,安装程序为什么不能检测到cluster,不能检测到cluster中的节点。
  • 通过Oracle Inventory来检测,这是从以前解决DBCA建库不出现节点选择界面所得到的经验。当时为了解决问题,甚至反编译了DBCA部分JAVA代码。

打开/oracle/oraInventory/ContentsXML/inventory.xml,文件内容如下:

<?xml version="1.0" standalone="yes" ?>
<!-- Copyright (c) 2005 Oracle Corporation. All rights Reserved -->
<!-- Do not modify the contents of this file by hand. -->
<INVENTORY>
<VERSION_INFO>
   <SAVED_WITH>10.2.0.1.0</SAVED_WITH>
   <MINIMUM_VER>2.1.0.6.0</MINIMUM_VER>
</VERSION_INFO>
<HOME_LIST>
<HOME NAME="OUIHome1" LOC="/oracle/product/10.2.0/crs" TYPE="O" IDX="1" CRS="true">
   <NODE_LIST>
      <NODE NAME="node1"/>
      <NODE NAME="node2"/>
   </NODE_LIST>
</HOME>
<HOME NAME="OUIHome2" LOC="/oracle/product/10.2.0/cluster" TYPE="O" IDX="2" CRS="true">
   <NODE_LIST>
      <NODE NAME="node1"/>
      <NODE NAME="node2"/>
   </NODE_LIST>
</HOME>
</HOME_LIST>
</INVENTORY>

会不会是安装程序在根据第一个CRS HOME检测cluster出现了问题呢。删除掉IDX=“1”的那个HOME,将IDX=“2”改成IDX=”1″,然后重装安装。啊,上帝保佑,成功了。当时差点就内牛满面啊。

对于这个问题,其实还有一个不算太完美的解决办法,就是分别在2个节点上安装好Oracle RDBMS软件,然后打开rac_on选项,然后relink oracle。只不过这样的情况下,DBCA这样的工具又不能正常使用了。

也许有朋友问,为什么不一开始就关注inventory的问题。这可能跟我的处理问题习惯有关系,通常情况下我会按常规方式处理,或者看看有没有类似的案例。接下来才会考虑那些比较另类的方法。
--EOF

如何给Oracle数据库分配内存?

Posted by 老熊 on 4月 15th, 2010

曾几何时,网络上流传着给Oracle数据库分配内存的一条法则:把80%的内存分配给Oracle使用,而又将这80%的内存分配80%给Oracle的SGA,剩下的20%分给Oracle的PGA。记得Tom曾说过类似这样的话:如果一个参数的设置对Oracle是最佳的,那么Oracle就会自动地将其设为了默认值。而显然,在内存分配这事上,Oracle的初始设置并不是按这个法则的,那么就是说从某一方面证明这个法则存在问题。

当然大部分DBA不会这样设置内存参数,但是也有不少的人在Oracle的内存分配上存在欠考虑的地方。

首先,我们来看看保留可用内存20%给操作系统是否合适。对于2G内存的服务器(现实中这样的机器不少),20%意味着400M,而通常400M对操作系统来说是不够用的。而对于内存特别多的主机,20%又显得太多。比如下面是一份来自于一台P595的内存情况:

====================================================|==========|===========
Memory Overview                                     |    Pages |  Megabytes 
----------------------------------------------------|----------|-----------
Total memory in system                              | 45875200 |  179200.00 
    Total memory in use                             | 34789026 |  135894.63 
    Free memory                                     | 11086174 |   43305.36 
====================================================|==========|===========
Segment Overview                                    |    Pages |  Megabytes 
----------------------------------------------------|---------|-----------
Total segment id mempgs                             | 32618956 |  127417.79 
    Total fork tree segment pages                   |     2074 |       8.10 
    Total kernel segment id mempgs                  |  3594452 |   14040.82 

这台主机共计179GB物理内存,已使用135G,其中内核占用14G。内核占用的内存不到总内存的10%。

以上的数据以及说明,只是表达这样一个观点,对于操作系统的保留内存,需要根据实际情况预以考虑,这包括了操作系统的内核参数的设置。比如在AIX下的默认设置,client和perm内存可以占用远远超过20%的内存,而HP-UX下的默认设置,File Cache和Buf Cache也可能占用远远超过20%的内存。所以对于这些环境的数据库,一定要注意调整OS的内核参数。对于OS的内存使用,至少保留20%也不失为一种稳妥的做法。

除了操作系统这一块,给Oracle分配内存的时候,还需要注意以下非常重要的几点,这几点经常被人忽略:

  • 注意业务高峰期的内存使用:我所维护的一套系统,平时的连接数通常在5000-5500左右,而在最高时连接数达到了8000,也就是到达了连接的上限才作罢。因此,我们需要为业务高峰期时保留足够的内存。
  • 对于RAC数据库,需要考虑到其他节点故障或停机维护时,连接和压力转移到继续工作的节点时的内存消耗。
  • 一些人只考虑到了连接时进程使用的PGA内存,这里存在一个很大的误解,就是认为一个连接,只会使用PGA的内存。但还有一个很重要的内存使用,那就是进程本身占用的操作系统内存,除了PGA之外的内存。进程本身有代码(在OS中这通常是共享的),有stack,有heap,还要有kernel的内存占用。PGA只是进程使用的内存中一部分,甚至大部分情况下只是一小部分。在Oracle 10.2.0.4 for AIX下测试过,一个空闲连接,也就是啥事儿都不干的一个连接,PGA占用500K左右,而server process进程占用的内存在4-5M之间。测试时这套库刚启动,没有任何负载。实际上据观察在一套运行比较长时间的库上,server process占用的内存在9-10M之间。当然不同的系统,不同的配置,oracle进程占用的内存有所不同,有兴趣的朋友可以测量一下Oracle进程在HP-UX和LINUX下的内存占用。

对于SGA内各组件的细分以及PGA大小设置,网上很多相关的文章可供参考,本文不再涉及。不过我的个人观点是:参数的调整也不是一步到位的事情,需要根据系统运行时对性能数据的分析来进行调整,直至达到最优化。

我们应该怎样安装Oracle数据库?

Posted by 老熊 on 4月 12th, 2010

应该怎么样安装数据库,从安装软件到创建数据库?对于这个问题,或许有的人不屑一顾,因为他们觉得这没有丝毫问题;同时有另一部分人,觉得这是个大问题。

在安装Oracle上,通常会有几种类型的人:

  • 完全抓不着头脑,不知道怎么安装,这通常是初学者,连Linux/Unix都不太熟悉。
  • 很少安装Oracle的人,但是知道对照文档一步一步操作,出了错也知道上google、baidu和metalink查找解决方案。
  • Oracle老手,安装数据库不需要任何文档,对每个步骤也很熟悉。
  • 对Oracle的安装非常熟悉,但是在安装时仍然按文档一步一步操作。

对我个人来说,我是最后一种类型的人。我也自认为安装了不少的数据库,覆盖了大部分的平台和操作系统。但我安装的时候,仍然会不嫌麻烦的一步一步操作按文档操作。在安装数据库软件包括建库,基本上没有遇到过什么问题。因为我也见过
很多朋友,被安装这一问题折腾得焦头烂额,特别是在安装RAC的时候,这里只是写一写我自己的一些做法,仅供参考,毕竟每个人都有自己的习惯和做法。

本文主要描述Linux/Unix下的Oracle安装,不涉及Windows系统下的安装。

我的习惯做法:

  • 平时注意收集安装文档,包括oracle online document(所谓的官方文档),metalink上的,还有IBM、HP这些公司与Oracle合作部门提供的文档。oracle online document中安装部分没事看一遍就可以了,但是metalink上很多文档详细地记录了版本的兼容性,安装时可能出现的问题以及解决办法等等,比如很实用的文档:《Oracle Database on Unix AIX,HP-UX,Linux,Mac OS X,Solaris,Tru64 Unix Operating Systems Installation and Configuration Requirements Quick Reference (8.0.5 to 11.2) [ID 169706.1]》、《Linux OS Requirements Reference List for Database Server [ID 851598.1]》、《Status of Certification of Oracle Clusterware with HACMP [ID 404474.1]》。而其他厂商的文档包括:《CookBook_V3.2_Oracle_9i_RAC_AIX5L》、《COOKBOOK_Oracle CTC RAC10g R2 on HP-UX》、《COOKBOOK-V2.0-10gRAC R2 - ASM - AIX5L - SAN Storage》如此等等,还包括网上一些朋友自己撰写的安装文档。在参考这些文档时,需要注意的是,一定要明白每一个步骤其目的,有什么作用。
  • 根据以上提到的文档,进行整理,形成自己的文档。我在前面说到,我安装时一步一步按文档操作,是指的按我自己的文档,而不是去参考前面提到的若干文档。在自己的文档中,甚至提供了详细的命令,这样在安装的时候对某些不熟悉的命令不至于现查资料。
  • 深入理解文档中提到的各个参数、各个命令的作用。这不光是对安装,而对于Oracle数据库的更深入理解也是大有好处的。

安装Oracle数据库时,在安装软件之前,通常有下面的操作:

  • 检查操作系统版本、相应组件是否安装,是否有安装好文档中指定的补丁,也包括c编译器或c语行环境,这些对Linux下的安装来说犹为重要。
  • 检查文件系统空间,特别是/tmp临时文件系统
  • 检查memory大小,特别是swap的大小。特别是在HP-UX下,swap的管理方式与其他系统有些不一样(此处不再细述),最
  • 好能够达到物理内存大小,对于特别大的物理内存,至少也要达到一半。
  • 检查主机时区,时间设置。这一步通常被很多人忽略。
  • 检查主机名设置,有的安装系统相当不负责任,直接将主机取名localhost。
  • 检查异步IO设置。
  • 检查网络设置,包括/etc/hosts文件的设置,特别是对RAC数据库犹为重要。
  • 检查内核参数,特别是共享内存、信号量、用户最多可运行进程数这些参数。
  • oracle用户创建后,注意编辑profile文件,设置相应的环境变量,注意不同的平台,相同意义的环境变量却有不同的名字,比如linux下的LD_LIBRARY_PATH和AIX下的LIBPATH。经常见到有的系统,oracle的PATH都没有设置,这样登录后操作相当不方便。
  • 对Oracle用户设置limit,通常是直接编辑/etc/security/limits.conf文件。
  • 给Oracle用户一些特定的权限,比如HP-UX下修改/etc/privgroup文件,10.2.0.4 for AIX下给用户CAP_NUMA_ATTACH, CAP_BYPASS_RAC_VMM, CAP_PROPAGATE 这样的权限等等。
  • 对于RAC,还需要设置rsh或ssh

至于安装的其他步骤,不是本文所要讲的主要内容,在此略过。

还要提及一点,安装的时候对于目录的选择,可以按照OFA的标准做法,也可以按照使用部门的习惯,建议使用OFA。经常有见到一些乱七八糟的目录,让人好找,这种做法不太好。

说到安装,不能不提到打补丁。在安装完成后,最好是打上较新的补丁包以及metlink上提到的一些建议打的补丁。而等到系统正式使用,发现问题再打补丁,其代价就昂贵得多。

由于安装Oracle软件以及升级版本和打补丁,比较耗时,有的朋友就喜欢下面的做法:安装好软件,打上补丁,然后tar成一个包,保存在自己的存储介质上,下次在其他主机上安装时,直接用这个tar包解开。这种做法可以省一些安装软件的时间,但是需要tar包的环境是否一致。我曾遇到过下面的问题:

某套新装9208的库,报ORA-600[504]错误,通过在metalink上搜索发现其最符合的一个BUG对应的补丁已经打上。其他类似环境下,包括有同样的补丁,却没有这样的错误。我找了一台操作系统完全一样的测试主机,安装与有问题库的版本完全一样的版本和补丁,却也没有这样的错误;接下来我将出问题的Oracle home 复制到测试主机上,结果问题重现了,看起来问题在Oracle软件上。我尝试执行relink操作,居然失败。发现这份Oracle在relink包括有HA代码,实际上这是一个单机的环境,也没有安装HACMP。最后,使用rac_off选项,再重新relink,新生成的oracle,不再出现这样的问题。在有问题的生产主机上重新relink后,问题解决。

出现问题的Oracle,就是通过解tar文件来安装的,在主机上我们也发现了以前安装时保留的tar文件。因此通过这种方式安装的,建议进行relink。

下面再谈谈创建数据库的一些个人经验:

  • 尽量使用new database(9i)或者custom database(10g)这样的选项来创建库,建库时只安装必须的组件,这种做法有3个主要好处:更安全、更稳定、以后升级时所花的时间更少。
  • 创建数据库时注意选择正确的字符集。
  • 如果是选用模板创建数据库,注意模板有可能不与Oracle软件软件版本相匹配。同时在建库完成后需要运行相应的脚本,比如在安装了PSU的情况下,那么使用模板建库,得需要运行PSU带的脚本(具体参考PSU的README)。
  • 在打完补丁之后,在创建数据库,避免在升级软件之后还要升级数据库。
  • 建完库后,建议设置一下大体上合理的数据库参数。

希望本文能够对Oracle数据库的安装不太熟悉的朋友一些帮助。

一些数据迁移的技巧

Posted by 老熊 on 3月 1st, 2010

去年年底做了不少系统的数据迁移,大部分系统由于平台和版本的原因,做的是逻辑迁移,少部分做的是物理迁移,有一些心得体会,与大家分享。

首先说说迁移流程,在迁移之前,写好方案,特别是实施的方案步骤一定要写清楚,然后进行完整的测试。我们在迁移时,有的系统测试了四五次,通过测试来完善方案和流程。

针对物理迁移,也即通过RMAN备份来进行还原并应用归档的方式(这里不讨论通过dd方式进行的冷迁移),虽然注意的是要将数据库设为force logging的方式,在用RMAN做全备之前,一定要执行:

alter database force logging;

否则可能会产生坏块。

对于逻辑迁移,在job_processes设置为>0的数值之前,注意job的下次执行时间和job所属用户。比如job的定义在之前已经导入,但是在迁移之时,job已经运行过,那么迁移完成之后,job的下次时间还是原来的时间,这样可能会重复运行。另外,job通过IMP导入后,job所属用户会变成导入用户的名称,显然job原来的用户就不能对JOB进行管理了,可以通过下面的sql进行修改:

update sys.job$ set lowner=cowner , powner=cowner;

在迁移之前,应该禁止对系统进行结构上的修改和发布,比如表结构,索引,存储过程包等。

如果是用exp/imp导入的对象,包括存储过程等,应该检查对象是否与原生产库一致,比如由于dblink的原因,imp之后,存储过程不能创建,导致有部分存储过程丢失,尽管这些存储过程可能没有被使用。

下面是一些加快迁移速度的技巧:

  • 通过dblink,使用append insert的方式,同时利用并行,这种方式比exp/imp更快
  • 对于有LONG类型的列,insert..select的方式显然是不行的,可以通过exp/imp的方式,但是这种方式速度非常慢,其原因在于imp时一行一行地插入表。有另外一种方式,即sqlplus的copy命令,下面是一个示例:
    spool copy_long_table_1.log
    conn / as sysdba
    set copycommit=2000
    set arraysize 30
    set long 10485760
    
    copy from system/xxxx@source_db append username.table_name using select * from username.table_name;
    
    spool off
    exit
    

    不过,sqlpus的copy命令不支持有timestamp和lob列类型的表。如果有timestamp类型的表,可以通过在exp时,加上rowid的条件,将一个表分成多个部分同时操作,对于有lob类型的表,也可以同样处理(因为insert …select方式下,有lob类型列时,也同样是一行一行地插入)。注意在这种方式下,就不能使用direct的方式exp/imp。下面是exp导出时parfile示例:

    query="where rowid>=dbms_rowid.rowid_create(1,71224,52,9,0) and rowid<=dbms_rowid.rowid_create(1,71224,55,1038344,10000)"
    file=/dumpdata/n1.dmp
    tables=username.table1
    constraints=n
    grants=no
    indexes=no
    buffer=104857600
    ...
    ...
    query="where rowid>=dbms_rowid.rowid_create(1,71224,423,137,0) and rowid<=dbms_rowid.rowid_create(1,71224,432,59272,10000)"
    file=/dumpdata/n6.dmp
    tables=username.table1
    constraints=n
    grants=no
    indexes=no
    buffer=104857600
    

    将表分成几部分同时操作,不仅仅可以利用rowid,也可以利用表上的列,比如说,表上有一个created_date的列,并且保证是递增插入数据,那么这种情况下,也可以使用这个字段将表分成不同的范围同时进行导出和导入。不过使用ROWID通常具有更高的效率。
    当然对于有lob列的表,可以按上述方式,拆成多个insert方式同时插入,不需要exp/imp。

  • 对于特别大的分区表,虽然使用并行可以提高速度,但是受限于单个进程(不能跨DB LINK进行并行事务,只能并行查询,也即insert..select只能是SELECT部分才能进行并行)的处理能力,这种方式下速度仍然有限。可以并行将数据插入多个中间表,然后通过exchange partition without validation 的方式,交换分区,这种方式将会大大提高了速度。
    有朋友可能会问,为什么不并行直接插入分区表,当然如果是非direct path(append)方式,则是没问题的,但是这种方式插入的性能较低。而direct path的方式,会在表上持有mode=6(互斥)的TM锁,不能多个会话同时插入。(update: 在insert 时使用这样的语句:insert into tablename partition (partname) select * from tablename where ….,更简单更有效率。)
  • 迁移时,将数据分成两部分,一部分是历史表,第二部分是动态变化的表,在迁移之前,先导入历史表,并在历史表上建好索引,这无疑会大大减少迁移时业务系统中断时间。
  • 迁移之前,考虑清理掉垃圾数据。
  • 迁移时,应保证表上没有任何索引,约束(NOT NULL除外)和触发器,数据导入完成后,再建索引。建索引时同样,同时使用多个进程跑脚本。索引创建无成后,应去掉索引的PARALLEL属性
  • 在创建约束时,应按先创建CHECK约束,主键,唯一键,再创建外键约束的顺序。约束状态为 ENABLE NOVALIDATE,这将大大减少约束创建时间。而在迁移完成后,再考虑设回为ENABLE VALIDATE。
  • 通过使用dbms_stats.export_schame_stats和dbms_stats.import_schame_stats导入原库上的统计信息,而不用重新收集统计使用。

朋友们可以看到,以上均是针对9i的,实际上在10g甚至11g环境下,也仍然很多借鉴意义。当然这些技巧不仅仅用于完整的数据库迁移,也可以应用到将个别表复制到其他数据库上。

这里没有提到的是利用物化视图或高级复制、触发器之类的技术,因为这些技术,毕竟要修改生产库,对生产库的运行有比较大的影响,因此,只有在停机时间要求特别严格,而在这个时间内又不能完成迁移时才应该考虑。

从迁移的经验来说,只有完善的流程,完整的测试才可以保证成功。这里只是列举了一些小技巧,如果对整个迁移过程有兴趣,可以针对这个话题再进行讨论。

统计信息与子分区

Posted by 老熊 on 12月 19th, 2009

在以前的一篇文章《DBMS_STATS、ANALYZE以及Global Statistics》中,提到使用10g数据库dbms_stats收集统计信息时,granularity缺省值为“AUTO”,其含义是“Auto -- Table + Partition + Subpartition (10g,表+分区,当子分区是list分区时还包括子分区)”。本文就这个问题再深入地探讨一下。

大家都知道,子分区有两种,一种是分区为RANGE,子分区为HASH,另一种是分区为RANGE,子分区为LIST。在10g数据库中,如果在使用dbms_stats收集统计信息时,如果没有显式指定granularity(粒度),那么granularity就会取自dbms_stats配置:
而其缺省值是“AUTO”,而不再是9i下的”DEFAULT”:

SQL> select dbms_stats.get_param('granularity') param from dual;

PARAM
------------------------------
AUTO

而10g自带的自动收集统计信息的任务“GATHER_STATS_JOB”,其granularity同样是取自granularity param。当然可以通过下面的SQL来更改其值:

SQL> exec dbms_stats.set_param('granularity','global and partition');

这样更改后,dbms_stats默认就会收集表以及分区级统计信息,不收集子分区级统计信息。

那么,granularity=auto时,到底是怎么样的呢?前面说到了子分区是以list方式分区时,那么就会收集子分区级统计信息,其言外之意就是如果子分区是以hash方式分区时就不会收集子分区统计信息了。到底是不是这样呢?下面做个测试,测试环境是Oracle 10.2.0.4 for Linux AS4:

QL> create table t1
  2  partition by range(object_id)
  3  subpartition by hash(data_object_id)
  4  subpartitions 4
  5  ( partition p1 values less than(10000),
  6    partition p2 values less than(20000),
  7    partition p3 values less than (maxvalue)
  8  )
  9  as select * from dba_objects;  

Table created.

SQL> create table t2
  2  partition by range(object_id)
  3  subpartition by list(object_type)
  4  subpartition template(
  5    subpartition sp1 values ('TABLE'),
  6    subpartition sp2 values ('INDEX'),
  7    subpartition sp3 values ('VIEW'),  
  8    subpartition sp4 values (DEFAULT)
  9  )  
 10  ( partition p1 values less than(10000),
 11    partition p2 values less than(20000),
 12    partition p3 values less than (maxvalue)
 13  )
 14  as select * from dba_objects; 

Table created.

我们先建再从个测试表,表T1是RANGE+HASH方式的复合(组合)分区表,表T2是RANGE+LIST方式的复合分区表。
下面将”granularity” param重新设回为”auto“,然后收集T1和T2的统计信息:
Read the rest of this entry »

视图与权限

Posted by 老熊 on 11月 14th, 2009

前几天客户遇上这样一个问题,某个用户A将视图的SELECT给予另一个用户B,但是用户B查询这个视图时,仍然报错:ORA-01031: 权限不足。这是怎么一回事呢?下面来模拟一下这个过程:

有三个用户test1,test2,test3, 三个用户都具有DBA色色权限。

用TEST1用户创建一个表T1,并将其查询权限授予TEST2:

SQL> create table t1 as select * from all_objects;

表已创建。

SQL> grant select on t1 to test2;

授权成功。

用TEST2用户创建一个视图,视图的基表是TEST1.T1,并将查询权限授予TEST3:

SQL> create view v_t1 as select * from test1.t1;

视图已建立。

SQL> grant select on v_t1 to test3;

授权成功。

TEST3用户查询视图TEST2.V_T1:

SQL> select * from test2.v_t1 where rownum<1;
select * from test2.v_t1 where rownum<1
                    *
ERROR 位于第 1 行:
ORA-01031: 权限不足

可以看到报了权限不足的错误,就算这里TEST3用户有DBA权限。
这到底是怎么回事呢?
其实视图的权限,有两点需要引起注意:

1. 视图中,类似于定义者权限的存储过程,是屏蔽了角色权限的。比如如果TEST1没有显式地将T1表的SELECT权限给予TEST2,那么TEST2在创建视图V_T1时也会报ORA-01031错误,即使TEST2用户拥有DBA角色权限。

2.如果在用户A的视图中,引用了其他用户B的表,用户A将视图的访问权限给予用户C,那么就变相地将用户B的表的访问权限给予了用户C,因此,用户A必须有将用户B的表的访问权限转授用户C的权限,也就是用户B在授予A权限时,必须使用with grant option。

显然这里正是由于第2点的原因,导致用户TEST3不能访问视图。用户TEST1执行下面的操作,将解决这个问题:

SQL> grant select on t1 to test2 with grant option;

授权成功。

对于视图的UPDATE,DELETE权限,同样是如此。

在测试时,有一个现象,有点意思。就是如果用户TEST2没有显式地把V_T1的SELECT权限授予TEST3,而TEST3在有SELECT ANY TABLE或DBA权限时,则查询这个视图时不会报权限不足的错误。由于有SELECT ANY TABLE权限的存在,所有的用户表都可以被访问。但是显式授予表的权限时,似乎表的权限有更高的优先级,并且没有跟系统权限和角色权限进行结合。或者版本不同,表现得不一样,在我的测试中,是Oracle 9.2.0.8 for Windows。

怎样删除UNDO自动管理模式下的UNDO段

Posted by 老熊 on 10月 30th, 2009

在自动UNDO管理模式下,我们有时仍然想手动删除UNDO段。比如某个UNDO段出现了逻辑坏块。
下面首先来看看,直接删除UNDO段能不能成功。

SQL> drop rollback segment "_SYSSMU9$";
drop rollback segment "_SYSSMU9$"
*
ERROR 位于第 1 行:
ORA-30025: 不允许 DROP 段 '_SYSSMU9$' (在撤消表空间中)

看来是行不通的。那么怎么样才能删除呢?试试下面的办法:

SQL> alter session set "_smu_debug_mode"=4;

会话已更改。

SQL> drop rollback segment "_SYSSMU9$";
drop rollback segment "_SYSSMU9$"
*
ERROR 位于第 1 行:
ORA-01545: 指定的回退段'_SYSSMU9$'不可用

还是不行。下面我们看看UNDO段的状态:

SQL> select segment_name,status from dba_rollback_segs;

SEGMENT_NAME                   STATUS
------------------------------ ----------
SYSTEM                         ONLINE
_SYSSMU1$                      ONLINE
_SYSSMU2$                      ONLINE
_SYSSMU3$                      ONLINE
_SYSSMU4$                      ONLINE
_SYSSMU5$                      ONLINE
_SYSSMU6$                      ONLINE
_SYSSMU7$                      ONLINE
_SYSSMU8$                      ONLINE
_SYSSMU9$                      ONLINE
_SYSSMU11$                     OFFLINE

发现这个要删除的UNDO状态为ONLINE。下面我们将UNDO段置为OFFLINE状态,再删除:

SQL> alter rollback segment "_SYSSMU9$" offline;

回退段已变更。

SQL> drop rollback segment "_SYSSMU9$";

回退段已删除。

可以看到UNDO段已经被删除。这里首先把UNDO段OFFLINE,然后再DROP。值得注意的是,在没有修改”_smu_debug_mode”的情况下,UNDO段是不能OFFLINE的。

总结:
要在UNDO自动管理模式下删除UNDO段,需要三个步骤:

  • 执行alter session set “_smu_debug_mode”=4;
  • 执行 alter rollback segment “undo-segment-name” offline;
  • 执行 drop rollback segment “undo-segment-name” ;

记一次ORA-600[4042]故障的处理

Posted by 老熊 on 10月 17th, 2009

一套运行在Linux下的Oracle 9.2.0.4的库,出现了大量的ORA-600[4042]错误。

ORA-00600: internal error code, arguments: [4042], [31760], [], [], [], [], [], []

关于ORA-600错误,第一个参数,也就是第一个方括号中的标识,通常可以用来定位Oracle错误发生的内部模块。(可以参考Metalink Doc ID 146580.1:What is an ORA-600 Internal Error?)如果是数字,最高位通常是指一个大的模块,而接下来的一位是小的模块。比如这里[4042],4000,最高位是4,是Transaction Layer(事务层),而次高位是0,Transaction Undo(详见Metalink Doc ID : 175982.1 ORA-600 Lookup Error Categories)。

针对这个错误,很明显是跟事务有关。在处理的时候,第一反应肯定是检查TRACE文件:

ORA-00600: internal error code, arguments: [4042], [31760], [], [], [], [], [], []
Current SQL statement for this session:
INSERT INTO XXX .....
....
----- Call Stack Trace -----
calling              call     entry                argument values in hex      
location             type     point                (? means dubious value)     
-------------------- -------- -------------------- ----------------------------
ksedmp()+269         call     ksedst()+0           0 ? 0 ? 0 ? 0 ? BFFF90A4 ?
                                                   A16D886 ?
ksfdmp()+14          call     ksedmp()+0           3 ? BFFF91B0 ? 98585B4 ?
                                                   AD58FA0 ? 3 ? A4B929C ?
kgeriv()+188         call     ksfdmp()+0           AD58FA0 ? 3 ?
kgeasi()+108         call     kgeriv()+0           AD58FA0 ? AD9AFC0 ? FCA ? 1 ?
                                                   BFFF91EC ?
ktugusc()+787        call     kgeasi()+0           AD58FA0 ? AD9AFC0 ? FCA ? 2 ?
                                                   1 ? 4 ? 7C10 ?
ktuswr()+2049        call     ktugusc()+0          BFFF9394 ? D ? 1 ? 0 ? 0 ?
                                                   0 ? 0 ? 0 ?
ktusmous_online_und  call     ktuswr()+0           D ? 0 ? 0 ? 0 ? 0 ? 1 ?
oseg()+898                                         
ktusmaus_add_us()+3  call     ktusmous_online_und  1 ? 1 ? BFFF94F8 ? 1 ?
27                            oseg()+0             
ktubnd()+7646        call     ktusmaus_add_us()+0  BFFF9CEC ? 0 ?
ktuchg()+581         call     ktubnd()+0           BFFF9678 ? 8468F4F0 ?
                                                   BFFF9CEC ? 0 ?
ktbchg2()+318        call     ktuchg()+0           2 ? 89E91A08 ? 1 ? B7BC3484 ?
                                                   B7BC348C ? AD7BECC ?
                                                   BFFF9CEC ? AD7BE38 ? 0 ? 0 ?
kdtchg()+1406        call     ktbchg2()+0          0 ? 89E91A08 ? B7BC3484 ?
                                                   B7BC348C ? AD7BECC ?
                                                   BFFF9CE4 ? AD7BE38 ? 0 ? 0 ?
kdtwrp()+2272        call     kdtchg()+0           B7BA8638 ? B7BC3484 ?
                                                   B7BC348C ? AD7BECC ?
                                                   AD7BE38 ? 1 ? 1C6 ?
kdtInsRow()+1724     call     kdtwrp()+0           B7BA8638 ? B7BA0000 ?
                                                   60DD40A4 ? B7BC31AC ? C ?
                                                   2C88E28 ?
insrow()+275         call     kdtInsRow()+0        B7BA8638 ? 89E95354 ?
                                                   89E950EC ? B7BA8418 ?
                                                   9840000 ? AD589E8 ?
insdrv()+2566        call     insrow()+0           B7BA8638 ? BFFF9FEC ? 0 ?
insexe()+1665        call     insdrv()+0           B7BA8638 ? 89E950EC ? 0 ?
                                                   B7BA8418 ? 0 ? 0 ?
opiexe()+10831       call     insexe()+0           89E95354 ? BFFFA220 ?
opipls()+6068        call     opiexe()+0           4 ? 3 ? BFFFA98C ?
opiodr()+5238        call     kjxsupd()+987        66 ? 6 ? BFFFB64C ?
rpidrus()+140        call     opiodr()+0           66 ? 6 ? BFFFB64C ? 5 ?
skgmstack()+211      call     rpidrus()+0          BFFFB028 ? 10 ? BFFFB040 ?
                                                   BFFFB3E4 ? BFFFB028 ?
                                                   899782A ?
rpidru()+93          call     skgmstack()+0        BFFFB040 ? AD5A760 ? F618 ?
                                                   899782A ? BFFFB028 ?
rpiswu2()+777        call     rpidru()+0           BFFFB3E4 ? 40 ? 40 ? 0 ?
                                                   40C ? A4B929C ?
rpidrv()+1452        call     rpiswu2()+0          837327D8 ? 40 ? BFFFB4E8 ?
                                                   2 ? BFFFB508 ? 40 ?
psddr0()+113         call     rpidrv()+0           5 ? 66 ? BFFFB64C ? 3A ?
                                                   AD5907C ? BFFFB7F8 ?
psdnal()+173         call     psddr0()+0           5 ? 66 ? BFFFB64C ? 30 ? 20 ?
                                                   B7BBB6B8 ?
pevm_EXECC()+458     call     psdnal()+0           BFFFC844 ? BFFFC834 ?
                                                   AD53500 ? B7BBB6B8 ?
                                                   856EA21C ? 856EA21C ?
pfrrun()+31877       call     pevm_EXECC()+0       B7BBF19C ? AD9E4C0 ? 20 ?
peicnt()+291         call     pfrrun()+0           B7BBF19C ? 0 ? AD9E31C ?
                                                   ADA0464 ? AD5907C ?
                                                   BFFFCC10 ?
kkxexe()+451         call     peicnt()+0           BFFFC844 ? B7BBF19C ? 2 ?
                                                   AD9954C ? 5001AA24 ? 0 ?
opiexe()+12624       call     kkxexe()+0           B7BBD068 ? B7BB022C ?
                                                   AD53504 ? B7BBD068 ? 0 ? 0 ?
opiall0()+4435       call     opiexe()+0           4 ? 3 ? BFFFD064 ?
opial7()+441         call     opiall0()+0          3E ? 22 ? BFFFD164 ?
                                                   BFFFDC0C ? BFFFD1EC ? 0 ?
opiodr()+5238        call     kjxsupd()+987        47 ? F ? BFFFDC0C ?
ttcpip()+2124        call     opiodr()+0           47 ? F ? BFFFDC0C ? 1 ?
Cannot find symbol in /lib/tls/libc.so.6.
opitsk()+1635        call     ttcpip()+0           AD53500 ? 47 ? BFFFDC0C ? 0 ?
                                                   BFFFE4E4 ? BFFFE4E0 ?
opiino()+602         call     opitsk()+0           0 ? 0 ? AD53500 ? AD8D7B8 ?
                                                   83 ? 0 ?
opiodr()+5238        call     kjxsupd()+987        3C ? 4 ? BFFFF8B0 ?
opidrv()+517         call     opiodr()+0           3C ? 4 ? BFFFF8B0 ? 0 ?
sou2o()+25           call     opidrv()+0           3C ? 4 ? BFFFF8B0 ?
main()+182           call     sou2o()+0            BFFFF894 ? 3C ? 4 ?
                                                   BFFFF8B0 ? 1 ? 0 ?
00622DE3             call     main()+0             2 ? BFFFF954 ? BFFFF960 ?
                                                   5FBC66 ? 734FF4 ? 0 ?

从SQL来看,是个简单的INSERT语句,那么就涉及到事务处理了。
从call stack来看,在stack顶端,下面的几行表明错误应该是跟回滚段有关。

# ktugusc()+787        call     kgeasi()+0           AD58FA0 ? AD9AFC0 ? FCA ? 2 ?  
#                                                    1 ? 4 ? 7C10 ?  
# ktuswr()+2049        call     ktugusc()+0          BFFF9394 ? D ? 1 ? 0 ? 0 ?  
#                                                    0 ? 0 ? 0 ?  
# ktusmous_online_und  call     ktuswr()+0           D ? 0 ? 0 ? 0 ? 0 ? 1 ?  
# oseg()+898                                           
# ktusmaus_add_us()+3  call     ktusmous_online_und  1 ? 1 ? BFFF94F8 ? 1 ?  
# 27                            oseg()+0               

这一步的分析其实很快,基本上凭call stack中的函数名字来判断。由于当时有其他的事情在处理,同时也不在这套库的现场,就让DBA重新创建了一个UNDO表空间,并将UNDO_TABLESPACE参数设置为新的UNDO表空间名字,错误就不在出现。

其实这个故障的处理,类似于去年处理的一个案例,详见记一次并行恢复问题导致Oracle数据库Crash故障的处理

一个简单的故障处理过程,记录下来,供朋友们参考。


Copyright © 2007 老熊的三分地-Oracle、UNIX、数据恢复. All rights reserved.