2016-12-07 1 views
0

우리는 지난 5 년간 매일 데이터베이스에서 BLOBS을 추출하는 저장 프로 시저를 실행 해 왔습니다. 일반적으로 우리는 매일 약 25 개의 파일을 추출합니다. 대다수의 파일 크기는 약 500KB이고 소수는 10,000KB입니다.데이터베이스에서 추출 BLOB가 느림

프로세스가 가장 빠른 적이 없었지만 데이터 센터를 이동 한 후이 프로세스를 12 시간 이상 수행 할 수 있습니다. 55MB 정도만 압축하면 그 자체로 충격적입니다. 우리는 Oracle, 디스크 I/O 등의 성능을 살펴보기 위해 모든 관련 팀을 참여 시켰으며 모든 것이 완벽하다고 주장합니다. 내가 읽어 봤는데

UTL_FILEDBMS_LOB.read, 본 사람들은 내가이 어떤 의미를 알아낼 수 없습니다 솔직히 말해서 등 각 루프 후 pos를 재설정에 대해 이야기하고, 일반적인 합의는 것이있다 동일한 결과를 달성하는 훨씬 더 좋은 방법입니다.

우리는 불행히도 리팩토링에 대한 자유가 없으므로 누구나 우리 절차에서 눈부신 부분을 볼 수 있습니까? 나는 단지 내가 완전히 이해하지 못하는 것을 고심하고 있으며 우리 인프라를 유지하는 사람들은이 코드에 그것을 모두 탓하고 손을 씻는다.

CREATE OR REPLACE PROCEDURE PKG_EXTRACT (l_brand IN VARCHAR2) AS 

    l_file  UTL_FILE.FILE_TYPE; 
    l_buffer RAW(32767); 
    l_amount BINARY_INTEGER := 32767; 
    l_pos  INTEGER; 
    l_blob  BLOB; 
    l_blob_len INTEGER; 
    x NUMBER; 
    l_file_name VARCHAR2(200); 
    l_count  INTEGER := 1; 
    v_code NUMBER; 
    v_errm VARCHAR2(64); 
log_file  UTL_FILE.FILE_TYPE; 
rec_num number; 

BEGIN 
DECLARE 
    CURSOR extract_cur IS 
     SELECT DATA, BIN_NAME 
     FROM STAGING 
     WHERE UPPER(EXTRACTED)='N'; 

BEGIN 
log_file := UTL_FILE.fopen('DATA_DOWNLOAD_DIR','pkg_extract.log','a', 32767); 

UTL_FILE.put_line(log_file,'Logging is being done in 24 hours format - V1.5 ',TRUE); 
UTL_FILE.put_line(log_file,'Extract procedure started on Date-Time = '|| TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'),TRUE); 


select count(1) into rec_num from staging; 

UTL_FILE.put_line(log_file,'Total Number of records found = ' || rec_num , TRUE); 
select count(1) into rec_num from staging where UPPER(EXTRACTED)='N'; 
UTL_FILE.put_line(log_file,'Total Number of records matching criteria = ' || rec_num , TRUE); 

    dbms_output.put_line('Loop through records and write them to file'); 
    FOR extract_rec IN extract_cur 
    LOOP 

     l_pos := 1; 
     l_blob := extract_rec.DATA; 
     l_blob_len := DBMS_LOB.getlength(l_blob); 

     -- Save blob length. 
     x := l_blob_len; 

     l_file_name := extract_rec.BIN_NAME ; 

     -- Open the destination file. 
     dbms_output.put_line('Open the destination file:- ' || l_file_name); 
     l_file := UTL_FILE.fopen('DATA_DOWNLOAD_DIR',l_file_name,'wb', 32767); 
     dbms_output.put_line('File opened'); 

     -- Read chunks of the BLOB and write them to the file until complete. 
     dbms_output.put_line('l_pos:- ' || l_pos); 
     dbms_output.put_line('l_blob_len:- ' || l_blob_len); 
     WHILE l_pos <= l_blob_len 
     LOOP 
     dbms_output.put_line('DBMS_LOB.read from position: ' || l_pos); 
     DBMS_LOB.read(l_blob, l_amount, l_pos, l_buffer); 
     dbms_output.put_line('UTL_FILE.put_raw'); 
     UTL_FILE.put_raw(l_file, l_buffer, TRUE); 
     dbms_output.put_line('Written ' || l_amount || ' bytes of data starting at position: ' || l_pos); 

     -- Set the start position for the next cut. 
     l_pos := l_pos + l_amount; 

    --updating the extract field 

     dbms_output.put_line(extract_rec.BIN_NAME); 

     END LOOP; 

     l_count := l_count + 1; 
     -- Close the file. 
     dbms_output.put_line('Close the file:- ' || l_file_name); 
     UTL_FILE.fclose(l_file); 

     update staging set extracted='Y', extract_timestamp=sysdate where bin_name=extract_rec.BIN_NAME; 
     commit; 

    END LOOP; 

UTL_FILE.put_line(log_file,'Extract procedure Completed on Date-Time = '|| TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'),TRUE); 

IF UTL_FILE.is_open(log_file) THEN 
     UTL_FILE.fclose(log_file); 
end if; 
END; 

EXCEPTION 
    WHEN OTHERS THEN 
    v_code := SQLCODE; 
    v_errm := SUBSTR(SQLERRM, 1, 64); 
    dbms_output.put_line('Error code ' || v_code || ': ' || v_errm); 
UTL_FILE.put_line(log_file,'--------------------------------------' ,TRUE); 
UTL_FILE.put_line(log_file,'Error Occurred while executing '||'Error code ' || v_code || ': ' || v_errm ,TRUE); 
UTL_FILE.put_line(log_file,'Extract procedure Completed with errors - '|| TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'),TRUE); 
UTL_FILE.put_line(log_file,'--------------------------------------' ,TRUE); 

    -- Close the file if something goes wrong. 
    IF UTL_FILE.is_open(l_file) THEN 

     UTL_FILE.fclose(l_file); 

IF UTL_FILE.is_open(log_file) THEN 
     UTL_FILE.fclose(log_file); 
end if; 
    END IF; 
    RAISE; 
END; 
/

을 편집 CURSOR extract_cur에 대한

실행 계획. 내가 코드를 변경, 그래서 이런 일을 피하려고 select count(1) into rec_num from staging/SQL 그렇게보다 효율적으로 사용하는 커서를 PL 무엇 오라클 있기 때문에 PL/변환 여기 않는 SQL 및 SQL에 SQL

Plan hash value: 3428151562 
----------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  6 | 678 |  9 (0)| 00:00:01 | 
|* 1 | TABLE ACCESS FULL| STAGING |  6 | 678 |  9 (0)| 00:00:01 | 
----------------------------------------------------------------------------- 
Predicate Information (identified by operation id): 
--------------------------------------------------- 
    1 - filter(UPPER("S"."EXTRACTED")='N') 
+0

'준비'테이블에서 통계를 수집 해 보았습니까? – GurV

+0

@ GurwinderSingh 일반적으로 매달 데이터베이스에서 수행됩니다. 그것은 특히 큰 데이터베이스가 아닙니다 ... 하루 25 건의 레코드와 30 일간의 가치 만 지니고 있습니다. – hshah

+1

커서의 쿼리에 대한 실행 계획을 가져 와서 질문에 붙여 넣을 수 있습니까? – GurV

답변

0

확인, 여기에 변경된 코드입니다. 그리고 인덱스 create index extracted_idx on staging(extracted asc);을 추가해보십시오. 수정 된 코드는 다음과 같습니다. 컴파일 할 수 있는지 검사 할 수 없기 때문에 컴파일할지 모르지만 작동해야합니다.

CREATE OR REPLACE PROCEDURE PKG_EXTRACT(l_brand IN VARCHAR2) AS 

    l_file  UTL_FILE.FILE_TYPE; 
    l_buffer RAW(32767); 
    l_amount BINARY_INTEGER := 32767; 
    l_pos  INTEGER; 
    l_blob  BLOB; 
    l_blob_len INTEGER; 
    x   NUMBER; 
    l_file_name VARCHAR2(200); 
    l_count  INTEGER := 1; 
    v_code  NUMBER; 
    v_errm  VARCHAR2(64); 
    log_file UTL_FILE.FILE_TYPE; 
    rec_num  number; 

    CURSOR extract_cur IS 
    SELECT DATA, 
       BIN_NAME 
      FROM STAGING 
     WHERE EXTRACTED = 'N'; 

    cursor c_count is 
    select count(1) cnt 
      from staging; 

    cursor c_rec_num is 
    select count(1) cnt 
     from staging 
     where EXTRACTED = 'N'; 

BEGIN 
    log_file := UTL_FILE.fopen('DATA_DOWNLOAD_DIR', 
          'pkg_extract.log', 
          'a', 
          32767); 

    UTL_FILE.put_line(log_file, 
        'Logging is being done in 24 hours format - V1.5 ', 
        TRUE); 
    UTL_FILE.put_line(log_file, 
        'Extract procedure started on Date-Time = ' || 
        TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'), 
        TRUE); 

    open c_cnt; 
    fetch c_cnt into rec_num; 
    close c_cnt; 

    open c_rec_num; 
    fetch c_rec_num into rec_num; 
    close c_rec_num; 

    UTL_FILE.put_line(log_file, 
        'Total Number of records found = ' || rec_num, 
        TRUE); 
    --dont know why you doing this again 
    open c_rec_num; 
    fetch c_rec_num into rec_num; 
    close c_rec_num; 

    UTL_FILE.put_line(log_file, 
        'Total Number of records matching criteria = ' || 
        rec_num, 
        TRUE); 

    FOR extract_rec IN extract_cur LOOP 

    l_pos  := 1; 
    l_blob  := extract_rec.DATA; 
    l_blob_len := DBMS_LOB.getlength(l_blob); 

    -- Save blob length. 
    x := l_blob_len; 

    l_file_name := extract_rec.BIN_NAME; 

    -- Open the destination file. 

    l_file := UTL_FILE.fopen('DATA_DOWNLOAD_DIR', l_file_name, 'wb', 32767); 

    -- Read chunks of the BLOB and write them to the file until complete. 

    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); 

     -- Set the start position for the next cut. 
     l_pos := l_pos + l_amount; 

    --updating the extract field 

    END LOOP; 

    l_count := l_count + 1; 
    -- Close the file. 
    UTL_FILE.fclose(l_file); 

    update staging 
     set extracted = 'Y', 
       extract_timestamp = sysdate 
    where bin_name = extract_rec.BIN_NAME; 
    commit; 

    END LOOP; 

    UTL_FILE.put_line(log_file, 
        'Extract procedure Completed on Date-Time = ' || 
        TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'), 
        TRUE); 

    IF UTL_FILE.is_open(log_file) THEN 
    UTL_FILE.fclose(log_file); 
    end if; 
END; 

EXCEPTION 
WHEN OTHERS THEN 
     v_code := SQLCODE; v_errm := SUBSTR(SQLERRM, 1, 64); 
     UTL_FILE.put_line(log_file, '--------------------------------------', TRUE); 
     UTL_FILE.put_line(log_file, 'Error Occurred while executing ' || 'Error code ' || v_code || ': ' || v_errm, TRUE); 
     UTL_FILE.put_line(log_file, 'Extract procedure Completed with errors - ' || TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR,HH24.MI.SSXFF'), TRUE); 
     UTL_FILE.put_line(log_file, '--------------------------------------', TRUE); 

    -- Close the file if something goes wrong. 
    IF UTL_FILE.is_open(l_file) THEN 

    UTL_FILE.fclose(l_file); 

     IF UTL_FILE.is_open(log_file) THEN 
      UTL_FILE.fclose(log_file); 
     end if; 
    END IF; 
END; 
/
+0

나는 변화를 시도하고 이해하기 위해이 일을 겪어왔다. 이것은 나의 장점은 아니지만 나는 거기에 가고 있다고 생각한다 ... 여기서 도움을 청하기위한 목적은 모두 배우는 것이다. 나는 "왜이 일을 다시하는지 모르겠다"라는 주석 주변의 코드에 대한 질문을 가지고있다. 원본이 총 행 수를 가져 와서 출력하기 전에 rec_num에 할당 한 다음 "N"의 수를 동일한 변수에 할당하고 출력하기 때문에 나에게 맞는 것처럼 보이지 않습니다. 너의 것은 "c_count"여야한다. 나는 그것이 "c_count"이어야한다고 생각한다. – hshah

+0

나는 읽을 때마다 한 단어 만 볼 수 있기 때문에 "cnt"의 이름을 바꿔야 할 것입니다;) – hshah

+0

이것을 살펴 보았습니까? 내가 옳은 줄에 있니? – hshah

관련 문제