由于ODU命令比较多,特别是关键的unload命令比较复杂,本文将简单介绍几种场景下使用ODU进行数据恢复时,使用的命令序列。本文不会详细介绍每个命令的使用,详细的命令请参考本网站ODU页面下的链接

场景1. 数据库不能启动,但是SYSTEM表空间中的数据字典是完整的。

  • 生成数据字典:unload dict
  • 列出用户: list user
  • 列出用户下的所有表: list table username
  • 恢复表: unload table username.tablename
  • 也可以按用户恢复: unload user username

场景2. 表被TRUNCATE。

  • OFFLINE表所在的表空间
  • 生成数据字典:unload dict
  • 显示表的段头:desc username.tablename
  • 找到实际的data object id: dump datafile file# block block#
  • 扫描数据:scan extent
  • 恢复表:unload table username.tablename object object_id

UPDATE:
从3.0.7版本开始,恢复Truncate表更方便,只需要执行下面的步骤:

  • OFFLINE表所在的表空间
  • 生成数据字典:unload dict
  • 扫描数据:scan extent
  • 恢复表:unload table username.tablename object auto

场景3. 表被DROP。

  • OFFLINE表所在的表空间
  • 使用logminer从日志里面挖掘被drop掉的表其data object id,如果不能挖掘,按下面的场景4进行恢复。
  • 扫描数据:scan extent
  • 如果没有表结构信息,需要自动来判断:unload object data_object_id sample
  • 恢复表:unload object data_object_id column coltype coltype...

场景4. 系统表空间损坏。

  • 扫描数据:scan extent
  • 搜索数据:unload object all sample
  • 从结果文件sample.txt查找需要的数据
  • 恢复需要的表:unload object data_object_id column coltype coltype...

场景5. 表中数据被DELETE。

  • 将参数unload_deleted设置为YES
  • 生成数据字典:unload dict
  • 恢复表: unload table username.tablename
,

我们经常使用Sql Trace和10046 event来诊断Oracle数据库性能问题。而level超过1的10046事件通常称为extended sql trace,通常用于诊断确定的单个SQL、存储过程或会话的性能问题,具有如下的几个优点:

  • 可以得到SQL执行时实际的执行计划。
  • 可以得到SQL执行时所花时间的具体分布,CPU消耗了多长时间,多块读消耗了多长时间等等。
  • 可以得到SQL执行时的各种与性能相关的统计数据,逻辑读、物理读、fetch次数、parse次数等等。
  • 不仅能够用于性能测试,同时能够用于诊断正在执行的SQL或存储过程的性能。
  • 有很多的工具用于格式化生成的trace文件,除了Oracle自带的TKPROF、Metalink Note 224270.1 Trace Analyzer,以及第三方的免费工具如orasrp,《Troubleshooting Oracle Performance》作者开发的TVD$XTAT,甚至还有商业化的软件Hotsos Profiler等。

不过前段时间在用10046事件诊断一个性能问题的时候,却让生成的结果误导了。后来仔细检查发现,在会话开启sql trace的情况下,SQL语句会重新解析,导致开启sql trace之后与开启之前相比,执行计划可能发生了变化,导致sql trace的结果不能真实地反映会话执行SQL的情况,在分析时容易发生偏差。

下面是一个测试:

测试的环境是Oracle 10.2.0.1 for Windows,不过前面提到的案例,是发生在Oracle 9i下的,所以9i和10g都有这个问题,而11g目前还没有测试过,有兴趣的朋友可以在11g上进行测试。

首先创建一个sql文件,内容为:

select /*+ testsql */ sum(value) from t1 where flag=:v_flag;

创建一个列上数据有倾斜的表:
SQL> create table t1 (value number ,flag number,pad varchar2(2000));

表已创建。

SQL> insert into t1 select rownum,mod(rownum,2000),lpad('x',1000,'x') from dba_objects;

已创建49796行。

SQL> commit;

提交完成。

SQL> insert into t1 select rownum,3000,lpad('x',1000,'x') from dba_objects where rownum<=10000;

已创建10000行。

SQL> commit;

提交完成。

SQL> create index t1_idx on t1(flag);

索引已创建。

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

PL/SQL 过程已成功完成。
SQL> select column_name,num_distinct,num_buckets from user_tab_columns where table_name='T1';

COLUMN_NAME                    NUM_DISTINCT NUM_BUCKETS
------------------------------ ------------ -----------
VALUE                                                  
FLAG                                   2030          75
PAD         

在创建的测试表中,FLAG列有2001个不同的值,其中,0-1999之间每个值约为25个,而有一个特殊的值3000,有10000个。收集统计信息时,在FLAG列上收集了直方图。

下面运行test.sql:

SQL> var v_flag number;                             
SQL> exec :v_flag:=3000; 
SQL> set autot on stat
SQL> @test
                                                       
SUM(VALUE)
----------
  50005000
统计信息
-------------------------------------------------------
          0  recursive calls
          0  db block gets
       8575  consistent gets
          0  physical reads
          0  redo size
        412  bytes sent via SQL*Net to client
        384  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

我们来看看SQL的执行计划:

Read the rest of this entry

,

其实这是一篇技术文章。

最近比较忙,通宵干活也逐渐平常起来,BLOG更新也少了,其实想写的东西挺多的。

闲话少扯,切入正题。

■ Poor connection management can cause poor response times and unreliable
systems.
----摘自《Oracle Database Performance Tuning Guide 10g Release 2 (10.2)》”Understanding Scalability--Factors Preventing Scalability“一节.

■ Good Database Connection Management
Connecting to the database is an expensive operation that is highly unscalable.
Therefore, the number of concurrent connections to the database should be
minimized as much as possible. A simple system, where a user connects at
application initialization, is ideal. However, in a Web-based or multitiered
application, where application servers are used to multiplex database connections
to users, this can be difficult. With these types of applications, design efforts
should ensure that database connections are pooled and are not reestablished for
each user request.

----摘自《Oracle Database Performance Tuning Guide 10g Release 2 (10.2)》”Application Design Principles--SQL Execution Efficiency“一节.

1. Bad Connection Management
The application connects and disconnects for each database interaction. This
problem is common with stateless middleware in application servers. It has over
two orders of magnitude impact on performance, and is totally unscalable.

----摘自《Oracle Database Performance Tuning Guide 10g Release 2 (10.2)》”The Oracle Performance Improvement Method--Top Ten Mistakes Found in Oracle Systems“一节.

以上的内容,全部是关于连接管理(connection management)的,也就是应用系统连接到数据库的方式,其中之一就是,是使用长连接还是短连接。其实在以前,我看到如上所述的内容,并没有引起重视的,甚至可以说是不以为然。因为现在的使用Oracle数据库的大型的高并发的应用系统,在连接数据库上,一般都是使用了连接池,连接管理基本上都不存在什么问题。

然而事实证明,我错了。就在前不久,遇上一套系统,Oracle数据库的会话数保持在4000以上的高并发系统,一个关键的应用居然用的短连接。不幸的是,这个应用连接数据库的速率非常的快,而创建一个数据库的连接耗时非常的长,闲时都在150ms以上。在业务高峰期,连接数据库的排队已经非常高,Listener已经不能够及时处理连接请求,连接数据库通常需要1s以上,甚至数秒,严重影响了系统的性能。就算使用两个Listener都已经承受不了压力。

解决这个问题的根本办法还是修改应用,使用连接池。

看起来真是“只有想不到,没有做不到”,一切皆有可能啊。

本文将以ODU 3.0.2版的配置文件为例,详细介绍ODU的配置参数,从配置文件也可以体会到ODU的众多功能特点。

ODU默认的配置文件是config.txt,在启动ODU时,会自动打开这个配置文件,当然在进入ODU后,仍然可以通过"load config [config filename]"命令来装入配置文件,这个命令中的config filename(配置文件名)是可选的,如果省略此项,将载入默认的配置文件config.txt。

配置文件是一个纯文本文件,每行为一个配置参数,为“参数名”和“值”,二者均不区分大小写,之间以若干空格分隔。下面是一个配置文件的内容示例:

byte_order   little
block_size    8192
data_path   data
lob_path    /lob
charset_name ZHS16GBK
ncharset_name AL16UTF16
output_format text
lob_storage   infile
clob_byte_order  little
delimiter  |
unload_deleted no

下面将详细介绍每一项配置参数的用法以及作用:

1. BYTE_ORDER

这个参数,指示数据文件平台字节序。可选值为"LITTLE"和"BIG",默认值为“LITTLE”。

这个参数跟ODU运行在哪个平台无关,而只与数据文件的平台有关。比如,数据文件是AIX平台上的,那么这个值就是“BIG",而数据文件是x86平台上的,那么这个值就是”LITTLE“。ODU能够跨平台恢复数据,比如可以将AIX上的Oracle数据文件复制到Windows上,由Windows版本上的ODU进行恢复,反之亦然。只要通过BYTE_ORDER这个参数正确地指示数据文件的平台,即可实现跨平台恢复。

2. BLOCK_SIZE

这个参数设置数据文件缺省的块大小。ODU支持同一数据库具有不同块大小数据文件,如果在ODU的control文件中没有没文件指定块大小(参见《ODU命令详解 PartI》中“open”命令),则使用配置文件中的BLOCK_SIZE指定的大小。这个参数可选的值有2048、4096、8192、16384、32768,默认值是8192。

3. DATA_PATH

DATA_PATH指定恢复的数据所存储的目录,如果需要恢复的数量量非常大,可以用这个参数值指定一个与ODU软件所在目录不同的路径。注意这个参数指定的目录必须是已经存在的,ODU不会自动创建这个目录。
可以使用相对路径,也可以使用绝对路径。默认值为”data“,表示恢复的数据默认放在ODU软件所在目录的data子目录中。

4. LOB_PATH

Read the rest of this entry

,

这次的案例同样是一个省电信的数据库,只不过比《记一个SQL优化案例》中提到的数据库规模要大得多。先简单地介绍一下环境,运行在AIX 5300 TL05上的Oracle 9.2.0.8。系统维护人员发现一个应用的中间件队列全部堵塞。检查数据库的等待事件,发现这个应用连接的会话,基本上都是在等待latch free,latch#为98,很明显是SQL性能出现了问题。因此,检查几个会话正在运行的SQL,都是下面类似的SQL:

select c.acct_id,
       a.serv_id,
       d.cust_code,
       d.cust_id,
       a.acc_nbr,
       c.acct_name,
       c.acct_nbr_97,
       e.name serv_acct_state,
       to_char(b.state_date, 'yyyymmdd') state_date,
       f.name serv_state,
       h.product_name
  from serv a,
       serv_acct b,
       acct c,
       cust d,
       (select domain, name
          from v_domain
         where table_name = 'SERV_ACCT'
           and field_name = 'STATE') e,
       (select domain, name
          from v_domain
         where table_name = 'SERV'
           and field_name = 'STATE') f,
       product h
 where a.serv_id = b.serv_id
   and b.acct_id = c.acct_id
   and a.cust_id = d.cust_id
   and b.state = e.domain
   and a.state = f.domain
   and a.product_id = h.product_id
   and b.state = '10A'
   and c.state = '10A'
   and a.state in ('2HA', '2HC', '2HD', '2HE', '2HH', '2HN', '2HS')
   and a.serv_id in
       (SELECT distinct serv_id
          FROM serv_attr
         WHERE attr_val = '0xx833xxxxx'
           AND attr_id IN (SELECT attr_id
                             FROM a_query_acct_attr
                            WHERE state = 'A0A'
                              and attr_type = 'ACT'))

我隐去了代码中那ATTR_VAL=条件后真实的值,以'0xx833xxxxx'代替。

这个SQL咋一看跟《记一个SQL优化案例》提到的SQL都很相似,想想也能明白,都是一家开发商开发的系统^_^。

我在这条SQL中看到下面这样的代码,我就头痛:

SELECT distinct serv_id
          FROM serv_attr
         WHERE attr_val = '0xx833xxxxx'
           AND attr_id IN (SELECT attr_id
                             FROM a_query_acct_attr
                            WHERE state = ’A0A’
                              and attr_type = ’ACT’)

对于SERV_ATTR这个表,我们可理解为这个表存储了所有用户的属性,每一个用户有多行,每一行有一个ATTR_ID,表示属性ID,也就是表示是什么属性,而ATTR_VAL则是属性的值。这样可以很方便地进行扩展,比如增加属性类型,甚至是自定义属性等。但是这样的设计,虽然具有了很大的灵活性,但不得不说,这样的设计,放在数据库中,基本上违背了关系型数据库的初衷。ATTR_VAL虽然定义为VARCHAR2类型,但实际存储的数据也可能是数值,日期,只不过都以字符串来表示。这样的表,对于Oracle的优化器来说,可以说是一个巨大的挑战。

虽然如此,不得不先接受现实,得想办法让这个SQL正常。第一步当然还是看执行计划了:

Read the rest of this entry

,

接上文《Oracle压缩表数据块格式解析 PartI》,本文将分析压缩表块中实际的行数据部分。

首先我们还是先来看看行数据部分(tab 1)序号为0的行的数据:

tab 1, row 0, @0x1855
tl: 52 fb: --H-FL-- lb: 0x0  cc: 13
col  0: [ 3]  53 59 53
col  1: *NULL*
col  2: [ 5]  56 41 4c 49 44
col  3: [ 1]  4e
col  4: [ 1]  4e
col  5: [ 1]  4e
col  6: [ 2]  c1 62
col  7: [ 5]  54 41 42 4c 45
col  8: [ 7]  78 6d 03 0f 12 2a 38
col  9: [ 7]  78 6d 03 0f 12 2a 38
col 10: [19]  32 30 30 39 2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35
col 11: [ 7]  41 43 43 45 53 53 24
col 12: [ 2]  c1 62
bindmp: 2c 00 0d 06 76 c9 78 79 79 79 ca c1 62 cd 54 41 42 4c 45 77 77 db 32 30 30 39 2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35 cf 41 43 43 45 53 53 24 ca c1 62

bindmp那一行,与符号表中的一样,都是表示这一行数据在数据块中的原始数据,也就是压缩后的数据。我们需要把这个数据解压还原。

bindmp: 2c 00 0d 06 76 c9 78 79 79 79 ca c1 62 cd 54 41 42 4c 45 77 77 db 32 30 30 39
        2d 30 33 2d 31 35 3a 31 37 3a 34 31 3a 35 35 cf 41 43 43 45 53 53 24 ca c1 62

为了便于排版,这里把数据处理后显示为2行。

2c 行标志字节,与普通表数据块一样
00 锁标志,即ITL插槽位置
0d 这个字节表示数据被压缩后的列数,可以理解为后面的数据被分割成了几部分。
06 表示这一行中前面6列数据一定是被压缩的,具体含义在后面可以进一步解析。这一个字节,是网上很多讲压缩表格式时都没有正确地给出其意义的。
接下来的部分,与符号表类型,都是
    标志字节+数据
76 表示数据来自符号表中的序号为118(0x76)的行。
c9 依照解析符号表时的推论,应该表示接紧着后面的数据是实际的列数据,长度为1。但实际上在这里更为复杂。这里也是网上所有关于压缩表块格式分析的文章没有提到的。上面提到过”06 表示这一行中前面6列数据一定是被压缩的“,由于到这列为止,只压缩了1列,因此还没有到6列,那么这1列也是被压缩的,只不过压缩的数据没有在符号表中,而在这个字节紧接着的后面。

我们来看看上面提到的"C9”究竟代表什么?这里就不得不引入了压缩块头部中的fcls_9ir2:

Read the rest of this entry

,

前段时间花了点时间研究了一下Oracle压缩表的数据块格式,并给ODU增加了压缩表支持功能。鉴于目前在网络上基本上没有完整的关于Oracle压缩表数据块的格式分析,我觉得有必要把自己的研究得到的知识,发布出来与大家分享。

由于Oracle 9i与Oracle 10g在压缩块格式上没啥区别,我使用ODU也能够在11g上导出压缩表,所以本文就是Windows上的Oracle 9.2.0.8下的压缩表来进行块格式分析。下面我们用DBA_OBJECTS的数据建一个测试表:

SQL> create table t1 as select * from dba_objects;

表已创建。
SQL> select header_file,header_block from dba_segments where owner=user and segment_name='T1';

HEADER_FILE HEADER_BLOCK
----------- ------------
          8          137
SQL> alter system dump datafile 8 block 138;

系统已更改。

由于T1表所在的表空间是MSSM管理方式的表空间,一般来说紧接着段头后面的数据块就是存储表实际数据的第1个块。所以dump出了datafile 8的138块。

下面从块头往下一直进行解析。

Start dump data blocks tsn: 8 file#: 8 minblk 138 maxblk 138
buffer tsn: 8 rdba: 0x0200008a (8/138)
scn: 0x0000.003d0735 seq: 0x02 flg: 0x04 tail: 0x07350602
frmt: 0x02 chkval: 0xe80a type: 0x06=trans data
Block header dump: 0x0200008a
Object id on Block? Y
seg/obj: 0x1be4 csc: 0x00.3d0735 itc: 3 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0xffff.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.003d0735
0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
0x03   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

这一部分是Oracle块的头部,与其他非压缩表块的头部没什么不同。接下来是表类型(相对于索引类型的数据块)的数据头部:

data_block_dump,data header at 0x3393074
===============
tsiz: 0x1f88
hsiz: 0x280
pbl: 0x03393074
bdba: 0x0200008a
     76543210
flag=-0------  这里“O”标记表示是压缩的块
ntab=2     这里说明块里面有两个“表”的数据,实际上是“符号表”和“实际的表数据”,分别是"tab 0"和"tab 1"
nrow=291
frre=-1
fsbo=0x280
fseo=0x28c
avsp=0xc
tosp=0xc
  r0_9ir2=0x0        这里及下面的数行都是压缩表特有的数据
  mec_kdbh9ir2=0x3  这个字段后面有详细说明
  r1_9ir2=0x0
            76543210
  flag_9ir2=------OC
    fcls_9ir2[7]={ 0 32768 32768 32768 32768 32768 32768 }  这个字段后面有详细说明
    perm_9ir2[13]={ 0 11 1 12 6 7 8 9 10 2 3 4 5 }  这个字段后面有详细说明
0x32:pti[0] nrow=122 offs=0
0x36:pti[1] nrow=169 offs=122
0x3a:pri[0] offs=0x189f
0x3c:pri[1] offs=0x1890
0x3e:pri[2] offs=0x1889
0x40:pri[3] offs=0x18ae
0x42:pri[4] offs=0x18c4
0x44:pri[5] offs=0x18ce

行目录数据比较多,在此大部分省略。接下来,就是tab 0,也就是符号表的数据了:

Read the rest of this entry

,

今天是5月12日,去年今日,因汶川大地震,举国悲伤。

我们希望不要沉浸在悲伤的过去,只是希望,逝者安息,生者更坚强,去创造美好的明天。祝愿灾区的小朋友能够拥有美好的未来;希望为灾区无私奉献的志愿者们,以及参与援建灾区的人们,好人一生平安。

虽然10g及以上版本的Oracle数据库,提供了recyclebin(回收站)功能,可以找回被drop的表。但是仍然存在着很多8i、9i的库以及没有开启recyclebin功能、drop时直接purge操作等,这样的情况下,如果想找回被意外drop的表,常规的手段是通过备份来恢复。如果没有备份,那就没有办法来恢复了。不过ODU提供了一个可能,在没有备份的情况下,恢复被drop表的数据。

下面通过一个示例来演示如何使用ODU来恢复被drop的表。
首先创建一个测试表:

SQL> create table odu_test ( a number,b varchar2(10),c nvarchar2(30),d varchar2(20),e date,f timestamp,g binary_float,h binary_double);

Table created.

SQL> insert into odu_test select rownum,lpad('x',10),'NC测试' || rownum, 'ZHS测试'|| rownum,sysdate+dbms_random.value(0,100),systimestamp+dbms_random.value(0,100),rownum+dbms_random.value(0,10000),rownum+dbms_random.value(0,10000) from dba_objects where rownum<=10000;

10000 rows created.

SQL> commit;

Commit complete.

SQL> create table t1 as select * from odu_test;

Table created.
SQL> drop table odu_test purge;

Table dropped.

在发现重要的表被意外drop掉的时候,应该立即停止应用,offline那个表所在的表空间或关闭数据库。这里odu_test表是建在users表空间下,先将users表空间offline:

SQL> alter tablespace users offline;

Tablespace altered.

然后需要使用logminer来查找被drop表的data object id:

SQL> select group#,status from v$log;

    GROUP# STATUS
---------- ----------------
         1 INACTIVE
         2 INACTIVE
         3 CURRENT

SQL> col member for a50
SQL> select member from v$logfile where group#=3;

MEMBER
--------------------------------------------------
/u01/app/oracle/oradata/xty/redo03.log

SQL> exec sys.dbms_logmnr.add_logfile(logfilename=>'/u01/app/oracle/oradata/xty/redo03.log');

PL/SQL procedure successfully completed.

SQL> exec sys.dbms_logmnr.start_logmnr(options=>sys.dbms_logmnr.dict_from_online_catalog);

PL/SQL procedure successfully completed.
SQL> select scn,timestamp,sql_redo from v$logmnr_contents where operation='DDL' and sql_redo like '%odu_test%' order by 2 ;

SCN TIMESTAMP SQL_REDO
---------- ------------------- ----------------------------------------------------------------------
    681455 2009-05-08 11:20:50 create table odu_test ( a number,b varchar2(10),c nvarchar2(30),d varc
                               har2(20),e date,f timestamp,g binary_float,h binary_double);

    681521 2009-05-08 11:21:17 create table t1 as select * from odu_test;
    681567 2009-05-08 11:21:34 drop table odu_test purge;

SQL> select scn,timestamp,sql_redo from v$logmnr_contents where timestamp=to_date('2009-05-08 11:21:34','yyyy-mm-dd hh24:mi:ss') order by 1;

       SCN  SQL_REDO
----------  ----------------------------------------------------------------------
    681566  set transaction read write;
    681567  drop table odu_test purge;
    681569  Unsupported
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570 
    681570 
    681570 
    681570  Unsupported
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681570  Unsupported
    681570 
    681570 
    681570 
    681571  Unsupported
    681572 
    681572  delete from "SYS"."OBJ$" where "OBJ#" = '52230' and "DATAOBJ#" = '5223
            0' and "OWNER#" = '57' and "NAME" = 'ODU_TEST' and "NAMESPACE" = '1' a
            nd "SUBNAME" IS NULL and "TYPE#" = '2' and "CTIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "MTIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "STIME" = TO_DATE('2009-05-
            08 11:20:46', 'yyyy-mm-dd hh24:mi:ss') and "STATUS" = '1' and "REMOTEO
            WNER" IS NULL and "LINKNAME" IS NULL and "FLAGS" = '0' and "OID$" IS N
            ULL and "SPARE1" = '6' and "SPARE2" = '1' and "SPARE3" IS NULL and "SP
            ARE4" IS NULL and "SPARE5" IS NULL and "SPARE6" IS NULL and ROWID = 'A
            AAAASAABAAAMzdAAS';

    681572 
    681573  commit;
    681574  set transaction read write;
    681574  Unsupported
    681576  commit;
    681577  set transaction read write;
    681579  Unsupported
    681581  commit;

SQL> exec sys.dbms_logmnr.end_logmnr;

PL/SQL procedure successfully completed.

从SCN为681572的几行中,delete from ”SYS”.”OBJ$” where ”OBJ#” = ’52230′ and ”DATAOBJ#” = ’52230′ 可以看到被drop表的data object id为52230。

下面我们使用ODU来恢复这个被删除的表:

Read the rest of this entry

,

其实这篇文章,我也不知道用什么标题为好。只是对今天发生的一个案例的思考。

今天是6号,是电信每个新帐期收费的第1天(可能各地有所差异)。某个省电信的收费系统Oracle数据库不堪压力,前台已经不能正常进行收费。而我登上数据库主机时,发现CPU使用率已经达到100%,90%以上的CPU使用率是user模式。这个主机有40个CPU(按逻辑CPU算是80个),而运行队列一直在130以上。

由于我之前没有接触过这套系统,对系统的能力,平时系统的状况都不是很了解。没有发现明显消耗CPU的进程,检查等待事件、以及使用statspack做了个10分钟左右的报告,没有发现特别的异常。唯一的异常是,活动会话太多。

为了解决此问题,客户立即给主机增加了16个CPU(这些CPU本来就在主机上,只是没有划给系统使用),同时重启了应用中间件。这个问题就这样暂时解决了,“火被扑灭了”。

下面分析一下今天这个问题:

大家应该都会有一个共识,应用服务器是很容易水平(横向)扩展的,一台应用服务器不够,可以再增加一台,对于很多应用,应用服务器几乎具有无限制的水平扩展能力。所以一个系统,其甁颈往往出现在数据库。RAC数据库几乎不能够线性扩展,比如两个节点的RAC,其处理能力达到单个节点库的1.6倍已经算是不错了。同时数据库往往受限于IO子系统的能力极限,扩展能力有限。因此,通过增加应用服务器,应用服务器可以接受无限制的请求,但是数据库的处理能力则是有限制的。

回到今天这个问题上,由于今天是业务高峰期,数据库接收到大量来自于应用服务器的请求,达到了某一个临界值以后,系统资源效率下降,比如,IOPS达到一定程度,IO响应时间大幅下降(当然这个系统IO子系统还没表现明显的瓶颈),CPU效率也会下降,同一个事务,以前只需要消耗CPU时间1s的,现在可能需要1.1s,一些等待也会加剧。这样应用服务器的请求队列越来越长,导致数据库的请求也越来越多,活动会话数越来越高。数据库处理的速度已经跟不上请求的速度了。CPU也就长时间保持在100%的使用率,也不足为奇了。

重启应用中间件,就强制中止了所有的请求,消除了排队,避免了给数据库增加更多的压力。重启应用中间件以及增加CPU(增加处理能力)之后,系统就恢复了正常。不过之后一段时间的观察,CPU使用率按之前40个CPU折算,也是在85%左右,也是一个比较高的值。

经过后面一段时间分析,数据库中也存在一些性能问题,存在着较大的优化余地。但是,对于这样一个成熟的庞大的系统来说,不要期望像像网上很多文章介绍的那样,优化极个别的SQL就能解决问题。存在性能不够优化的SQL很多(注意这里说的不够优化,并不是说存在严重的性能问题),并且反映出来的问题很多,涉及面相当广,比如有些表的统计信息缺失,有些SQL写法有问题,有些是索引使用不够合理,执行计划经常变动,不够稳定、有些表有碎片等等。解决这样的系统的性能问题,非一日之功。

所以今天对这个案例,让我思考的是另一方面,对于容量规划。某些系统,像电信的收费(销帐)系统,在业务高峰期,其交易量可以达到平时交易量的2-4倍(只是个粗略估计)。对于这样的系统,我们是不是应该让系统保证足够的处理能力?特别是CPU以及IO子系统能力。对于RAC数据库来说,对每个节点,是不是应该让系统足够在另一个节点DOWN掉的情况下能够支持所有的业务量,如果不能,那么RAC的高可用性就不能得到保证,一个节点DOWN掉以后,其他节点,如果不能承受DOWN掉节点转移过来的压力,也将会不堪重负而垮掉。

同时随着业务量的增加和数据量的增长,系统的压力也会越来越大。因此容量规划也需要考虑这个因素。

对于今天这样的一个系统,如果需要系统能够支撑业务高峰期的压力,通过优化系统,使之在业务低谷期,CPU的利用率应该控制在30%以下,否则今天这样的事情难免会重演。

一篇杂乱的文章,没有详细讨论容量规划,这是一个很大的话题,欢迎讨论。