分区键与分区本地索引

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环境下,也仍然很多借鉴意义。当然这些技巧不仅仅用于完整的数据库迁移,也可以应用到将个别表复制到其他数据库上。

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

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

一次ORA-01410故障的解决

Posted by 老熊 on 1月 27th, 2010

在一个表上建索引时,报ORA-01410错误,我们查询这个表来重现这个错误:

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.6.0 - 64bit Production
With the Partitioning option
JServer Release 9.2.0.6.0 - Production

SQL> set timing on
SQL> set time on
14:20:03 SQL> select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a;
select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a
                                                    *
ERROR at line 1:
ORA-01410: invalid ROWID

ORA-01410错误通常见于通过索引访问表,而索引或表由逻辑上的损坏。而这里显示没有通过索引访问表?那问题出在哪里呢?在这种情况下,这个错误与ORA-08103极其类似,参照《记一次ORA-8103错误的处理》

14:27:00 SQL> alter session set max_dump_file_size=unlimited;

Session altered.

Elapsed: 00:00:00.01
14:27:18 SQL> alter session set db_file_multiblock_read_count=1;

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> alter session set events 'immediate trace name trace_buffer_on level 1048576';

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> alter session set events '10200 trace name context forever, level 1';

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a;
ERROR at line 1:
ORA-01410: invalid ROWID

Elapsed: 00:05:50.82
14:33:09 SQL> 14:33:09 SQL> alter session set events 'immediate trace name trace_buffer_off';

Session altered.

在trace文件的最后,我们可以看到:

Consistent read started for block 10 : 2489c394
  env: (scn: 0x0a0d.690ff414  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.000000
00  scn: 0x0000.00000000 0sch: scn: 0x0000.00000000)

这里只有”start“,而没有finish,表明在读2489c394这个块出了问题。
用ODU工具的rdba查看文件号和坏号:

ODU> rdba 2489c394

  rdba   : 0x2489c394=613008276
  rfile# : 146
  block# : 639892

通过”alter sytem dump datafile 146 block 639892”命令发现块中的object_id与CUST_ORDER表的data object id不同,看起来这就是问题所在(此处不再列出数据)。
看起来有坏块了。不过这个库是个查询库,把表TRUNCATE之后重新从生产库同步过来,发现问题仍然存在,甚至把表DROP之后重建也是如此,均是发生在146/639892这个块上。

而TRUNCATE/DROP表都不能解决问题,显然这个块还在内存中,看起来需要刷新buffer cache了:

14:37:07 SQL> alter session set events 'immediate trace name flush_cache level 1';
Session altered.

刷新buffer cache后,问题解决。

总结:这个问题,与ORA-8103类似,都是出现了逻辑坏块,只不过这次的坏块是发生在内存中的块。至于坏块是怎么进入到内存中,为什么在重建表后还在内存中,这就是个谜了,或者是ORACLE的BUG,或者跟用的同步软件DSG有关。在这个案例中,块的object_id与段的实际的data object id不一致。而object_id不一致有时也会报ORA-600错误。

2010新年快乐

Posted by 老熊 on 1月 1st, 2010

此时此刻,我迎来了2010年的第一个加班。在这么一个杯具性时刻,还是要祝所有的朋友们,新年快乐!

回顾2009年,用一句简单的话来说就是,“这一年没有白过”。

看看这一年,有些什么样的收获:

自己开发的ODU软件,走向成熟,得到了许多朋友的认可,也帮助很多朋友解决了燃眉之急。从年初发布v2.0.1版,到支持压缩表,中间相隔了3个月的时间。从那之后,就是一些功能的完善和BUG的修复,到如今已经相对比较稳定。通过开发这个软件,我自己对ORACLE的块格式有了相当清楚地了解,同时这个软件也发挥了他独特的作用,帮助一些朋友恢复了数据。

与盖国强、杨廷锟、段林仲、邹德平合作的《Oracle DBA手记》,即将面世。这完全是一个意外。通过这样的写作,收获是巨大的。我也希望除BLOG之外方式的知识分享,能够让更多的从事Oracle数据库相关工作的朋友受益。

技术方面,理论知识方面进展不大,但是实战经验值大幅提升。学习,从来都是实践与理论相结合。新的一年,争取有更进一步的突破。

作了房奴,收了房,也有了代步的工具。压力是越来越大了。争取明年七八月时能住进新房。

这一年,也是辛苦的一年。特别是最近2个月,加班与通宵已经成为十分常见的活动,无奈的是,绝大多数时候没办法控制这样的事情的发生。

2010年,我希望也有更多的收获:

  • 争取把OCM给过了。谁叫这玩意儿商业需求是如此的旺盛。
  • 多写一些有价值的技术文章。
  • 接触接触Oracle之外的东西。

统计信息与子分区

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 12月 8th, 2009

说起来汗颜,我这个BLOG主要写Oracle相关的文章,也附带写点UNIX,可惜从来没正经写过UNIX方面的东西。毕竟不是专业的SA,水平不够恐怕误导读者朋友。这次的故障,主要是从OS层进行处理的,稍微算是沾上一点UNIX的边。闲话少扯了,说正事吧。

事情的起因,是系统的最终用户反映某些查询功能比较慢。简单地看了一下主机的负载以及数据库的性能状况,没发现什么异常,甚至可以说系统相当地轻闲。

那问题出在哪?我首先观察到内存的使用率相当地高,达到99%。但是从操作上看,速度还没受到影响。不过很快想到,这个系统某些模块,用了短连接,难道是监听太慢引起的?这个库启了6个监听(详见《一切皆有可能》),分别TNSPING这几个监听,有个别监听非常慢,重启监听后,查询功能比较慢的问题得到解决。

不过之前观察到的内存的异常使用引起了我极大的注意。这套系统,平时一般都会有几十G的空闲内存,不会达到这么高的。第一反应是用ipcs命令检查一下共享内存,发现有一个异常的共享内存段,占了60多G。

[oracle@hostname%/oracle]ipcs -ma
IPC status from /dev/kmem as of Mon Dec  7 10:58:53 2009
T         ID     KEY        MODE        OWNER     GROUP   CREATOR    CGROUP NATTCH      SEGSZ  CPID  LPID   ATIME    DTIME    CTIME 
Shared Memory:
m          0 0x41180809 --rw-rw-rw-      root      root      root      root      0        348  2725  2725  2:38:57  2:38:57  2:38:50
m          1 0x4e0c0002 --rw-rw-rw-      root      root      root      root      2      61760  2725  2727 12:27:19 18:19:39  2:38:50
m          2 0x411c0de1 --rw-rw-rw-      root      root      root      root      2       8192  2725  2727 12:27:19  2:38:50  2:38:50
m          3 0x00a5c581 --rw-------     sfmdb     users     sfmdb     users     11   10469376  3362  3398  2:39:38  2:39:39  2:39:38
m          4 0x4118043d --rw-------      root      root      root      root      1       4096  3410  4745  2:40:12 no-entry  2:40:12
m          5 0x06347849 --rw-rw-rw-      root      root      root      root      1      65544  3535  6722 17:53:03 17:53:03  2:39:47
m    1015814 0x0c6629c9 --rw-r-----      root       dba      root       dba      0   35921048  6722  6722 17:53:03 no-entry 17:53:03
m     819207 0x491002d0 --rw-r--r--      root      root      root      root      0      22908  3674  3674  2:39:54  2:39:54  2:39:54
m    5472264 0x00000000 D-rw-r-----    oracle       dba    oracle       dba      6 66640334848  5508 23604 17:58:00 17:58:00 17:58:00
m   95387657 0x0000cace --rw-rw-rw-      root       sys      root       sys      0          2 21306 21306 20:24:33 20:24:33 20:24:29
m   35520522 0xa57bccf8 --rw-r-----    oracle       dba    oracle       dba  12231 66640334848  3231 26942 10:58:53 10:58:53 18:10:36

ID为”5472264″的共享内存段就是异常的共享内存段。
为什么会出现这种情况?数据库可以确定是被重启过,询问客户这套系统的DBA,的确是在头一天出现了异常然后进行了重启。至于出现了什么样的异常,为什么要重启,这里不再深入。本文只讨论怎么样来清除这个异常的共享内存段。

由于这个内存段的NTATTCH(number of attach)为6,在HP-UX下是清理不掉的:

[oracle@hostname%/oracle]ipcrm -m 5472264
ipcrm: shmid(5472264): not found

这是由于还有进程attach(理解为连接吧)到这个共享内存段上。只要找到这个进程被KILL之,就会解决问题。一种简单的方法是使用lsof来找到这些进程:

[oracle@hostname%/oracle]lsof | egrep "COMMAND|5472264"

不过简单的方法,不一定效率就高。这个系统光oracle server process就有5000个以上,lsof实在很慢。所以运行几分钟就直接放弃(因为以前在这套系统上运行过lsof命令,知道要输出完结果时间比较“漫长”)。

OK, 手工找一下吧。从上面的ipcs输出的CTIME字段看到,正常的共享内存段是18:10左右创建的,而异常的是17:58左右创建的,那么attach到这个异常共享内存段的进程应该是在18点之前创建,而在17:58左右。首先使用”ps -ef | grep defunct“,没有发现僵死进程。然后根据这样的条件,并且经过一系列筛选,得到下面的结果:

[oracle@hostname%/oracle]ps -ef | grep oraclesidname | grep "17:" | grep -v "18:17" | grep -v "11:17"
  oracle 22586     1  1 07:17:43 ?         0:31 oraclesidname (LOCAL=NO)
  oracle 28403     1  0 09:17:38 ?         0:02 oraclesidname (LOCAL=NO)
  oracle 22618     1  0 07:17:59 ?         0:00 oraclesidname (LOCAL=NO)
  oracle  7539     1  0 08:17:42 ?         0:10 oraclesidname (LOCAL=NO)
  oracle  7419     1  0 08:17:05 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 22580     1  0 07:17:42 ?         0:36 oraclesidname (LOCAL=NO)
  oracle  7421     1  0 08:17:06 ?         0:06 oraclesidname (LOCAL=NO)
  oracle  7537     1  0 08:17:42 ?         0:02 oraclesidname (LOCAL=NO)
  oracle  7535     1  0 08:17:41 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 21395     1  0 17:56:49 ?         0:01 oraclesidname (LOCAL=NO)
  oracle 22616     1  0 07:17:59 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 20786     1  0 17:54:24 ?         0:10 oraclesidname (LOCAL=NO)
  oracle 22614     1  0 07:17:58 ?         0:00 oraclesidname (LOCAL=NO)
  oracle  7423     1  0 08:17:06 ?         0:18 oraclesidname (LOCAL=NO)

看上去进程号为21395和20786的进程,正好满足前面提到的条件。KILL这两个进程,检查共享内存段,发现这个异常的共享内存段自动被清除。再检查内存的使用,内存的使用率也大幅下降,回到正常状态。

今天也算是幸运的,在没有监控系统的情况下,人为的较早发现了这个问题,避免了全系统范围内的系统问题。如果没有及时发现这个问题,内存的使用一上去,开始大量使用交换页,那就头疼多了。


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