|
Программирование >> Oracle
500, строку 20001 и т.д. При выполнении запроса блоки считываются в случайном порядке и, вероятно, по несколько раз. Вероятность возникновения ошибки ORA-01555 в этом случае составляет почти 100 процентов. Итак, в одном сеансе выполним: tkyte@TKYTE816> create rollback segment rbs small 2 storage 3 (initial 8k next 8k 4 minextents 2 maxextents 3) 5 tablespace rbs test Rollback segment created. tkyte@TKYTE816> alter rollback segment rbs small online; Rollback segment altered. tkyte@TKYTE816> create table t 2 as 3 select * 4 from all objects Table created. tkyte@TKYTE816> create index t idx on t(object id) 2 / Index created. tkyte@TKYTE816> begin 2 for x in (select rowid rid from t) 3 loop 4 commit; 5 set transaction use rollback segment rbs small; 6 update t 7 set object name = lower(object name) 8 where rowid = x.rid; 9 end loop; 10 commit; 11 end; 12 / Пока выполняется этот блок PL/SQL, в другом сеансе выполним: tkyte@TKYTE816> select object name from t where object id > 0 order by object id; OBJECT NAME i obj# tab$ /91196853 activationactivation /91196853 activationactivation ERROR: ORA-01555: snapshot too old: rollback segment number 10 with name RBS SMALL too small 3150 rows selected. tkyte@TKYTE816> select count(*) from t; COUNT (*) 21773 Как видите, после чтения около трех тысяч строк (примерно одной седьмой части данных) в произвольном порядке, мы получили-таки сообщение об ошибке ORA-01555. В этом случае ошибка б1ла вызвана исключительно чтением таблицы Т по индексу, в результате чего в случайном порядке читались блоки по всей таблице. Если бы выполнялся полный просмотр таблицы, то вероятность того, что ошибка ORA-01555 не возникла бы, гораздо больше. (Попробуйте изменить запрос SELECT на SELECT /* + FULL(T) */ ... и посмотрите, что произойдет; в моей системе даже многократное выполнение этого запроса не приводит к возникновению ошибки ORA-1555). Поскольку операторы SELECT и UPDATE будут последовательно просматривать таблицу Т, то оператор SELECT скорее всего опередит в просмотре UPDATE (оператор SELECT только читает данные, a UPDATE должен прочитать их и изменить, поэтому выполняется этот оператор медленнее). Выполняя случайные чтения, мы увеличиваем вероятность того, что оператору SELECT понадобится прочитать блок, давно измененный и зафиксированный оператором UPDATE. Этот пример демонстрирует некоторую неоднозначность ошибки ORA-01555. Ее возникновение зависит от того, как одновременно работающие сеансы читают и изменяют таблицы. Данные извлекаются в нескольких транзакциях Это разновидность той же ситуации. Вариант этот не отличается от предыдущего, но сеанс создает проблемы сам себе. Никаких действий со стороны других сеансов не нужно. Мы уже изучали эту ситуацию в главе 4, посвященной транзакциям, и кратко рассмотрим ее снова. Смысл в том, что извлечение строк, перемежающееся выполнением операторов COMMIT, - верный способ получить сообщение об ошибке ORA-01555. По моим наблюдениям, большинство ошибок ORA-01555 связано с такими действиями. Удивительно, но разработчики иногда реагируют на получение этой ошибки тем, что фиксируют транзакции еще чаще, поскольку в сообщении сказано, что сегмент отката - слишком мал. Предполагают, что это поможет решить проблему (исходя из ошибочного вывода, что при изменении используется слишком много места в сегменте отката), тогда как на самом деле это приводит лишь к скорейшему возникновению этой ошибки. Итак, надо изменить большой объем информации. По некоторым причинам вы не обеспечили достаточно места в сегментах отката. Поэтому принимается решение фик- сировать транзакцию каждые X строк, чтобы сэкономить место в сегментах отката. Мало того, что это медленнее, и в конечном итоге будет сгенерировано больше данных отмены и повторного выполнения, это еше и гарантированный способ получить сообщение об ошибке ORA-01555. Продолжая предыдущий пример, я могу легко это продемонстрировать. Используя ту же таблицу Т, что и в примере выше, просто выполни- tkyte@TKYTE816> declare 3 begin 4 5 6 7 8 9 10 l cnt number default 0; for x in (select rowid rid, loop t.* from t where object id 12 13 15 16 17 18 declare * if (mod(l cnt,100) then commit; set transaction use rollback segment rbs small; end if; update t set object name = lower(object name) where rowid = x.rid; l cnt := l cnt + 1; end loop; commit; end; ERROR at line 1: ORA-01555: snapshot too old: rollback segment number 10 with name RBS SMALL too small ORA-06512: at line 4 Здесь выполняется случайное чтение таблицы Т по индексу. Мы изменяем по одной строке за раз в цикле. После изменения 100 строк транзакция фиксируется. Рано или поздно мы повторно обратимся в запросе к блоку, уже измененному оператором UPDATE, и этот блок уже не удастся восстановить по сегментам отката (поскольку данные в них давно перезаписаны). Теперь мы попали в неприятную ситуацию: процесс изменения завершился неудачей на полпути. Можно, как было показано в главе 4, придумать более изощренный метод изменения данных. Например, найти минимальный и максимальный идентификатор OBJECT ID. Можно поделить этот диапазон на поддиапазоны по 100 строк и делать изменения, записывая в другую таблицу информацию об успешно выполненных действиях. Это позволит реализовать процесс, который должен быть реализован одним оператором и одной транзакцией, в виде множества отдельных транзакций, организованных с помощью сложного процедурного кода таким образом, чтобы они могли быть перезапущены в случае сбоя. Например: >
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |