Stored procedures 在Oracle 11g中执行导出到文件时出现间歇性错误
系统信息Stored procedures 在Oracle 11g中执行导出到文件时出现间歇性错误,stored-procedures,oracle11g,Stored Procedures,Oracle11g,系统信息 Oracle数据库11g/11.2企业版 摘要 调用该过程时(通过Hibernate的CallableStatements),它会根据要保留的天数(一个输入参数)获取要导出的所有分区。评估这些分区后,接受分区列表作为输入参数的函数(代码墙中的第一个)使用DBMS_数据泵API将内容导出到平面文件中。 有时(我没有一个100%可复制的场景),它会失败,出现以下错误。需要注意的是,99.9%的时间这些程序运行缓慢。数据库用户对exports目录具有写访问权限 程序如下: TYPE PART
Oracle数据库11g/11.2企业版 摘要
调用该过程时(通过Hibernate的CallableStatements),它会根据要保留的天数(一个输入参数)获取要导出的所有分区。评估这些分区后,接受分区列表作为输入参数的函数(代码墙中的第一个)使用DBMS_数据泵API将内容导出到平面文件中。
有时(我没有一个100%可复制的场景),它会失败,出现以下错误。需要注意的是,99.9%的时间这些程序运行缓慢。数据库用户对exports目录具有写访问权限 程序如下:
TYPE PARTITION_NAME_TYPE IS RECORD (
PARTITION_NAME VARCHAR2(32)
);
TYPE PARTITION_LIST IS REF CURSOR RETURN PARTITION_NAME_TYPE;
PROCEDURE EXPORT_PARTITIONS (
P_TABLE_NAME IN VARCHAR2,
P_PARTITION_LIST IN PARTITION_LIST,
P_EXPORT_DIR IN VARCHAR2,
P_PARALLELISM_DEGREE IN NUMBER DEFAULT 1,
P_FILE_PATH OUT VARCHAR2,
P_RESULT OUT VARCHAR2,
P_RESULT_MSG OUT VARCHAR2)
IS
V_PUMP_HANDLE NUMBER; -- Data Pump job handle
V_PUMP_STATE VARCHAR2(255);
V_PUMP_FILE VARCHAR2(255);
V_PUMP_DIR VARCHAR2(255);
V_JOB_STATE VARCHAR2(4000);
V_STATUS KU$_STATUS1010;
V_LOGS KU$_LOGENTRY1010;
V_ROW PLS_INTEGER;
V_ERRORDETAILS VARCHAR2(4000);
V_PARTITION_LIST VARCHAR2(32767);
V_PARTITION_LIST_TEMP PARTITION_NAME_TYPE;
BEGIN
FETCH P_PARTITION_LIST INTO V_PARTITION_LIST_TEMP;
IF P_PARTITION_LIST%NOTFOUND THEN
P_RESULT := 'FAILED';
P_RESULT_MSG := 'No partitions to export';
RETURN;
END IF;
V_PARTITION_LIST := V_PARTITION_LIST_TEMP.PARTITION_NAME;
V_PUMP_FILE := V_PARTITION_LIST_TEMP.PARTITION_NAME;
LOOP
FETCH P_PARTITION_LIST INTO V_PARTITION_LIST_TEMP;
IF P_PARTITION_LIST%NOTFOUND THEN
V_PUMP_FILE := V_PUMP_FILE || '_TO_' || V_PARTITION_LIST_TEMP.PARTITION_NAME;
EXIT;
END IF;
V_PARTITION_LIST := V_PARTITION_LIST || ',' || V_PARTITION_LIST_TEMP.PARTITION_NAME;
END LOOP;
V_PUMP_HANDLE := DBMS_DATAPUMP.OPEN('EXPORT', 'TABLE');
DBMS_DATAPUMP.ADD_FILE(HANDLE => V_PUMP_HANDLE, FILENAME => V_PUMP_FILE || '.dbf' , DIRECTORY => P_EXPORT_DIR, FILETYPE => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE);
DBMS_DATAPUMP.ADD_FILE(V_PUMP_HANDLE, V_PUMP_FILE || '.log',P_EXPORT_DIR, NULL, DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE);
DBMS_DATAPUMP.DATA_FILTER (V_PUMP_HANDLE, 'PARTITION_LIST', V_PARTITION_LIST);
DBMS_DATAPUMP.SET_PARALLEL(V_PUMP_HANDLE, P_PARALLELISM_DEGREE);
DBMS_DATAPUMP.START_JOB (V_PUMP_HANDLE);
DBMS_DATAPUMP.WAIT_FOR_JOB(V_PUMP_HANDLE, V_PUMP_STATE);
DBMS_DATAPUMP.DETACH(V_PUMP_HANDLE);
P_RESULT := 'SUCCESS';
-- get actual path to Export Directory
SELECT directory_path INTO V_PUMP_DIR FROM ALL_DIRECTORIES WHERE directory_name = P_EXPORT_DIR;
P_FILE_PATH := V_PUMP_DIR || '/' || V_PUMP_FILE || '.dbf';
P_RESULT_MSG := 'Successfully exported ' || P_TABLE_NAME || '(' || V_PARTITION_LIST || ') as ' || V_PUMP_FILE || '.dbf';
EXCEPTION
WHEN OTHERS THEN
DBMS_DATAPUMP.GET_STATUS(V_PUMP_HANDLE, 8, 0, V_JOB_STATE, V_STATUS);
V_LOGS := V_STATUS.ERROR;
V_ROW := V_LOGS.FIRST;
LOOP
EXIT WHEN V_ROW IS NULL;
V_ERRORDETAILS := 'LOGLINENUMBER='||V_LOGS(V_ROW).LOGLINENUMBER || ' ERRORNUMBER='||V_LOGS(V_ROW).ERRORNUMBER || ' LOGTEXT='||V_LOGS(V_ROW).LOGTEXT;
V_ROW := V_LOGS.NEXT (V_ROW);
END LOOP;
P_RESULT_MSG := 'Failed to export partitions on ' || P_TABLE_NAME || '(' || V_PARTITION_LIST || ') : ' || SQLERRM || ' Details: (' || V_ERRORDETAILS || ')';
P_RESULT := 'FAILED';
DBMS_DATAPUMP.STOP_JOB(V_PUMP_HANDLE);
END EXPORT_PARTITIONS;
PROCEDURE EXPORT_PARTITIONS (
P_TABLE_NAME IN VARCHAR2,
P_NUM_DAYS_TO_KEEP IN NUMBER,
P_EXPORT_DIR IN VARCHAR2,
P_PARALLELISM_DEGREE IN NUMBER DEFAULT 1,
P_FILE_PATH OUT VARCHAR2,
P_RESULT OUT VARCHAR2,
P_RESULT_MSG OUT VARCHAR2)
IS
V_MIN_DAYS_TO_KEEP NUMBER := 1;
V_CNT NUMBER;
V_PARTITIONS PARTITION_LIST;
V_EXPORT_PARTITIONS_RESULT VARCHAR2(2000);
V_EXPORT_PARTITIONS_RESULT_MSG VARCHAR2(2000);
BEGIN
IF P_NUM_DAYS_TO_KEEP < V_MIN_DAYS_TO_KEEP THEN
RAISE_APPLICATION_ERROR(-20000, 'Cannot drop partitions less than ' || V_MIN_DAYS_TO_KEEP || ' days old.');
END IF;
SELECT COUNT(*)
INTO V_CNT
FROM USER_TABLES
WHERE TABLE_NAME = P_TABLE_NAME;
IF V_CNT = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Table Does Not Exist');
END IF;
OPEN V_PARTITIONS FOR SELECT partition_name
FROM USER_TAB_PARTITIONS
WHERE table_name = UPPER(P_TABLE_NAME)
AND
CASE
WHEN INSTR(PARTITION_NAME, 'MAXVALUE') = 0
THEN TO_DATE(SUBSTR(PARTITION_NAME, -8), 'YYYYMMDD')
END < TRUNC(SYSDATE) - P_NUM_DAYS_TO_KEEP
ORDER BY partition_name ASC;
EXPORT_PARTITIONS(P_TABLE_NAME, V_PARTITIONS, P_EXPORT_DIR, P_PARALLELISM_DEGREE, P_FILE_PATH, V_EXPORT_PARTITIONS_RESULT, V_EXPORT_PARTITIONS_RESULT_MSG);
CLOSE V_PARTITIONS;
IF INSTR(V_EXPORT_PARTITIONS_RESULT, 'SUCCESS') = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Cannot export partitions. Logs purging will not proceede. ' || V_EXPORT_PARTITIONS_RESULT_MSG);
END IF;
P_RESULT := 'SUCCESS';
P_RESULT_MSG := V_EXPORT_PARTITIONS_RESULT_MSG;
EXCEPTION
WHEN OTHERS THEN
P_RESULT_MSG := 'Failed To Export Partition on ' || P_TABLE_NAME || ': ' || SQLERRM;
P_RESULT := 'FAILED';
END EXPORT_PARTITIONS;
在绞尽脑汁数天之后,我仍然没有找到有问题的代码
编辑#1过程的调用方是Hibernate CallableStatement。java端ResultMg的声明如下:
call.registerOutParameter("resultMsg", Types.VARCHAR);
这将调用
EXPORT\u分区(tableName、daysToKeep、exportDir、paralelismdege)
过程,然后调用第二个EXPORT\u分区
过程。您可能会生成很长的错误消息。此过程的调用者如何定义p\u error\u msg
?这似乎太小了——您显示的代码没有太大问题。您必须从导出中获得一个实际的异常,但您没有看到它,因为您正在捕获它,然后无法将其作为消息传递回调用方。编辑原始帖子以包含有关调用序列的更多信息。好的,但是如果分区列表足够大,您仍可能试图超过32k;如果它试图附加太多的分区名,以至于v_partition_names
本身试图超过32k,那么这可能就是导致原始异常的原因。您需要添加一些调试,可能首先显示试图创建\p_error\u msg\的组件的大小和值,然后再实际到达第208行。这将需要立即导出约1200个分区,这根本不可能。此外,我还考虑实现一个异常处理,当超过变量的大小时,它会切断分区的解析,然后分批执行。您可能会生成相当长的错误消息。此过程的调用者如何定义p\u error\u msg
?这似乎太小了——您显示的代码没有太大问题。您必须从导出中获得一个实际的异常,但您没有看到它,因为您正在捕获它,然后无法将其作为消息传递回调用方。编辑原始帖子以包含有关调用序列的更多信息。好的,但是如果分区列表足够大,您仍可能试图超过32k;如果它试图附加太多的分区名,以至于v_partition_names
本身试图超过32k,那么这可能就是导致原始异常的原因。您需要添加一些调试,可能首先显示试图创建\p_error\u msg\的组件的大小和值,然后再实际到达第208行。这将需要立即导出约1200个分区,这根本不可能。此外,我还考虑实现一个异常处理,当超过变量的大小时,它会切断分区的解析,然后分批执行。
call.registerOutParameter("resultMsg", Types.VARCHAR);