当Web端查询某些特殊记录时Oracle就死掉[转]

一个系统最近频频死掉,症状是系统的CPU资源全部耗尽,系统环境如下:

双CPU、RAID5
Oracle 8.1.7.0.1
Weblogic 8.12
JAVA JDBC
FT的是Oracle和weblogic分别跑在RedHat 7.3上!也不知道是怎么想的!

字符集是ZHS16GBK

系统每隔一段时间就会出现CPU被99%占用,两个CPU空闲全为0,同时v$session_wait中的事件都是SQL*Net more data to client或SQL*Net message from client,alert.log中一切正常,StatsPack Report也没有发现任何的不正常,唯独SQL*Net事件等待次数非常的多!最初以为是不是内网的交换机出问题了,网络原因导致数据库问题?

后来经分析发现,当在Web客户端查询某个表的特定记录时,CPU占用率直线上升,而Web却查不到这个记录,但是这个记录在数据库表里的确存在!如果只有一个Web在查这个记录一个CPU被耗尽,如果开了多个Web查这个记录比如开5个,则这5个server process平均每个占20%左右CPU,同时两个CPU被耗尽,系统挂掉!但是在SQLPLUS里查询该记录没有任何的不正常!


针对一个号码的测试如下(X为隐藏部分):


现在在Web客户端查询130XXXXXXXX这个号码,然后TOP里看到:

  1:22pm  up 1 day, 10:15,  4 users,  load average: 0.98, 0.50, 0.33
106 processes: 104 sleeping, 2 running, 0 zombie, 0 stopped
CPU0 states: 15.0% user,  6.0% system,  0.0% nice, 78.4% idle
CPU1 states: 90.3% user,  1.4% system,  0.0% nice,  7.2% idle
Mem:  1031044K av, 1021448K used,    9596K free,       0K shrd,   46060K buff
Swap: 2097112K av,   37424K used, 2059688K free                  860096K cached
  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 5309 oracle    25   0  8832 8832  8252 R    99.0  0.8   0:59 oracle
 3026 oracle    16   0  138M 138M  137M S     2.9 13.7  27:41 oracle
 3028 oracle    16   0  144M 144M  143M S     2.5 14.3  28:59 oracle
 3030 oracle    16   0  129M 129M  129M S     2.1 12.8  23:25 oracle
 5193 oracle    15   0  1092 1092   856 S     2.1  0.1   1:39 top
 5310 oracle    15   0  1092 1092   856 R     2.1  0.1   0:06 top
 1234 oracle    15   0  136M 136M  135M S     0.3 13.5   1:27 oracle
 3020 oracle    15   0 26004  25M 25380 S     0.3  2.5   3:48 oracle
 1199 oracle    15   0 11788  11M 11256 S     0.1  1.1   0:13 oracle
 1219 oracle    15   0  161M 161M  159M S     0.1 16.0   0:47 oracle
    1 root      15   0   476  432   416 S     0.0  0.0   0:05 init
    2 root      15   0     0    0     0 SW    0.0  0.0   0:00 keventd
    3 root      34  19     0    0     0 SWN   0.0  0.0   0:00 ksoftirqd_CPU0
    4 root      34  19     0    0     0 SWN   0.0  0.0   0:00 ksoftirqd_CPU1
    5 root      15   0     0    0     0 SW    0.0  0.0   0:06 kswapd
    6 root      25   0     0    0     0 SW    0.0  0.0   0:00 bdflush
    7 root      15   0     0    0     0 SW    0.0  0.0   0:00 kupdated
    8 root      25   0     0    0     0 SW    0.0  0.0   0:00 mdrecoveryd
   16 root      15   0     0    0     0 SW    0.0  0.0   0:28 kjournald
   95 root      16   0     0    0     0 SW    0.0  0.0   0:00 khubd


此时:
SQL> select a.sid,status from v$session a,v$process b where a.paddr=b.addr and b.spid=5309;

       SID STATUS
---------- --------
        29 ACTIVE

SQL> select sid,event,wait_time,seconds_in_wait,state from v$session_wait where sid=29;

       SID EVENT                         WAIT_TIME  SECONDS_IN_WAIT STATE
---------- ----------------------------- ---------- --------------- -------------------
        29 SQL*Net more data to client           -1              56 WAITED SHORT TIME

在SQLPLUS中:
SQL> select rowid,tel,name from teluser_tbl where tel='130XXXXXXXX';

ROWID              TEL                  NAME
------------------ -------------------- ---------------------
AAAGHgAAFAAAU27AAA 130XXXXXXXX          某某华南工程有限公

显然这里"公"后面少了"司"

用PL/SQL Developer的"SQL窗口"查询,"司"是一个乱码:
TEL                  NAME
-------------------- ---------------------
130XXXXXXXX          某某华南工程有限公¢

用PL/SQL Developer的"命令窗口"查询,"公"后面是个"?"
 
问题就出在这个NAME字段上!如果把"公"后面的乱码删掉就没有任何问题了!系统就一切正常了!

我用dbms_system.set_sql_trace_in_session来跟踪PID=5309的session不会生成任何trace文件,也就是他的SQL已经执行完了,正在给Web返回数据,而Web始终接收不到这个数据,server process却悬在这里并且占用着大量的CPU资源。

我开7个IE窗口查询同样的手机号码,TOP如下:

  7:46pm  up 16:40,  4 users,  load average: 7.81, 7.77, 7.62
105 processes: 96 sleeping, 9 running, 0 zombie, 0 stopped
CPU0 states: 96.4% user,  3.1% system,  0.0% nice,  0.0% idle
CPU1 states: 99.0% user,  1.0% system,  0.0% nice,  0.0% idle
Mem:  1031044K av, 1021452K used,    9592K free,       0K shrd,   36812K buff
Swap: 2097112K av,   42588K used, 2054524K free                  881416K cached
  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 3438 oracle    25   0  9592 9592  8988 R    32.7  0.9  21:29 oracle
 3431 oracle    25   0 11900  11M 11300 R    32.3  1.1  21:57 oracle
 3436 oracle    25   0  9136 9136  8552 R    30.9  0.8  20:51 oracle
 3454 oracle    25   0 11916  11M 11260 R    24.5  1.1  14:39 oracle
 3433 oracle    25   0 25028  24M 24376 R    23.5  2.4  16:06 oracle
 3442 oracle    25   0  9092 9092  8508 R    23.5  0.8  15:51 oracle
 3452 oracle    25   0  9056 9056  8472 R    23.5  0.8  14:36 oracle

这七个进程的EVENT全部是:SQL*Net more data to client
而WEB端不会有任何现在,也就是数据根本无法传递给WEB!!!

相应的SQL很简单,执行计划很好,成本也很低,我用TOP-PID+v$session、v$process、v$sqlarea抓到SQL,执行计划如下:

select rowid, t.tel,t.password,t.language,t.isfree,t.status,
t.enddate,t.endprocess,t.province,t.city,t.code,t.name,
t.address,t.postcode,t.companytel,t.familytel,t.fax,t.qq,
t.email,t.regdate,t.unregdate,t.logincount
from teluser_tbl t where t.tel ='133XXXXXXXX'

Elapsed: 00:00:00.45
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TELUSER_TBL'
   2    1     INDEX (UNIQUE SCAN) OF 'SYS_C001736' (UNIQUE)

 


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1628  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

显然这是个不错执行计划。

把这个记录的block dump出来:

SQL> select file_id from dba_extents where segment_name='TELUSER_TBL';

FILE_ID
---------------
      5

SQL> set serveroutput on
SQL> declare
  2  a number;
  3  begin
  4  a:=dbms_rowid.rowid_block_number('AAAGHgAAFAAAU27AAA');
  5  dbms_output.put_line(a);
  6  end;
  7  /
85435

PL/SQL 过程已成功完成。

SQL> declare
  2  a number;
  3  begin
  4  a:=dbms_rowid.rowid_row_number('AAAGHgAAFAAAU27AAA');
  5  dbms_output.put_line(a);
  6  end;
  7  /
0

PL/SQL 过程已成功完成。

SQL> alter system dump datafile 5 block 85435;

System altered.

TRACE文件如下(只贴row 0的):

[oracle@sxdb udump]$ less ora_5301.trc
/opt/ora/admin/sxit/udump/ora_5301.trc
Oracle8i Enterprise Edition Release 8.1.7.0.1 - Production
With the Partitioning option
JServer Release 8.1.7.0.1 - Production
ORACLE_HOME = /opt/ora/8.1.7
System name:    Linux
Node name:      sxdb
Release:        2.4.18-3smp
Version:        #1 SMP Thu Apr 18 07:27:31 EDT 2002
Machine:        i686
Instance name: sxit
Redo thread mounted by this instance: 1
Oracle process number: 45
Unix process pid: 5301, image: oracle@sxdb (TNS V1-V3)

*** 2005-01-20 13:11:49.539
*** SESSION ID:(19.25087) 2005-01-20 13:11:49.538
Start dump data blocks tsn: 4 file#: 5 minblk 85435 maxblk 85435
buffer tsn: 4 rdba: 0x01414dbb (5/85435)
scn: 0x0000.058b81ce seq: 0x01 : 0x02 tail: 0x81ce0601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
 
Block header dump:  0x01414dbb
 Object id on Block? Y
 seg/obj: 0x61e0  csc: 0x00.58b81cd  itc: 1  : -  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   xid:  0x0003.031.0000aa4c    uba: 0x00c003de.10f4.48  --U-    1  fsc 0x0000.058b81ce
 
data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x58
pbl: 0x09724644
bdba: 0x01414dbb
flag=-----------
ntab=1
nrow=35
frre=-1
fsbo=0x58
fseo=0x312
avsp=0x389
tosp=0x389
0xe:pti[0]      nrow=35 offs=0
0x12:pri[0]     offs=0x1edf
0x14:pri[1]     offs=0x1e13
0x16:pri[2]     offs=0x1d47
0x18:pri[3]     offs=0x1c75
0x1a:pri[4]     offs=0x1ba9
0x1c:pri[5]     offs=0x1add
0x1e:pri[6]     offs=0x1a11
0x20:pri[7]     offs=0x1945
0x22:pri[8]     offs=0x1879
0x24:pri[9]     offs=0x17ad
0x26:pri[10]    offs=0x16e2
0x28:pri[11]    offs=0x1617
0x2a:pri[12]    offs=0x154c
0x2c:pri[13]    offs=0x1481
0x2e:pri[14]    offs=0x13b6
0x30:pri[15]    offs=0x12eb
0x32:pri[16]    offs=0x1220
0x34:pri[17]    offs=0x1155
0x36:pri[18]    offs=0x108c
0x38:pri[19]    offs=0xfc1
0x3a:pri[20]    offs=0xef6
0x3c:pri[21]    offs=0xe2d
0x3e:pri[22]    offs=0xd62
0x40:pri[23]    offs=0xc99
0x42:pri[24]    offs=0xbce
0x44:pri[25]    offs=0xb05
0x46:pri[26]    offs=0xa39
0x48:pri[27]    offs=0x96d
0x4a:pri[28]    offs=0x8a5
0x4c:pri[29]    offs=0x7d9
0x4e:pri[30]    offs=0x70d
0x50:pri[31]    offs=0x645
0x52:pri[32]    offs=0x57d
0x54:pri[33]    offs=0x3e2
0x56:pri[34]    offs=0x312
block_row_dump:
tab 0, row 0, @0x1edf
tl: 217 fb: --H-FL-- lb: 0x0 cc: 50
col  0: [11]  31 33 30 3X 3X 3X 3X 3X 3X 3X 3X   (X-是我隐藏的,避免泄露客户信息)
col  1: [ 6]  32 30 30 35 30 31
col  2: [ 4]  b9 e3 b6 ab
col  3: [ 4]  b7 f0 c9 bd
col  4: [ 4]  30 37 36 35
col  5: [19]  c2 b7 c7 c5 bb aa c4 cf b9 a4 b3 cc d3 d0 cf de b9 ab cb ——这就是NAME字段一共存储了19个字节,这行记录的NAME是"某某华南工程有限公"一共9个字符,显然数据库多存储了一个字节!而这个字节造成了乱码!
col  6: *NULL*
col  7: *NULL*
col  8: *NULL*
col  9: *NULL*
col 10: *NULL*
col 11: *NULL*
col 12: *NULL*
col 13: [14]  32 30 30 35 30 31 30 35 31 37 31 34 33 32
col 14: [14]  32 30 30 35 30 31 30 35 31 37 31 34 33 32
col 15: [ 2]  c1 02
col 16: [ 1]  80
col 17: [ 1]  80
col 18: [ 2]  c1 02
col 19: [14]  32 30 30 35 30 31 30 35 31 37 31 34 33 32
col 20: [14]  32 30 30 35 30 31 30 35 31 37 31 34 33 32
col 21: [ 1]  80
col 22: [ 1]  80
col 23: [ 2]  c1 04
col 24: [ 1]  80
col 25: [ 2]  c1 06
col 26: [ 2]  c1 02
col 27: [ 4]  30 30 30 30
col 28: [ 1]  80
col 29: *NULL*
col 30: [14]  32 30 30 35 30 31 30 35 31 37 31 34 33 32
col 31: [ 1]  80
col 32: *NULL*
col 33: [ 1]  80
col 34: [ 1]  80
col 35: *NULL*
col 36: *NULL*
col 37: *NULL*
col 38: *NULL*
col 39: *NULL*
col 40: *NULL*
col 41: [ 1]  80
col 42: [ 1]  80
col 43: [ 1]  80
col 44: [ 1]  80
col 45: [ 1]  80
col 46: [ 1]  80
col 47: [ 1]  80
col 48: [14]  32 30 30 35 30 33 30 37 31 37 31 34 33 32
col 49: [ 2]  c1 0c

由于这个字段不是很重要,最后用update teluser_tbl set name=rtrim(name,substr(name,-1));节断了最右端的一个字符,系统恢复正常。

后来开发人员详细检查程序,发现C语言在读入数据的时候少读了一个字节!

那为什么这个字节会造成如此严重的问题呢,目前唯一的解释是ZHS16GBK字符集的Bug,这个字节在ZHS16GBK字符集中对应的字符与OS底层通讯代码冲突,造成通讯故障,从而引发数据库故障。

先到这里,等有空再把这个问题详细研究一下。

posted on 2008-08-13 14:59  一江水  阅读(1078)  评论(1编辑  收藏  举报