|
Программирование >> Oracle
tkyte@TKYTE816> insert into done values (0) ; 1 row created. declare l cnt number; l max number; tkyte@TKYTE816> 4 begin 5 select 6 select 8 while 9 loop 10 11 13 14 15 16 17 18 20 end loop; 21 end; 22 / object id into max(object id) (l cnt < l max) l cnt from done; into l max from t; update t set object name = lower(object name) where object id > l cnt and object id <= l cnt+100; update done set object id = object id+100; commit; set transaction use rollback l cnt := l cnt + 100; segment rbs small; PL/SQL procedure successfully completed. Мы предложили сложное решение, которое надо еще тестировать и анализировать. И работать оно будет намного медленнее, чем простой оператор: update t set object name = lower(object name) where object id > 0; Простое и, по моему мнению, правильное решение этой дилеммы, - сконфигурировать для системы сегменты отката достаточного размера и использовать один оператор UPDATE. При изменении большего, чем обычно, объема данных используйте черновой сегмент отката, создаваемый специально для больших процессов, и удалите его после этого. Это решение гораздо лучше сложного процедурного решения, которое может не сработать просто в силу своей сложности (из-за ошибки программирования). Проще дать системе сделать все необходимое, чем пытаться придумать сложные обходные пути, позволяющие сэкономить место . Отложенная очистка блоков Эту причину ошибки ORA-01555 полностью устранить сложнее, но она встречается реже, поскольку слишком редки обстоятельства, при которых она возникает (по крайней мере, в версии Oracle 8i). Мы уже рассматривали механизм очистки блоков: сеанс, tkyte@TKYTE816> create table done(object id int); Table created. обратившийся к измененному блоку, проверяет, активна ли еще транзакция, изменившая его. Если оказалось, что транзакция не активна, он очищает блок так, что следующему обращающемуся к нему сеансу не придется выполнять этот процесс. Чтобы очистить блок, сервер Oracle находит (по заголовку блока) сегмент отката, использованный в предыдущей транзакции, а затем проверяет по заголовку сегмента отката, была ли транзакция зафиксирована. Эта проверка выполняется одним из двух способов. В одном из них используется способность сервера Oracle определить, что транзакция зафиксирована, даже если ее слот в таблице транзакций сегмента отката был перезаписан. Другой способ - найти в таблице транзакций сегмента отката номер зафиксированного изменения (COMMIT SCN), что свидетельствует о недавней фиксации транзакции, поскольку ее слот еще не перезаписан. При отложенной очистке блока ошибка ORA-01555 может возникать в следующих случаях. Изменение выполнено и зафиксировано, а блоки не очищены автоматически (на- пример, транзакция изменила больше блоков, чем поместится в 10 процентах пространства буферного кэша в области SGA). Эти блоки не затронуты другим сеансом: их затронет наш невезучий запрос. Начался продолжительный запрос. Он рано или поздно прочитает некоторые из описанных выше блоков. Этот запрос начинается при значении SCN tl. Именно до этого номера изменения в системе придется откатывать данные для того, чтобы обеспечить согласованность чтения. Запись для транзакции, изменившей данные, в момент начала выполнения запроса еще находится в таблице транзакций сегмента отката. В ходе выполнения запроса в системе зафиксировано много транзакций. Эти транзакции не затронули рассматриваемые блоки (если бы затронули, мы бы с этой проблемой не столкнулись). Таблица транзакций в сегменте отката переполняется, и ее слоты используются повторно из-за большого количества зафиксированных транзакций. Что хуже всего, запись исходно изменившей данные транзакции тоже была использована повторно и переписана. Кроме того, система повторно использовала экстенты в сегменте отката, что не позволяет согласовать по чтению блок заголовка сегмента отката. Наименьший номер изменения SCN, записанный в сегменте отката, теперь превосходит tl (он больше, чем согласованный по чтению номер SCN для запроса) из-за большого количества зафиксированных транзакций. Теперь, когда наш запрос доберется до блока, измененного и зафиксированного до его начала, возникнут проблемы. Обычно запрос обращается к сегменту отката, на который указывает блок, и узнает состояние изменившей его транзакции (другими словами, находит номер изменения SCN для оператора фиксации транзакции). Если это значение SCN меньше, чем tl, запрос может использовать этот блок. Если же значение номера изменения SCN для оператора COMMIT превосходит tl, запрос должен отка- тить этот блок. Проблема, однако, в том, что запрос в этом случае не может определить: больше номер изменения COMMIT SCN для блока, чем tl, или меньше. Непонятно, можно использовать блок или нет. В результате выдается сообщение об ошибке ORA-01555. Можно искусственно вызвать возникновение этой ошибки с помощью одного сеанса, но более впечатляющим получится результат при использовании двух сеансов, поскольку можно будет показать, что ошибка не вызвана извлечением данных в нескольких транзакциях. Мы продемонстрируем оба примера, поскольку они невелики по объему и очень похожи. Создадим много блоков в таблице, которые требуют очистки. Затем откроем курсор для таблицы и в небольшом цикле инициируем множество транзакций. Я помещаю все изменения в один и тот же сегмент отката, чтобы гарантировано получить нужную ошибку. Рано или поздно произойдет ошибка ORA-01555, поскольку внешний запрос в цикле (SELECT * FROM T) столкнется с проблемой очистки блока из-за частого изменения и фиксации во внутреннем цикле: tkyte@TKYTE816> create table small(x int) ; Table created. tkyte@TKYTE816> insert into small values (0) ; 1 row created. tkyte@TKYTE816> begin 2 3 4 5 6 9 10 11 12 13 14 15 16 17 18 begin commit; set transaction use rollback segment rbs small; update t set object type = lower(object type); commit; end; for x in loop (select for i loop in 1 from t) x+1; update small set x commit; set transaction use rollback segment rbs small; end loop; end loop; ERROR at line 1: ORA-01555: snapshot too old: RBS SMALL too small ORA-06512: at line 8 rollback segment number 10 with name tkyte@TKYTE816> select * from small;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |