DeepEdit!

Программирование баз данных на Oracle, техническая документация, литература, статьи и публикации

  • Увеличить размер шрифта
  • Размер шрифта по умолчанию
  • Уменьшить размер шрифта

Ошибка “snapshot too old”

Теперь давайте рассмотрим вторую причину, по которой разработчики соблаз­няются фиксировать обновления в процедурном цикле, и которые происходят из­за безуспешных попыток экономно использовать “ограниченный ресурс” (сегменты отката). Это вопрос конфигурации; вы должны обеспечить наличие достаточного пространства для сегментов отмены, чтобы правильно выставить размер ваших транзакций. Фиксация в цикле, помимо того, что работает медленнее, также часто вызывает появление ужасной ошибки ORA-01555. Рассмотрим это более детально. 
Как вы знаете после прочтения глав 1 и 7, многоверсионная модель Oracle ис­пользует сегменты отмены для реконструирования блоков в том виде, в каком они были на момент запуска вашего оператора или транзакции (в зависимости от ре­жима изоляции). Если необходимая информация отмены больше не существует, вы получаете сообщение об ошибке ORA-01555: snapshot too old (“ORA-1555: уста­ревший снимок”), и ваш запрос не выполняется. Поэтому, если вы модифицируете таблицу, которую читаете (как в предыдущем примере), то генерируете информа­цию отмены, необходимую вашему запросу. Оператор UPDATE генерирует инфор­мацию отмены, которую ваш запрос, вероятно, использует, чтобы получить согла­сованное по чтению представление данных, которые ему нужно обновить. Если вы выполняете фиксацию, то позволяете системе повторно задействовать простран­ство отмены, только что использованное вами. И если она повторно использует это пространство, уничтожая данные отмены, которые впоследствии понадобятся вашему запросу, у вас возникает серьезная проблема. Оператор SELECT заверша­ется неудачей, а UPDATE останавливается на полпути. Вы получаете частично за­вершенную логическую транзакцию и, вероятно, не имеете никакой возможности перезапустить ее (подробнее об этом ниже). 
Давайте рассмотрим сказанное на небольшом примере. В маленькой тестовой базе данных я создал таблицу: 
ops$tkyte@ORA10G> create table t as select * from all_objects;
Table created. 
Таблица создана. 
ops$tkyte@ORA10G> create index t_idx on t(object_name);Index created. 
Индекс создан. 
ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'T', cascade=>true );
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена. 
Затем я создал очень маленькое табличное пространство отмены и указал сис­теме использовать его. Обратите внимание, что при установке AUTOEXTEND OFF я ограничил размер UNDO в системе до 2 Мбайт или менее: 
ops$tkyte@ORA10G> create undo tablespace undo_small
2 datafile size 2m
 3 autoextend off
 4 /
Tablespace created.
Табличное пространство создано. 
ops$tkyte@ORA10G> alter system set undo_tablespace = undo_small;
System altered.
Система изменена. 
Теперь, имея только одно маленькое табличное пространство отмены, я запу­стил следующий блок кода для выполнения UPDATE: 
ops$tkyte@ORA10G> begin
2 for x in ( select /*+ INDEX(t t_idx) */ rowid rid, object_name, rownum r
3     from t
 4 where object_name > ' ' )
5 loop
6   update t
7 set object_name = lower(x.object_name)
8 where rowid = x.rid;
9 if ( mod(x.r,100) = 0 ) then
10 commit;
11   end if; 
12  end loop; 
13 commit;
14 end;
15 /
begin
ERROR at line 1: 
ORA-01555: snapshot too old: rollback segment number with name "" too small
ORA-06512: at line 2 
ОШИБКА в строке 1: 
ORA-01555: устаревший снимок: номер сегмента отката с именем "" очень мал 
ORA-06512: в строке 2 
Возникла ошибка. Хочу отметить, что я добавил подсказку (hint) для использо­вания индекса в запросе и конструкцию WHERE, чтобы гарантировать чтение табли­цы в случайном порядке (вместе они заставляют стоимостной оптимизатор читать таблицу, “отсортированную” по ключу индекса). Когда мы обрабатываем таблицу через индекс, то читаем блок для извлечения одной строки, а следующая строка с высокой степенью вероятности должна уже быть в другом блоке. В конечном итоге обрабатываются все строки из блока 1, но не одновременно. Блок 1 может содер­жать, скажем, данные всех строк, у которых OBJECT_NAME начинается с букв A, M, N, Q и Z. Поэтому мы должны обратиться к этому блоку много раз, поскольку чи­таем данные, отсортированные по OBJECT_NAME, и можно предположить, что мно­жество значений OBJECT_NAME будут начинаться с букв между A и M. Поскольку фиксация выполняется часто и пространство отката используется повторно, мы со временем повторно навещаем блок, в котором просто невозможно выполнить откат к точке во времени, когда запрос начался, и именно в этот момент получаем ошибку. 
Это очень надуманный пример — просто чтобы наглядно продемонстрировать, что может случиться. Мой оператор UPDATE сгенерировал откат. У меня было очень маленькое табличное пространство отката (всего 2 Мбайт). Я многократно заво­рачивал сегменты отката, поскольку они используются в циклическом режиме. Каждый раз, когда я выполнял фиксацию, то тем самым позволял Oracle переза­писывать сгенерированные данные отката. В конце концов, мне понадобился не­который кусочек данных, который я сгенерировал, но его не оказалось в сегменте отката, поэтому возникла ошибка ORA-01555. 
Вы вправе указать, что в этом случае, если бы я не выполнял фиксацию в стро­ке 10 предыдущего примера, то получил бы следующую ошибку: 
begin
ERROR at line 1: 
ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDO_SMALL'
ORA-06512: at line 6 
ОШИБКА в строке 1: 
ORA-30036: невозможно расширить сегмент на 8 в табличном пространстве 
отмены 'UNDO_SMALL' 
ORA-06512: в строке 6 
Ниже описаны главные отличия между этими двумя ошибками. 
Пример с ORA-01555 оставляет мое обновление в совершенно неопределен­ном состоянии. Часть работы сделана, часть — нет. 
Я абсолютно ничего не могу сделать, чтобы избежать ORA-01555, если ста­ну выполнять фиксацию в цикле курсора FOR. 
Ошибки ORA-30036 можно избежать, выделив соответствующие ресурсы в системе. Эта ошибка устраняется корректным определением размера сегмен­та отката; первая ошибка — нет. Затем, даже если я не смогу избежать этой ошибки, то по крайней мере обновление откатывается и база данных оста­ется в известном, согласованном состоянии — я не оставляю ее в состоянии частичного обновления. 

Главным недостатком здесь является то, что вы не можете “сэкономить” про­странство отката за счет частой фиксации — оно вам нужно. Я работал в однополь­зовательской системе, когда получил ошибку ORA-01555. Достаточно было одного сеанса, чтобы привести к этой ошибке, а в реальной жизни таких ошибок будет множество, если каждый индивидуальный сеанс вызовет свою собственную ошиб­ку ORA-01555. Разработчики и администраторы баз данных должны работать со­вместно, чтобы адекватно определить размер этих сегментов для задач, решаемых в системе. Здесь не должно быть кратковременных изменений. Путем тщательного анализа вашей системы вы должны выяснить размер наибольшей транзакции и соответствующим образом установить размеры сегментов отката. Динамическое представление производительности V$UNDOSTAT может очень пригодиться для слежения за генерируемыми откатами и продолжительностью самых длительных запросов. Многие люди склонны воспринимать вещи вроде временной области, области отмены и области повторения как “накладные расходы”, которые следует со­кращать насколько возможно. Это напоминает о проблеме, с которой столкнулась компьютерная индустрия 1 января 2000 года — она была вызвана стремлением сэ­
кономить 2 байта в полях дат. Эти компоненты базы данных не являются наклад­ными расходами, а ключевыми компонентами системы. Их размер должен быть определен должным образом (не слишком большим, но и не слишком маленьким). 
 









jAntivirus