2

Oracle 11g (11.2.0.3) システムから BLOB をエクスポートする必要があります。このプロセスは、32,767 バイト未満の BLOB (JPG 写真) に最適です。dbms_log.read & utl_file.put_raw を使用して、5 秒以内に ~4000 枚の写真をデータベース サーバーのローカル ディレクトリにエクスポートできます。ファイルが読み取りバッファーの 32,767 バイトの制限を超えている場合、ここからパフォーマンスの問題が発生します。正確なパフォーマンスの問題について同様の投稿を見たことがありますが、提供された解決策はすでに調査されていますが、成功していません。監視ツールに基づいて、CPU、I/O、およびメモリは、エクスポート中に圧迫されていません。32,767 バイト単位でつなぎ合わせる必要がある大きな BLOB (すべてのサイズが 100K 未満) のエクスポートが、32,767 バイト未満の BLOB と比較して非常に遅い理由を理解しようとしています。

遅いブロブ抽出の関連記事

BLOBエクスポートチューニングの関連記事

32,767 バイトを超えるファイルで BLOB エクスポートの速度が低下したことを経験した人はいますか?

DECLARE

  CURSOR cur_photo IS
    select  substr(c.custnum, -7, length(c.custnum)) custnum,
            cp.cust_id,
            cp.photo 
    from customer c
    inner join customer_photo cp
      on c.cust_id = cp.cust_id
    inner join customer_def_grp_value cdv
      on c.cust_id = cdv.cust_id;

  select_sql varchar2(225);
  l_file      UTL_FILE.FILE_TYPE;
  l_buffer    RAW(32767);
  l_amount    PLS_INTEGER := 32767;
  l_pos       PLS_INTEGER := 1;
  l_blob      BLOB;
  l_blob_len  PLS_INTEGER;
  l_filename  varchar2(225);
  error_number varchar2(225);
  error_message varchar2(225);

BEGIN
  --dbms_output.put_line('Starting at: ' || to_char(systimestamp, 'DD-MON-YYYY HH:MI:SS.FF6'));
  --DBMS_OUTPUT.ENABLE (buffer_size => NULL); 
  FOR custphoto IN cur_photo LOOP
    --dbms_output.put_line('In the loop ' || custphoto.cust_id);

    select_sql := 'SELECT photo FROM customer_photo WHERE cust_id = :cust_id';
    --dbms_output.put_line('Statement: ' || select_sql);

    EXECUTE IMMEDIATE select_sql INTO l_blob using custphoto.cust_id;

    l_blob_len := DBMS_LOB.getlength(l_blob);
    --dbms_output.put_line('BLOB length: ' || l_blob_len);

    -- Set the filename
    l_filename := custphoto.custnum || '.jpg';
    --dbms_output.put_line('Filename: ' || l_filename);

    -- Open the destination file.
    l_file := UTL_FILE.fopen('jpeg', l_filename, 'wb', 32767);

    --dbms_output.put_line('Start Export at: ' || to_char(systimestamp, 'DD-MON-YYYY HH:MI:SS.FF6'));

    IF l_blob_len < 32767 then
      --dbms_output.put_line('BLOB < 32767 bytes');
      DBMS_LOB.read(l_blob, l_blob_len, l_pos, l_buffer);
      UTL_FILE.put_raw(l_file, l_buffer, TRUE);
    ELSE -- write in pieces
      --dbms_output.put_line('BLOB >= 32767 bytes');
      WHILE l_pos < l_blob_len LOOP
        DBMS_LOB.read(l_blob, l_amount, l_pos, l_buffer);
        UTL_FILE.put_raw(l_file, l_buffer, TRUE);
        l_pos := l_pos + l_amount;
      END LOOP;
    END IF;

    -- Close the file.
    UTL_FILE.fclose(l_file);

    -- Reset the pos for the next jpg file
    l_pos := 1;

  END LOOP;

EXCEPTION
  WHEN OTHERS THEN
    -- Close the file if something goes wrong.
    error_number := sqlcode; 
    error_message := substr(sqlerrm, 1, 100); 
    dbms_output.put_line('Error Number: ' || error_number); 
    dbms_output.put_line('Error Message: ' || error_message); 
    utl_file.fclose_all;
  RAISE;

END;

BLOB のエクスポートに関する洞察をお寄せいただきありがとうございます。

4

2 に答える 2

5

l_amountリセットも必要です。

l_amount := 32767;

dbms_lob.read の 2 番目のパラメーターはIN OUTパラメーターです。

http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_lob.htm#i999170

DBMS_LOB.READ (
   lob_loc   IN             BLOB,
   amount    IN OUT  NOCOPY INTEGER,
   offset    IN             INTEGER,
   buffer    OUT            RAW);

運が悪いと、読み取るのに 1 バイトしか残っていないため、次の大きなブロブをバイト単位でステップスルーします。

読み取るバイト数 (BLOB の場合) または文字数 (CLOB の場合)、または読み取られた数。

宿題を全部やり終えたので、遅いパフォーマンスを再現できました。ここで説明されているように、いくつかのテストデータを作成しました: BLOB 列を使用して Oracle でテスト データを準備する

次に、自分のテスト プログラムで試してみました。

create or replace directory outdir as '/home/oracle/pngs';
set serveroutput on
declare
  l_file      utl_file.file_type;
  l_buffer    RAW(32767);
  l_amount    PLS_INTEGER := 32767;
  l_pos       PLS_INTEGER := 1;
  l_blob_len  PLS_INTEGER;
begin
  --l_amount := 1; -- this wrecked the performance
  for c in (select * from demo.blob_test) loop
    l_file := UTL_FILE.fopen('OUTDIR', 'blob'||c.id||'.png', 'wb', 32767);
    l_blob_len := DBMS_LOB.getlength(c.data);
    IF l_blob_len < 32767 then
      dbms_output.put_line(systimestamp||' BLOB < 32767 bytes');
      DBMS_LOB.read(c.data, l_blob_len, l_pos, l_buffer);
      UTL_FILE.put_raw(l_file, l_buffer, TRUE);
      dbms_output.put_line(systimestamp||' done');
    ELSE 
      dbms_output.put_line(systimestamp||' BLOB >= 32767 bytes len '||l_blob_len);
      WHILE l_pos < l_blob_len LOOP
        --dbms_output.put_line(systimestamp||' l_pos '||l_pos||' l_amount '||l_amount);
        DBMS_LOB.read(c.data, l_amount, l_pos, l_buffer);
        UTL_FILE.put_raw(l_file, l_buffer, TRUE);
        l_pos := l_pos + l_amount;
      END LOOP;
      dbms_output.put_line(systimestamp||' done');
    END IF;
    l_pos := 1;
    l_amount := 32767; -- this handled it
    utl_file.fclose(l_file);
  end loop;
end;
于 2013-06-04T18:51:56.510 に答える
0

Oracle データベースに Java ランタイムがインストールされている場合は、Java 手順を使用して BLOB をファイルにダウンロードしてみてください。

DBMS_LOB パッケージを使用すると、10,000 個の PDF ドキュメントをダウンロードするのに 45 分かかりました。Java 手順を使用すると、同じ数とサイズのドキュメントをダウンロードするのに 15 秒かかります。

これらは、NOCACHE を使用して Oracle 11.2.0.3 テーブルの securefile に格納された BLOB でした。

于 2014-05-26T16:37:04.833 に答える