У меня есть процедура, используемая для шифрования транзакционных данных при перемещении живых данных в тестовую среду.Эта таблица содержит ок.100 миллионов строк распределены по 50 разделам.Новый раздел добавляется каждый месяц.По мере увеличения громкости процедура выполняется медленнее, чем раньше.
Я планирую ввести некоторую степень распараллеливания в мой код.Это новая территория, и мне интересно, есть ли лучшие практики.Возможно, используйте dbms_parallel_execute, чтобы разбить обновление на куски?
Любые рекомендации по оптимизации моего кода очень приветствуются!
PROCEDURE Scramble_Transactions
AS
vSeed BINARY_INTEGER;
CURSOR Transactions_cur
IS
SELECT T.ID,
T.MONTH_PARTITION,
T.TRACE_NUM,
T.TXTDATA
FROM TRANSACTIONS T;
TYPE TBL IS TABLE OF Transactions_cur%ROWTYPE
INDEX BY PLS_INTEGER;
Transactions_Rec TBL;
vCounter NUMBER (10);
vString VARCHAR2 (300);
vLen NUMBER (5);
vFromRange VARCHAR2 (25);
vToRange VARCHAR2 (25);
BEGIN
vCounter := 0;
SELECT SUBSTR (TO_CHAR (SYSDATE, 'ddmmyyyyhhmiss'), 11)
INTO vSeed
FROM DUAL;
DBMS_RANDOM.initialize (vSeed);
DBMS_RANDOM.SEED (vSeed);
vFromRange := 0;
OPEN Transactions_cur;
LOOP
FETCH Transactions_cur BULK COLLECT INTO Transactions_Rec LIMIT 10000;
FOR I IN 1 .. Transactions_Rec.COUNT
LOOP
IF Transactions_Rec (i).TRACE_NUM IS NOT NULL
THEN
vString := Transactions_Rec (i).TRACE_NUM;
vLen := LENGTH (TRIM (vString));
vToRange := POWER (10, vLen) - 1;
Transactions_Rec (i).TRACE_NUM :=
LPAD (TRUNC (DBMS_RANDOM.VALUE (vFromRange, vToRange)),
6,
'1');
END IF;
IF Transactions_Rec (i).TXTDATA IS NOT NULL
THEN
vString := Transactions_Rec (i).TXTDATA;
vLen := LENGTH (TRIM (vString));
vToRange := POWER (10, vLen) - 1;
Transactions_Rec (i).TXTDATA :=
LPAD (TRUNC (DBMS_RANDOM.VALUE (vFromRange, vToRange)),
12,
'3');
END IF;
vCounter := vCounter + 1;
END LOOP;
FORALL rec IN 1 .. Transactions_Rec.COUNT
UPDATE Transactions
SET TRACE_NUM = Transactions_Rec (rec).TRACE_NUM,
TXTDATA = Transactions_Rec (rec).TXTDATA
WHERE ID = Transactions_Rec (rec).ID
AND MONTH_PARTITION = Transactions_Rec (rec).MONTH_PARTITION;
EXIT WHEN Transactions_cur%NOTFOUND;
END LOOP;
DBMS_RANDOM.TERMINATE;
CLOSE Transactions_cur;
COMMIT;
END Scramble_Transactions;
Отредактируйте мое решение на основе приведенной ниже обратной связи: Перепишите часть процедуры так, чтобыскремблирование данных выполняется как часть SQL вместо PL / SQL.Процедура теперь также принимает разделение из / в в качестве параметров, позволяющих параллельную обработку.
CREATE OR REPLACE PROCEDURE Scramble_Transactions(P_MONTH_PARTITION_FROM VARCHAR2, P_MONTH_PARTITION_FROM VARCHAR2)
AS
CURSOR Transactions_cur (V_MONTH_PARTITION_FROM TRANSACTIONS.MONTH_PARTITION%TYPE,
V_MONTH_PARTITION_TO TRANSACTIONS.MONTH_PARTITION%TYPE) IS
SELECT T.ID,
T.MONTH_PARTITION,
REGEXP_REPLACE(T.TRACE_NUM,'[0-9]','9') TRACE_NUM,
REGEXP_REPLACE(T.TXTDATA,'[0-9]','9') TXTDATA
FROM TRANSACTIONS T WHERE T.MONTH_PARTITION BETWEEN P_MONTH_PARTITION_FROM AND P_MONTH_PARTITION_FROM ;
TYPE TBL IS TABLE OF Transactions_cur%ROWTYPE
INDEX BY PLS_INTEGER;
Transactions_Rec TBL;
BEGIN
OPEN Transactions_cur(P_MONTH_PARTITION_FROM,P_MONTH_PARTITION_FROM);
LOOP
FETCH Transactions_cur BULK COLLECT INTO Transactions_Rec LIMIT 10000;
/*Some additional processing*/
FORALL rec IN 1 .. Transactions_Rec.COUNT
UPDATE Transactions
SET TRACE_NUM = Transactions_Rec (rec).TRACE_NUM,
TXTDATA = Transactions_Rec (rec).TXTDATA
WHERE ID = Transactions_Rec (rec).ID
AND MONTH_PARTITION = Transactions_Rec (rec).MONTH_PARTITION;
EXIT WHEN Transactions_cur%NOTFOUND;
END LOOP;
CLOSE Transactions_cur;
COMMIT;
END;
/
Теперь выполните процедуру параллельно с использованием DBMS_PARALLEL_EXECUTE.Запрос разбивается на части в зависимости от ключа раздела.
DECLARE
L_TASK_SQL CLOB;
V_TASKNAME USER_PARALLEL_EXECUTE_TASKS.TASK_NAME%TYPE;
V_STATUS USER_PARALLEL_EXECUTE_TASKS.STATUS%TYPE;
C_TASK_NAME VARCHAR2(50) := 'TRANSACTIONS_TASK';
BEGIN
L_TASK_SQL := 'SELECT PARTITION_NAME, PARTITION_NAME FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = ''TRANSACTIONS''';
DBMS_PARALLEL_EXECUTE.CREATE_TASK(C_TASK_NAME);
DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_SQL(
TASK_NAME => 'TRANSACTIONS_TASK',
SQL_STMT => L_TASK_SQL,
BY_ROWID => FALSE);
DBMS_PARALLEL_EXECUTE.RUN_TASK(
TASK_NAME => C_TASK_NAME,
SQL_STMT => 'BEGIN SCRAMBLE_TRANSACTIONS( :START_ID, :END_ID ); END;',
LANGUAGE_FLAG => DBMS_SQL.NATIVE,
PARALLEL_LEVEL => 6);
SELECT TASK_NAME, STATUS INTO V_TASKNAME,V_STATUS FROM USER_PARALLEL_EXECUTE_TASKS WHERE TASK_NAME = C_TASK_NAME;
DBMS_OUTPUT.PUT_LINE('TASK:'|| 'V_TASKNAME' ||' , STATUS:'|| V_STATUS);
DBMS_PARALLEL_EXECUTE.DROP_CHUNKS(TASK_NAME => 'TRANSACTIONS_TASK');
DBMS_PARALLEL_EXECUTE.DROP_TASK(TASK_NAME => 'TRANSACTIONS_TASK');
END;
/
Общее время выполнения уменьшено до 30 минут по сравнению с 13-14 часами ранее.