Точка сохранения в Oracle PL / SQL LOOP для остановки взаимных блокировок или записи блокировок - PullRequest
0 голосов
/ 08 апреля 2019

У меня есть простая процедура, но я не уверен в том, как наилучшим образом реализовать стратегию по предотвращению взаимоблокировок или блокировок записей. Я обновляю несколько таблиц в курсоре LOOP при вызове процедуры, которая также обновляет таблицы.

Были проблемы с взаимоблокировками или блокировками записей, поэтому мне было поручено излечить эту проблему от сбоя программы, когда она сталкивается с тупиковой блокировкой или блокировкой записи, но спать в течение 5 минут и продолжить обработку любых новых записи.

Идеальное решение состоит в том, что он пропускает блокировку тупика или блокировки записи и продолжает обработку оставшихся записей, которые не заблокированы, спит в течение 5 минут, а затем возвращает эту запись при повторном вызове курсора. Программа продолжает работать в течение дня, пока не будет убита.

Моя процедура ниже, я добавил то, что считаю лучшим, но должен ли я иметь исключение во внутреннем цикле, а не во внешнем цикле? Также есть точка сохранения во внутреннем цикле?

PROCEDURE process_dist_data_fix
IS

   lx_record_locked             EXCEPTION;
   lx_deadlock_detected         EXCEPTION;
   PRAGMA EXCEPTION_INIT(lx_record_locked, -54);
   PRAGMA EXCEPTION_INIT(lx_deadlock_detected, -60);   


   CURSOR c_files
   IS
        SELECT surr_id
          FROM batch_pre_dist_data_fix 
         WHERE status = 'REQ'
      ORDER BY surr_id;

   TYPE file_type IS TABLE OF batch_pre_dist_data_fix.surr_id%TYPE;
   l_file_tab file_type;

BEGIN

   LOOP

        BEGIN

            OPEN c_files;
            FETCH c_files BULK COLLECT INTO l_file_tab;
            CLOSE c_files;

            IF l_file_tab.COUNT > 0
                THEN

                    FOR i IN 1..l_file_tab.COUNT
                    LOOP    

                    -- update main table with start date
                        UPDATE batch_pre_dist_data_fix
                        SET start_dtm = SYSDATE
                        WHERE surr_id = l_file_tab(i);

                    -- update tables
                        update_soundmouse_tables (l_file_tab(i));

                   END LOOP;

           END IF;

        Dbms_Lock.Sleep(5*60); -- sleep for 5 mins before looking for more records to process

        -- if there is a deadlock or a locked record then log the error, rollback and wait 5 minutes, then loop again 
        EXCEPTION
            WHEN lx_deadlock_detected OR lx_record_locked THEN
                ROLLBACK; 
                Dbms_Lock.Sleep(5*60);   -- sleep for 5 minutes before processing records again

        END;

   END LOOP;

END process_dist_data_fix;

1 Ответ

0 голосов
/ 08 апреля 2019

Сначала поймите, что взаимоблокировка - это совершенно иная проблема, чем «запись заблокирована». Так что для «записи заблокированной» большую часть времени не должно быть ничего, что вам нужно делать. 9/10 вы хотите, чтобы программа ждала блокировки. если вы ждете слишком долго, возможно, вам придется пересмотреть границы транзакции. Например, здесь ваш шаблон кода довольно типичен. Вы читаете список «сделать», а затем «сделать это». В таких случаях будет редко, когда вы захотите сделать что-то особенное для «блокировки записи». если по какой-то причине строка таблицы batch_pre_dist_data_fix заблокирована, вам следует просто дождаться снятия блокировки и продолжения. если обратное верно, то, поскольку это задание занимает так много времени, и вы так долго блокируете batch_pre_dist_data_fix для другого процесса, вы можете переопределить границу транзакции. то есть, может быть, вы говорите, что после каждой итерации цикла вы делаете коммит. Но остерегайтесь того, как открытый курсор ведет себя при коммите.

тупик - совершенно другое животное. Здесь у вас всегда есть «другой сеанс», и db обнаружил ситуацию, когда вы никогда не сможете выйти из ситуации, и он убил один случайный сеанс. Таким образом, это когда сеанс 1 ожидает ресурса сеанса 2, а сеанс 2 ожидает ресурса f сеанса 1. Это условие бесконечного ожидания, которое может обнаружить db, поэтому он убивает одну случайную ошибку. Один из простых способов решения этой проблемы состоит в том, что если все транзакции, которые работают с несколькими таблицами, просто блокируют их в одном порядке, у нас не будет тупиковой ситуации. Допустим, у нас есть таблица A, B, C, D. Если мы просто решим, что таблицы будут заблокированы в этом порядке. то есть это нормально делать A, B, D или A, C, D или C, D - но никогда не делать D, C. Тогда вы не получите тупик. Для устранения неполадок см. Оракул дампа, созданный, когда он дал ошибку, найдите другой сеанс и список таблиц этого сеанса и посмотрите, как их следует заблокировать.

...