2

既存の Oracle BLOB をファイルに移行する必要があるプロジェクトに取り組んでいます。読み取り元の環境は、共有の Oracle 10gR2 サーバーです。現在、UTL_FILE を使用するスクリプトがあります。ただし、プロセスはかなり遅いです。25 GB のサンプル データを抽出するには、約 3 時間かかります。実際に移動するデータは 1 TB 程度です。これを大幅に調整するには、ヘルプ/アドバイスが必要です。

これが私のプロセスです:

  1. カーソルを開いて BLOB の ID と名前のリストを取得します
  2. 各ブロブを通過するループを開始します
  3. カスタム ストアド プロシージャである BLOB2FILE を使用して BLOB を抽出します (Web サイトから取得し、わずかに変更しました)。

コードは次のとおりです。

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 は、テーブルへのストアド プロシージャ ログです。そこに大きな打撃はないはずです。通常の FETCH の代わりに BULK FETCH を使用して、ステップ 1 の最適化を試みました。しかし、それは重要な結果をもたらしませんでした。

誰かが改善のためのアイデアを提案できますか?

4

1 に答える 1

2

ハードウェアが 1 時間あたり 8 GB をはるかに超える継続的な書き込みを処理するのに十分であると仮定するとsDir(さらに、システムからの同様の量の読み取りを処理しblobTable、システムが必要とする他の I/O を処理するのに十分)、最も単純なオプションは spawn することです。それぞれがこのプロシージャを呼び出しているいくつかの並列セッション。たとえば、それぞれが 1 つの LOB を抽出する 3 つのジョブを並行して実行する場合は、次のようにすることができます。

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 に答える