HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

Replace 函数操作CLOB导致临时表空间泄露

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2009-02-08 01:12:33

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

 

生产系统抛ORA-01652错误,找到跑错的程序,发现是下面一段代码(这里只是演示代码,不是原代码)抛出的:

 

package_body ppp
  aaa clob;
...
procedure change_content()
begin
。。。
  aaa := replace(aaa, 'b', ';;');
。。。
end;

procedure call_f()
begin
  aaa := empty_clob();
  aaa := 'aaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaabaaaaaaaaaaaaa';
  for i in 1..10000 loop
change_content();
  end loop;
end;

 

从逻辑上看,这段代码可以用以下代码进行模拟,

 

declare
  aaa clob;
begin
  aaa := empty_clob();
  aaa := 'aaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaabaaaaaaaaaaaaa';
  for i in 1..10000 loop
    aaa := replace(aaa, 'b', ';;');
  end loop;
end;

 

我们知道,在PLSQL中的LOB类型变量是占用临时表空间的。但是,从以上代码看,CLOB变量aaa的初始值并没有占用太大空间。我们在测试环境上运行该语句,用以下语句观察其临时表空间占用情况。

 

SELECT b.tablespace,
       ROUND(((b.blocks*p.value)/1024/1024),2)||'M' "SIZE",
       b.CONTENTS,
       s.sql_text,
       a.sid||','||a.serial# SID_SERIAL,
       a.username,
       a.program,
       sysdate as log_date
  FROM sys.v_$session a,
       sys.v_$sort_usage b,
       sys.v_$parameter p,
       v$sqlarea s
 WHERE p.name  = 'db_block_size'
   AND a.saddr = b.session_addr
   AND a.sql_address = s.address and a.sql_hash_value = s.hash_value
ORDER BY b.tablespace, b.blocks;
 
TABLESPACE       SIZE     CONTENTS SQL_TEXT SID_SERIAL       USERNAME PROGRAM  LOG_DATE
---------------------------------------------------------------------------------
TEMP     37M      TEMPORARY        declare   aaa clob; begin   aaa := empty_clob();   aaa := 'aaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaabaaaaaaaaaaaaa';   for i in 1..1000000000 loop     aaa := replace(aaa, 'a', ';;');   end loop; end;          137,28675        DEMO     plsqldev.exe     8/15/2007 11:42:11 AM

 

发现临时表空间在不停增长。问题就在于aaa := replace(aaa, 'b', ';;').Replace()函数会先将传入的CLOB参数变量在temp空间上保存一份,然后再转换为varchar2类型进行操作。这样,每次进行了replace操作后,就多占据了一份temp空间。

 

我们可以通过包dbms_lob中提供的copyinstrwrite函数来自己编写一个clob的字符替换函数,来避免temp空间的泄露。以下存储过程实现了CLOB中的字符串替换。

 

create or replace procedure lob_replace( p_lob in out clob,
                       p_what in varchar2,
                       p_with in varchar2 )
as
    n    number;
    len  number;
begin
    n := dbms_lob.instr( p_lob, p_what );
    while ( nvl(n,0) > 0 ) loop
        len := dbms_lob.getlength(p_lob);
        if (n+length(p_with)-1 > len)
        then
            dbms_lob.writeappend( p_lob, n+length(p_with)-1 - len, p_with );
        end if;
       
        if (len-n-length(p_what)+1 > 0)
        then
            dbms_lob.copy( p_lob,
                           p_lob,
                           len-n-length(p_what)+1,
                           n+length(p_with),
                           n+length(p_what) );
        end if;

        dbms_lob.write( p_lob, length(p_with), n, p_with );
       
        if ( length(p_what) > length(p_with) )
        then
            dbms_lob.trim( p_lob,
               dbms_lob.getlength(p_lob)-(length(p_what)-length(p_with)) );
        end if;
        n := dbms_lob.instr( p_lob, p_what );
    end loop;
end;
 

 

用以上过程代替replace函数,再次运行程序,用以上语句观察,不再存在临时空间泄露。

 

 

Top

Copyright ©2005,HelloDBA.Com 保留一切权利

申明
by fuyuncat