Сегменти відкату в СУБД Oracle, Резервне копіювання, Різне, статті

Сегменти відкату (rollback segments), звані також undo сегментами (наприклад, USN – Undo Segment Number) призначені для виконання трьох основних завдань:


  1. Виконання команди rollback.
  2. Відновлення примірника після збою (instance recovery).
  3. Забезпечення несуперечності зчитувальних з бази даних (read consistency) [прим.1].

Перший і другий пункти висвітлюються в літературі досить докладно і розумно, а ось повного опису функціональних можливостей Oracle по пункту 3 я не зустрічав.

Також в літературі я не зустрічав виразного опису механізму функціонування сегментів відкоту. Найкраще опис було, як це не дивно, в старій документації по Oracle версій 3, 4 і 5. Правда, в той час не було ще сегментів відкоту, а замість них був файл before image, але концепції залишилися колишніми. Тамтешня концепція “голів і хвостів”, на мій погляд, краще допомагає зрозуміти схему роботи сегментів відкату.

Давайте спочатку поговоримо про несуперечності, а потім – про пристрій сегментів відкоту. Звичайно, ці теми не охоплюють всіх аспектів адміністрування сегментів відкоту, але дуже корисні для розуміння їх структури та функціонування.

Несуперечність зчитувальних даних

Проблема несуперечності читань (read consistency) пов’язана з “заморожуванням” даних в СУБД на який-небудь момент часу та отримання користувачем звіту за цими даними. Під час отримання звіту якими користувачами іншим користувачам повинна бути надана можливість одночасної модифікації тих же самих даних (у противному випадку різко знижується конкурентоспроможність даних), тому вміст БД безперервно змінюється, а “заморожені” дані представляють собою знімок (snapshot) даних бази на якийсь момент часу. Реалізовано механізм таких знімків в СУБД Oracle через сегменти відкату (rollback segments або undo segments), в яких зберігаються старі версії даних. У випадку, коли отчетет намагається прочитати дані, модифіковані іншими користувачами за час його роботи, старі версії даних на момент початку звіту зчитуються з сегментів відкоту. Oracle не забезпечує гарантованого читання попередніх версій даних через можливе їх затирання в сегментах відкату іншими користувачами. У разі затирання в сегментах відкату старих версій даних, користувачеві, що виконує звіт, видається помилка “ORA-1555 Snapshot too old, rollback segment too small”. Чим довше виконується звіт, тим більша ймовірність затирання старих версій даних. Розроблена у форс система GRC призначена для гарантованого зберігання старих версій даних на час роботи звіту.

Oracle забезпечує несуперечність на рівні оператора і на рівні транзакції.

Несуперечність на рівні оператора.

Оператор вибірки select мови SQL завжди зчитує несуперечливі дані. Це означає, що якщо в якийсь момент оператор select зчитує-яку рядок таблиці і через деякий час зчитує цю ж рядок повторно [прим.2], То Oracle видає, або ту ж саму інформацію, або помилку ORA-1555.

Несуперечність на рівні транзакції.

Oracle підтримує такі типи транзакцій:


  1. Транзакція, що читає зафіксовані іншими транзакціями дані під час її роботи. Користувач може явно почати таку транзакцію за допомогою оператора set transaction read write . Транзакція за замовчуванням (від початку сеансу до першої команди commit / rollback, від commit / rollback до наступної commit / rollback або до кінця сеансу) є транзакцією цього типу. Перед початком будь-якої транзакції за умовчанням неявно виконується оператор set transaction read write. Якщо два оператора select в різних частинах транзакції цього типу звертаються до одних і тих же рядках у таблиці БД, то можливе отримання різних (суперечливих) результатів. Протиріччя виникне в тому випадку, якщо який-небудь користувач модифікував і зафіксував зміни цих рядків на інтервалі часу між двома вищезгаданими операторами select. Тобто в рамках однієї транзакції можливо читання даних, модифікованих та зафіксованих (committed) іншими користувачами [Прім.3]. Слід зазначити, що поява помилки ORA-1555 можливо у випадку, якщо Oracle не може забезпечить несуперечність на рівні оператора select, що входить в дану транзакцію.
  2. Транзакція “тільки для читання”. Користувач може почати таку транзакцію за допомогою оператора set transaction read only і закінчити оператором commit/rollback . Усередині такої транзакції можна вживати тільки оператори select , Оператори DML неприпустимі. При зверненні операторів select з різних частин транзакції до одних і тих же рядках даних Oracle видає або однакові несуперечливі дані, що знаходилися в БД на момент початку транзакції (set transaction read only), або помилку ORA-1555.
  3. Серіалізуемим транзакція. Така транзакція починається оператором set transaction isolation level serializable . Такі транзакції призначені для сериализации оновлень при конкурентній роботі декількох користувачів з одними і тими ж даними. За допомогою цих транзакцій, одночасно видаються різними користувачами, результат модифікації даних в базі такий, як якщо б дотримувалася черговість, тобто спочатку один користувач почав і закінчив транзакцію, після нього другий провів свою транзакцію, потім третій і т.д. Взагалі кажучи, процес серіалізациі транзакцій дуже складний, і не завжди можливо серіалізовать одночасно проводяться декількома користувачами транзакції. Відзначимо, що якщо запис в яку-небудь таблицю або таблиці ведеться тільки одним користувачем, то необхідність у сериализации відпадає. Серіалізуемим транзакції завжди несуперечливі, то є всі вибірки отримують дані з “знімка” бази, зробленого на момент видачі оператора set transaction isolation level serializable . За умови, що запис здійснюється тільки в робочі таблиці даного користувача, можна трактувати серіалізуемим транзакції: як транзакції тільки для читання (read only) з можливостями видачі операторів DML (insert, delete і update). Це зручно, коли програма отримання складного звіту зберігає результати в проміжних робочих таблицях [прім.4], З яких потім звіт виводиться на друк.

Механізм роботи сегментів відкоту

При створенні сегмента відкату виділяється minextents поки що порожніх екстентів, причому перший оракловскій блоків першого екстента зарезервований під таблицю транзакцій, або заголовок сегмента відкату. Ми будемо говорити, що голова і хвіст [прім.5] Сегмента відкату знаходяться на початку другого блоків першого екстента відразу після заголовка (див. рис. 1). При призначенні транзакції на цей сегмент відкоту, інформація відкату записується, починаючи з другого блоку, причому у кожної активної транзакції існує також голова і хвіст в сегменті відкату. При модифікації транзакцією даних і заповненні сегмента відкату голова транзакції просувається, заповнюючи блоки першого і можливо наступних екстентів. При цьому хвіст транзакції збігається з хвостом сегмента відкату (див. рис 2.). Подивитися, де знаходиться голова сегментів відкоту з точністю до екстента і блоку можна в таблиці V $ ROLLSTAT в стовпцях Curext і Curblk відповідно (перший екстент має номер 0).
 





 

Після того, як транзакція буде зафіксована оператором commit, хвіст сегмента відкату переміститься до голови (див. рис. 3). Тут голова і хвіст будуть вказувати на перший байт наступного за хвостом транзакції блоку. Запис інформації відкату від наступної призначеної на даний сегмент транзакції буде міститися в блоки екстентів, пересуваючи голову сегмента і транзакцій далі від хвоста за годинниковою стрілкою. Після фіксації транзакції інформація відкату, записана в екстенти 1 і 2, стає “неактивній” і може бути використана для отримання звітом старих версій даних, тобто для забезпечення несуперечності.

Довга транзакція під час запису інформації пересуває голову сегмента відкату. За заповненні екстента, голова переміщається в наступний екстент, потім в наступний і так далі – по колу. Кажуть, що сегменти відкату мають кільцеву структуру (змія). Це означає, що після заповнення четвертого екстента, запис триває в перший або, що те ж саме, голова переміщається в перший екстент [прім.6]. При цьому дотримується важливе правило: хвіст сегмента відкату ніколи не може переміститися в той екстент, де знаходиться голова. Таким чином, якщо голова сегмента відкату знаходиться в екстента 4, останній блок якого заповнений (хвіст перемістився в кінець екстента), і для продовження транзакції потрібне подальше переміщення хвоста за годинниковою стрілкою, то в сегмент відкоту додається п’ятий екстент (змія розтягується), і голова переміщається в його перший блок (рис. 4). Кількість розтягувань з моменту запуску бази даних протоколюється в стовпці Extends подання V $ ROLLSTAT.
 





 

Якщо кілька транзакцій записують в сегмент інформацію відкоту одночасно, то хвіст сегмента збігається з хвостом найпершої призначеної на сегмент відкоту активної транзакції, а голова сегмента – з головою останньої записала інформацію відкоту активної транзакції. У міру фіксації транзакцій оператором commit хвіст сегмента відкату намагається наздогнати голову. Якщо всі транзакції фіксовані і в даному сегменті відкоту немає активних транзакцій, то хвіст сегмента збігається з його головою [прім.7] (Рис. 3). Екстент може бути використаний для запису інформації відкату від декількох транзакцій, але в кожному блоці екстента зберігається інформація від тільки однієї транзакції.

Вище ми розглянули випадок, коли екстенти додаються в сегменти відкату. Тепер розглянемо механізм видалення екстентів (змія стискається) з сегментів відкоту. Всім відомо, що екстенти можна видалити (повернути в список вільних екстентів FET $) двома способами: за допомогою видачі оператора alter rollback segment … Shrink і автоматично за допомогою завдання слова optimal у фразі storage при створенні або модифікації сегмента відкату. Перший спосіб, хоча і має декілька “підводних каменів”, зазвичай питань не викликає. А ось другий спосіб ще як викликає. Звичайний питання в таких випадках: “А чому сегмент відкоту збільшився до 20 мегабайт, а стискатися не хоче, хоча optimal задано 5 мегабайт?”. Справа в тому, що, для запуску процедури видалення екстентів з сегменту відкоту, потрібно виникнення певної події, а саме, перевірка optimal і видалення екстентів проводиться при пересуванні голови сегмента на наступний екстент. Таким чином, сегмент відкоту не стискується відразу ж по завершенні транзакції, що викликала розширення сегмента відкату. Стискання відбувається, коли інші транзакції, призначені на цей сегмент відкоту, заповнять поточний екстент до кінця. Кількість стиснень з моменту запуску БД протоколюється в стовпці Shrinks подання V $ ROLLSTAT.

Тепер розглянемо одну із проблем, проблему блокуючих транзакцій, Яка може виникнути в адміністратора багатокористувацької системи. Припустимо, що який або недисциплінований користувач або оператор почав проводити транзакції (наприклад, вставив рядок в яку-небудь таблицю), не завершив її за допомогою оператора commit і пішов надовго у своїх справах. Інформація відкату для цієї транзакції записалася в якийсь сегмент відкоту, тобто голова сегмента просунулася на якусь відстань і транзакція залишилася активною. Хвіст цієї транзакції знаходиться поруч з головою, можливо, в одному і тому ж блоці. Чи не завершив транзакцію користувач пішов, а безліч інших користувачів продовжують модифікацію даних. Інформація відкату від різних транзакцій записується в оперативні сегменти відкату, у тому числі і в той, куди була призначена транзакція минулого користувача. Голова цього сегмента просувається далі за годинниковою стрілкою і підходить до екстента, в якому знаходиться хвіст активної транзакції пішов користувача. До цього моменту хвіст цієї активної блокує транзакції став хвостом всього сегмента відкату. Так як голова не може переміститися в екстент, в якому знаходиться хвіст, то додається новий екстент, і голова переміщається в нього. Потім ще один і т.д., незважаючи на те, що між головою і хвостом всі блоки всіх екстентів неактивні (всі транзакції були зафіксовані операторами commit). Це може привести до переповнення табличного простору сегментів відкоту. Звідси мораль – адміністраторові треба придумати систему відстрілу недисциплінованих користувачів, наприклад, використовувати idle time в profile, хоча це і не завжди можливо. З’ясувати, які користувачі блокують сегменти відкоту можна за допомогою запуску наступного скрипта:

select S.Username,S.Sid,S.Serial#,T.Start_time,T.Xidusn
  from V$Session S, V$TRANSACTION T, V$ROLLSTAT R
 where S.Saddr = T.Ses_addr 
   and T.Xidusn = R.Usn
   and ((R.Curext = T.Start_uext-1)
       or  
       ((R.Curext = R.Extents – 1) and (T.Start_uext = 0))
    );

Чим більше кількість екстентів в сегменті, тим менше ймовірність долучення екстента в сегмент відкоту. З цієї точки зору, двадцять маленьких екстентів в сегменті відкату краще, ніж два великих, що займають таке ж місце.

Тепер трохи про механізмі, який підтримує несуперечність даних. Після того, як голова сегмента відкату здійснить повний оборот, інформація відкату від неактивних транзакцій починає записуватись нову. Кількість таких перезаписів (повних оборотів) з початку запуску СУБД відображено в стовпці Wraps подання V $ ROLLSTAT. Таким чином, теоретично можна зробити всі сегменти відкату такими великими, що поточні транзакції не будуть змушувати голови всіх сегментів відкоту здійснювати повний оборот за прийнятне для роботи звіту час, скажімо, кілька годин. Але практично зробити це неможливо з ряду очевидних причин, головна з яких – неможливість передбачити заздалегідь обсяг поновлення БД іншими транзакціями. Таким чином, неможливо забезпечити гарантовану несуперечність читань, тобто гарантоване відсутність виникнення помилки ORA-1555 при роботі, наприклад, тривалого звіту (read only транзакції), що істотно збіднює функціональні можливості. Але, на щастя, гарантувати несуперечність читань можна. Для цього потрібно лише штучно заблокувати кожен оперативний сегмент відкоту короткими транзакціями до початку виконання звіту [прім.8] І розблоковувати, тобто зафіксувати ці транзакції, після виконання. Отже, можна відкрити декілька додаткових сеансів по числу сегментів відкоту, в кожному з них заблокувати короткою транзакцією сегмент відкоту за допомогою оператора set transaction use rollback segment і запустити звіт. При цьому модифікуючі сеанси можуть отримати від Oracle повідомлення про помилку через неможливість розширення заблокованого сегмента відкоту, але виконує звіт сеанс ніколи не отримає ORA-1555.

Процедура штучного блокування сегментів відкоту повинна була б бути реалізована в рамках програмного забезпечення Oracle, але, на жаль, цього поки немає, принаймні в 8.1.6. Щоб заповнити цю прогалину, в ЗАТ “ФОРС-ХОЛДИНГ” була реалізована система GRC (Guaranteed Read Consistency), Що дозволяє блокувати сегменти відкату різними способами з метою гарантування несуперечності читань. Зокрема, система програмної блокування сегментів відкоту пропонується до реалізації в даний час. Вартість системи можна дізнатися з рекламного листка, представленого на Web-сайті ЗАТ “ФОРС-ХОЛДИНГ” – www.fors.ru/.

Примітки

1.Іноді в російськомовній літературі по базах даних термін consistency перекладають як “цілісність”, що, на мій погляд, неправильно, тому що російське слово “цілісність” зарезервовано для перекладу англійської слова “integrity”.

2. Передбачається, що на інтервалі часу між читаннями даний рядок може бути модифікована іншими користувачами з фіксацією змін (commit) довільну кількість разів.

3. У стандарті мови SQL транзакції цього типу так і називаються: read committed. Починаючи з версії Oracle 8.1.5, для оператора set transaction read write передбачений синонім – оператор set transaction isolation level read committed. 

4. До речі, починаючи з версії сервера Oracle 8.1.5, реалізовані спеціальні робочі таблиці, що діють в рамках сеансу або транзакції користувача, з якими операції DML проводяться набагато швидше, ніж з звичайними таблицями через те, що запис оновлень в журнали (redo logs) блокується. За рахунок цього також досягається економія дискової пам’яті в redo logs.

5. Тварина, яке слід уявляти собі при вживанні термінів “голова” і “хвіст” – змія. Змія прагне згорнутися в кільце і вкусити себе за хвіст. Змія гумова, розтягується і стискається.

6. Нумерація екстентів особливого сенсу не має і приводиться тут тільки для наочності. Серед усіх екстентів сегмента відкату особливий статус має лише той екстент, у першому блоці якого розташовується таблиця транзакцій. Номер цього екстента в поданні DBA_EXTENTS – 0, і він не може бути видалений з сегменту відкоту

7. Змія вкусила себе за хвіст і заспокоїлася.

8. Насправді дійсність складніше наведеного мною опису, так як в інформація про зафіксовані транзакції може бути затерта нової транзакцією в таблиці транзакцій, але, на щастя, Oracle може в цьому розібратися.


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*