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