Сегменти відкату в СУБД 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-сайті ЗАТ "ФОРС-ХОЛДИНГ" – http://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>

*

*