我们一直在运行一个存储过程,以便在过去的5年中每天从数据库中提取BLOBS
。我们通常每晚提取大约25个文件,大多数大小约为500KB,少数几乎接近10,000KB。
这个过程从来都不是最快的,但是在我们移动数据中心之后,这个过程可能需要12个小时。当你只提取~55MB左右时,这本身就令人震惊。我们已聘请所有相关团队来了解Oracle的性能,磁盘I / O等,他们声称一切都很完美。
我一直在阅读有关UTL_FILE
和DBMS_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
的执行计划。
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 :(得分:0)
好吧所以我改变了你的代码,试着避免这些事情select count(1) into rec_num from staging
,因为oracle在这里做了什么,它将pl / sql转换为sql和sql转换为pl / sql所以更好地使用游标,这里是更改的代码。并尝试添加索引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;
/