|
Программирование >> Oracle
19 0 Глава 4 Теперь давайте вернемся ко второй причине, связанной с экономным использованием разработчиком ограниченного ресурса (сегментов отката). Это проблема конфигурирования - необходимо предусмотреть достаточно места в сегментах отката для поддержки транзакций требуемого размера. Фиксация в цикле - мало того, что обычно медленнее, это одна из наиболее частых причин возникновения печально известной ошибки ORA-01555. Давайте рассмотрим эту проблему более детально. Как вы уже знаете из глав 1 и 3, в модели многовариантного доступа Oracle данные в сегментах отката используются для восстановления блоков в том виде, который они имели в начале выполнения оператора или транзакции (в зависимости от уровня изолированности). Если необходимые данные отмены недоступны, выдается сообщение об ошибке ORA-01555 snapshot too old, и запрос не выполняется. Так что, если вы изменяете считываемую вами таблицу (как в представленном выше анонимном блоке), то генерируете данные отмены, необходимые для выполнения запроса. Производимые изменения генерируют данные отмены, которые могут потребоваться при выполнении запроса для согласованного представления изменяемых данных. Если изменения фиксируются, системе разрешается повторно использовать только что занятое место в сегменте отката. Используя его, она стирает данные отката, которые в дальнейшем могут понадобиться для выполнения запроса, т.е. у вас возникает большая проблема. Оператор SELECT не выполнится, и изменение остановится на полпути. В результате получается частично завершенная транзакция, и никакого приемлемого способа выполнить ее повторно, как правило, не существует (об этом - чуть позже). Давайте рассмотрим это на маленьком примере. В небольшой тестовой базе данных я создал таблицу: tkyte@TKYTE81 create table t as select * all objects; Table created. tkyte@TKrE81 create index t idx on t(object name); Index created. Я затем отключил все сегменты отката и создал один маленький сегмент: tkyte@TKYTE816> create rollback segment rbs small storage (initial 64k 2 next 64k minextents 2 maxextents 4) tablespace tools; Rollback segment created. Теперь, когда доступен только маленький сегмент отката, я ввел следующий блок кода для выполнения необходимых изменений: tkyte@TK:E816> begin 2 for x in (select rowid rid, objectname, rownum r 3 from t 4 where object name > chr(O)) 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 10 with name RBS SMALL too small ORA-06512: at line 2 Я получил сообщение об ошибке. Обращаю ваше внимание, что я добавил индекс и конструкцию WHERE, желая добиться случайности чтения строк таблицы. Конструкция WHERE приведет к использованию индекса (для этого я использовал оптимизатор, основанный на правилах). При доступе к таблице через индекс, обычно считывается блок, содержащий одну строку, а следующая необходимая строка оказывается уже в другом блоке. Мы обработаем все строки в блоке 1, просто не сразу. Пусть в блоке 1 находятся строки А, М, N, Q и Z. Так что придется обращаться к блоку пять раз через достаточно продолжительные интервалы времени. Поскольку изменения фиксируются часто и разрешено повторное использование пространства в сегменте отката, то в конце концов, повторно обратившись к блоку, который нельзя воспроизвести, мы получим сообщение об ошибке. Этот придуманный пример наглядно демонстрирует, как все происходит. Оператор UPDATE генерировал данные отката. У нас б1ло четыре экстента размером 64 Кбайта в сегменте отката, т.е. всего 256 Кбайт. Мы многократно использовали сегмент оката, поскольку сегменты отката используются циклически. При каждой фиксации серверу Oracle разрешалось перезаписывать сгенерированные нами данные отката. В определенный момент понадобился нами же сгенерированный фрагмент данных, которого больше нет, и было получено сообщение об ошибке ORA-01555. Если не фиксировать изменения, то будет получено следующее сообщение об ошибке: begin * ERROR at line 1: ORA-01562: failed to extend rollback segment number 10 ORA-01628: max # extents (4) reached for rollback segment RBS SMALL ORA-06512: at line 6 Но между этими двумя ошибками есть, однако, важные отличия. Ошибка ORA-01555 оставляет изменения в абсолютно неизвестном состоянии. Часть изменений сделана, часть - нет. Мы ничего не можем сделать, чтобы избежать ошибки ORA-01555, если зафиксировали изменения в цикле FOR по курсору. Всегда можно избежать ошибки ORA-01562, в1делив системе соответствующие ресурсы. Второй ошибки можно избежать, создав сегменты отката нужного размера, а первой - нет. Подведу итоги: нельзя сэкономить место в сегментах отката, фиксируя изменения чаше, - эта информация в сегментах отката необходима. (Работая в однопользовательской системе, я в течение одного сеанса получил сообщение об ошибке ORA-01555.) Разработчики и администраторы баз данных должны совместно определить необходимый размер сегментов отката для обычно выполняемых заданий. Путем анализа системы необходимо выяснить максимальный размер транзакций и создать для них соответствующие сегменты отката. В рассмотренном выше примере это может быть однократное изменение. В данном случае можно отдельно создать в системе большой сегмент отката исключительно для выполнения этого изменения. Затем с помощью оператора SET TRANSACTION заставить транзакцию использовать этот сегмент отката. По ее завершении сегмент отката можно удалить. Если подобная транзакция выполняется неоднократно, необходимо включить сегмент отката необходимого размера в систему на постоянной основе. Многие считают такие компоненты, как временные пространства, сегменты отката и журналы повторного выполнения, излишним расходованием ресурсов и выделяют для них минимум места на диске. Это заставляет вспомнить проблему, с которой столкнулась компьютерная индустрия 1 января 2000 года - и все из-за стремления сэкономить 2 байта в поле даты. Эти компоненты базы данных - ключевые в системе, и они должны иметь соответствующий размер (не большой и не маленький, а такой, как нужно). Конечно, самая большая проблема подхода с фиксацией до завершения транзакции состоит в том, что в случае прекращения изменений, база данных остается в неопределенном состоянии. Если заранее не подготовиться к этому, очень сложно продолжить выполнение неудачно завершившейся транзакции с момента останова. Например, если к столбцу применяется не функция LOWER(), а другая, например last ddl time = last ddl time + 1; Если цикл изменения прерван по ходу, как нам его перезапустить? Нельзя просто выполнить его повторно, поскольку к некоторым датам будет добавлено значение 2, а к некоторым - 1. При повторном сбое окажется, что к некоторым добавлено значение 3, к другим - 2, к остальным - I и так далее. Необходим более сложный подход, позволяющий фрагментировать данные. Например, можно обрабатывать все значения object names, начинающиеся с А, затем - начинающиеся с В, и так далее: tkyte@TKYTE816> create table to do 2 as 3 select distinct substr(object name, 1, 1) first char 4 from T 5 / Table created. tkyte@TKYTE816> begin 2 for x in (select * from to do) 3 loop 4 update t set last ddl time = last ddl time+l 5 where object name like x.first char 7 dbms output.put line(sql%rowcount rows updated); 8 delete from to do where first char = x. first char;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |