2

我正在开发一个需要将现有 Oracle blob 迁移到文件中的项目。要读取的环境是共享的 Oracle 10gR2 服务器。目前我有一个使用 UTL_FILE 的脚本。但是,这个过程非常缓慢。提取 25 GB 的样本数据大约需要 3 小时。要移动的实际数据大约为 1 TB。我需要帮助/建议来显着调整这一点。

这是我的过程:

  1. 打开游标以获取 blob id 和名称列表
  2. 开始循环遍历每个 blob
  3. 使用自定义存储过程 BLOB2FILE 提取 blob(从网站上获取并稍作修改)

这是代码:

create or replace
PROCEDURE BLOB2File(
    lngBlobID IN NUMBER,
    sFileName IN VARCHAR2,
    sDir      IN VARCHAR2)
AS
  iFileLen INTEGER;
  iLineLen INTEGER := 32000; -- max line size for utl_file
  vStart   NUMBER  := 1;
  vBlob BLOB;
  l_output utl_file.file_type;
  my_vr RAW(32000);
  iTmp INTEGER;
BEGIN
  -- get blob details
  LOG_IT('Entered. Blob Id: ' || lngBlobID || ', File Name: ' || sFileName || ', Directory: ' || sDir);
  SELECT blobData,
    lengthb(blobData)
  INTO vBlob,
    iFileLen
  FROM blobTable
  WHERE id = lngBlobID;
  LOG_IT('Acquired the blob. Blob size: ' || TO_CHAR(iFileLen));
  l_output := utl_file.fopen(sDir, sFileName,'wb', iLineLen);
  vStart   := 1;
  iTmp     := iFileLen;
  -- if small enough for a single write
  IF iFileLen < iLineLen THEN
    utl_file.put_raw(l_output,vBlob);
    utl_file.fflush(l_output);
  ELSE -- write in pieces
    vStart      := 1;
    WHILE vStart < iFileLen AND iLineLen > 0
    LOOP
      dbms_lob.read(vBlob,iLineLen,vStart,my_vr);
      utl_file.put_raw(l_output,my_vr);
      utl_file.fflush(l_output);
      -- set the start position for the next cut
      vStart := vStart + iLineLen;
      -- set the end position if less than 32000 bytes
      iTmp       := iTmp - iLineLen;
      IF iTmp     < iLineLen THEN
        iLineLen := iTmp;
      END IF;
    END LOOP;
  END IF;
  utl_file.fclose(l_output);
  LOG_IT('Exited');

  EXCEPTION
  WHEN OTHERS THEN
  LOG_IT('**ERROR** ' || SQLERRM, SQLCODE, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;

LOG_IT 是记录到表的存储过程。那里不应该有任何重大打击。我尝试通过使用 BULK FETCH 而不是普通的 FETCH 来优化第 1 步。然而,它并没有产生任何显着的结果。

任何人都可以提出任何改进的想法,或者更好的方法来解决这个问题吗?

4

1 回答 1

2

假设您的硬件足以处理超过 8 GB/小时的持续写入sDir(并处理从类似数量的读取blobTable和处理系统需要的任何其他 I/O),最简单的选择可能是生成几个并行会话,每个会话都调用此过程。例如,如果您想并行运行三个作业,每个作业都提取一个 LOB,您可以执行类似的操作。

DECLARE
  l_jobno INTEGER;
BEGIN
  dbms_job.submit( l_jobno, 'begin BLOB2File( 1, ''1.lob'', ''DIRECTORY'' ); end;', sysdate + interval '5' second );
  dbms_job.submit( l_jobno, 'begin BLOB2File( 2, ''2.lob'', ''DIRECTORY'' ); end;', sysdate + interval '5' second );
  dbms_job.submit( l_jobno, 'begin BLOB2File( 3, ''3.lob'', ''DIRECTORY'' ); end;', sysdate + interval '5' second );
  commit;
END;

实际上,您可能不希望为每个 BLOB 拥有一个单独的线程——您可能希望生成较少数量的作业并为每个作业分配一系列lngBlobID值以进行处理。Oracle 将在任何时候运行的作业数量受JOB_QUEUE_PROCESSES参数限制,因此您可以提交数千个作业,并让 Oracle 限制它同时运行的数量。

于 2012-05-04T21:22:53.977 回答