Проблема с производительностью при обновлении 9 миллионов записей с использованием скрипта - PullRequest
1 голос
/ 30 апреля 2020

Мы запустили приведенный ниже скрипт, чтобы обновить несколько столбцов в таблице с 11G oracle DB (11.2.0.3), и для ее завершения требуется около 61 часа, что удивительно, поскольку мы используем Bulk collect и Forall, чтобы сделать актуальное обновление. Мы также включили параллельный dml. Мы также пытаемся выполнить обновление на основе rowid, а не с помощью столбца, который индексируется, поскольку мы думаем, что это будет быстрее. Любые советы, чтобы ускорить это было бы здорово. Ниже приведен скрипт

ALTER session enable parallel dml;
DECLARE
i NUMBER;
j number :=0 ;
TYPE tab_type IS TABLE OF rowid index by binary_integer;
tab_id tab_type;

CURSOR c1 IS
SELECT /*+ parallel(na,DEFAULT) */
                   rowid
                             from sample_table na
                             FOR UPDATE SKIP LOCKED;
BEGIN
  OPEN c1;
  LOOP
    FETCH c1 BULK COLLECT INTO tab_id LIMIT 10000;
    EXIT WHEN tab_id.COUNT = 0;

    FORALL i IN 1..tab_id.COUNT
                             update sample_table 
        set col1 = 'XXX'
        , col2 = 'XXX'
        , col3 = 'XXX'
        , col4 = 'XXX'
                             , col5= 'XXX'
        , col6 = 'XXX' 
     WHERE rowid = tab_id(i);
              j := j+1;
              if mod(j, 1000) = 0 THEN    -- Commit every 1000 records
                    COMMIT;
              end if;


  END LOOP;

  CLOSE c1;

END;
/

1 Ответ

1 голос
/ 30 апреля 2020

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

  1. Сначала инструкция FORALL не создает al oop. Он запускает один включенный 1 раз, обрабатывая всю коллекцию.
  2. Это также означает, что ваш интервал фиксации НЕ 1000, как вы указали, а 1 МБ.
  3. Индексная переменная (i) в оператор является локальным для этого оператора и может быть доступен только в рамках оператора forall. Таким образом, объявленная переменная i не является переменной, используемой в forall, и, следовательно, не требуется. Нет ошибки из-за правил области видимости.
  4. Поскольку после выхода из l oop фиксация не выполняется, последний набор не будет зафиксирован, если количество строк не будет кратным интервалу фиксации. В вашем случае с интервалом фиксации строки 1M, если у вас 8 999 999 строк, то будет зафиксировано только 8M.

Имея все это в виду, вы можете попробовать:

declare
  type tab_type is table of rowid;
  tab_id tab_type;

  k_buffer_limit constant pls_integer  := 10000;

 cursor c1 is
        select /*+ parallel(na,DEFAULT) */
               rowid
          from sample_table na
           for update skip locked;
begin
  open c1;
  loop
    fetch c1 bulk collect into tab_id limit 10000

    forall i in 1..tab_id.count
      update sample_table 
         set col1 = 'XXX'
           , col2 = 'XXX'
           , col3 = 'XXX'
           , col4 = 'XXX'
           , col5=  'XXX'
           , col6 = 'XXX' 
      where rowid = tab_id(i);

     commit;    
     exit when tab_id.count < k_buffer_limit; 
  end loop;

  close c1;

end;
Массовый сбор / обработка данных - это компромисс между переключением контекста и использованием памяти. Хотя сокращение переключения контекста - хорошая вещь, это может быть преодолено требованиями к памяти. Ваш процесс может идти в ожидании, чтобы получить достаточно памяти. Вы можете улучшить производительность, уменьшив размер буфера .
...