Алгоритм “міні-відкатів” в Oracle або ще раз про Write Consistency. Частина 1, Інші СУБД, Бази даних, статті

Введення

Якось раз, блукаючи просторами Інтернету, я в черговий раз побачив посилання на сайт Тома Кайта [1]. Виявивши, що Том продовжив дискусію циклом невеликих статей на цю тему, я теж вирішив зайнятися дослідженням цієї вельми непростий і мало вивченої проблеми – неявним перезапуском команди update у випадку, коли інші сеанси одночасно модифікують дані оновлюваної таблиці. Крім того, як я помітив, переважна більшість розробників та адміністраторів навіть і не підозрюють про існування таких перезапусків DML-команд і команди select for update, які можуть дуже істотно вплинути на логіку і продуктивність додатків. А якщо вони і знають про них, то не в повній мірі. Такі неявні, що виконуються Oracle перезапуски команд, я буду далі називати “міні-відкатами”.

Одним з моїх улюблених занять є вивчення всякого роду дампів і трасувань файлів. На мій погляд, це сильно допомагає в розумінні принципів та алгоритмів роботи будь-якої системи. Крім того, мені здається, що тільки знаючи як і чому це працює саме так, а не інакше, можна будувати ефективно працюючі системи і писати високопродуктивні програми. При цьому абсолютно не важливо, чи відноситься сказане до СУБД Oracle, операційної системі або взагалі, скажімо, до роботи двигуна внутрішнього згоряння. Тому я вибрав трохи інший спосіб для з’ясування того, що ж відбувається в обговорюваному Томом і відвідувачами його сайту випадку – вивчення трасування інформації, яку може надати Oracle. Ця інформація дасть нам можливість більш детально розібратися в алгоритмах роботи ядра Oracle. Крім того, такий спосіб не вимагає для виявлення “Міні-відкатів” використання тригерів, автономних процедур і т.д., що може так чи інакше вплинути на чистоту експериментів.

Незважаючи на те, що тут буде представлений досить великий обсяг інформації, зрозумілою в основному адміністраторам, сподіваюся, що розробники додатків теж проявлять інтерес до даної статті. Якщо комусь не цікаво вникати в усі подробиці розглянутої проблеми і вивчати трассіровочние файли, можна відразу перейти до кінця статті, де описані умови виникнення, алгоритм виконання “міні-відкатів” і частина можливих способів боротьби з ними.

Враховуючи те, що, якщо я наведу тут всі результати моїх досліджень, то це займе дуже багато місця і викличе головний біль у читачів :), я вирішив обмежитися публікацією лише частини результатів тестів. Але в кінці статті я постараюся коротко описати те, що ж таки відбувається при виникненні “міні-відкатів”, ну або, принаймні, те, що мені вдалося дізнатися стосовно цієї теми. Крім того, хочу зауважити, що практично все, про що тут буде йти мова, не документовано. Тому в статті можуть бути помилки, втрачені нюанси і тому подібні непорозуміння. Враховуючи складність теми та обсяг статті, можна навіть сказати, що вони напевно тут є :). Крім цього, подекуди свідомо, а подекуди й ні, я робив деякі спрощення в поясненнях, які, на мій погляд, не змінюють загальної картини, але істотно спрощують розуміння.

Пояснення та обговорення причин такої поведінки Oracle, тобто, чому це працює так, а не інакше, в мої плани не входить, тому що їх вже постарався викласти Том Кайт. Почитати його коментарі можна, пройшовши по посиланнях, зазначеним в [1] – [4]. Тому основною метою цієї статті буде показати, як це працює, тобто сам алгоритм “міні-відкатів” і альтернативний спосіб його вивчення з використанням деяких недокументованих можливостей. Крім того, я постараюся також дати кілька рекомендацій по можливості зменшення впливу “міні-відкатів” на продуктивність додатків.


Що таке “міні-відкат”?

Отже, суть проблеми. У нашій багатокористувацької системі є досить велика таблиця. Причому, не важливо, секціонірованная це таблиця, індекс-організована і т.д., чи має вона індекси чи ні – Суті це не міняє. В одному з сеансів ми запустили на виконання в режимі ізольованості READ COMMITED, прийнятому в Oracle за замовчуванням, команду update, вказавши у фразі WHERE (так званому предикаті) умова, за якою будуть вибиратися рядки, що підлягають оновленню. Після того, як почалося виконання команди update, інший сеанс оновив або видалив одну або більше рядків, які задовольняли предикату команди update першого сеансу на момент початку її виконання, але до зміни яких той ще не встиг дійти (зрозуміло, вже оновлені перших сеансів рядки заблоковані і не можуть ніким модифікуватися). При цьому:



  1. зміни та видалення рядків другому сеансі були зафіксовані до того моменту, коли перший сеанс, який виконує команду update, дістався до цих змінених рядків, або коли він дійшов до заблокували другий сеансом рядки і був змушений чекати фіксації зміни другої сеансом;

  2. змінені другому сеансі рядка на початок виконання перших сеансів команди update задовольняли умові, по якому відбувається оновлення (задовольняли предикату), а після модифікації перестали йому задовольняти.

В цьому випадку Oracle зробить “міні-відкат”: неявно перезапустить команду update і виконає оновлення вже нового набору рядків. Якщо ж у момент виконання команди update відбувається вставка нових (навіть задовольняють предикату) рядків іншими сеансами, то перезапуску команди не буде. Відбувається це тому, що виконання команди update складається з двох фаз:

1) узгоджене читання (consistent read) таблиці на момент початку виконання команди для знаходження рядків, що підлягають модифікації (в тому числі задовольняють предикату, якщо він заданий);
2) читання блоків даних таблиці в поточному (current read) режимі для модифікації знайдених на попередній фазі рядків.

Тому всі нові рядки, які були вставлені в таблицю іншими сеансами після початку виконання команди UPDATE, “не видно” під час першої фази команди update навіть незважаючи на фіксацію (COMMIT) таких вставок. Але, як ми побачимо пізніше, це вже не так, якщо проводиться “міні-відкат” цих команд.

Крім того, “міні-відкати” можуть виникати при виконанні команд DELETE, MERGE і SELECT FOR UPDATE. Так як в кожному випадку існує досить багато нюансів, то про це я постарався розповісти наприкінці статті, описавши алгоритм “міні-відкатів” для кожної з команд окремо.


Як буде проводитися тестування

Перед тим, як ми розглянемо результати тестування, я приведу короткий опис діагностичних подій, які буду використовувати. Опис кожної події буде виділено тим же самим кольором, яким виділена відповідна йому інформація в трасувань файлах, розміщених у цій статті.

Event 10219 – “monitor multi-pass row locking” (“відстежувати багатопрохідні блокування рядків”). Ця подія включає запис в трасувань файл серверного процесу, з яким з’єднаний користувач, діагностичної інформації при виникненні досліджуваної нами ситуації в разі, якщо виконується команда update або фаза update команди merge. Level 2 і вище включає видачу інформації про оновлюваних рядках.

Event 10218 – “dump uba of applied undo” (“видавати uba застосовуваних undo-записів”). Включення цієї події дозволяє отримувати у файлі трасування uba-адреси (uba – Undo Block Address) блоків, переглянутих і застосованих при відкат транзакції або команди (при явному відкат після видачі команди ROLLBACK або при неявному відкат команди, наприклад, при виникненні помилки ORA-00001: unique constraint і їй подібних).

Event 10200 – “consistent read buffer status” (“статус буфера при виконанні узгодженого читання”). При виконанні узгодженого читання блоку даних (НЕ заголовків сегментів і інших “службових” блоків) видає в трасувань файл список і статус всіх копій (клонів) цього блоку, що знаходяться в буферному кеші.

Event 10228 – “trace application of redo by kcocbk” (“відстежувати застосування інформації повтору в програмі kcocbk”). Записує в трасувань файл в придатному для аналізу вигляді всю redo-інформацію, генерується серверним процесом.

Тепер приступимо до тестування. Здебільшого тести мною проводилися на Oracle 9.2.0.7EE. Частина з них повторювалася на Oracle 8.1.7.4 і 10.1.0.4 і показала ідентичність алгоритмів. Всі трассіровочние файли отримані при виконанні тестів на Oracle 9.2.0.7EE. При виконанні тестів на інших версіях Oracle ваші трассіровочние файли, можливо, будуть трохи відрізнятися.

Для спрощення розуміння я постарався не сильно відхилятися від тестів, які наводилися на сайті Тома Кайта, але ввів ще один сеанс з метою демонстрації додаткових тонкощів і нюансів “міні-відкатів”. На початку кожного тесту я буду заново створювати таблицю wc_test в табличному просторі з розміром блоку 4k, керованому локально, з однаковим розміром екстента і з ручним (за допомогою freelists) управлінням вільним простором усередині сегмента (це не обов’язкові вимоги, просто вони спростять нам обробку отриманої інформації). В кожному тесті будуть запускатися кілька сеансів одного і того ж користувача. Ідентифікація сеансів буде здійснюватися за допомогою рядка запрошення в SQL * Plus. З цією метою була використана утиліта SQL * Plus 10.1.0.4.0 від версії Oracle 10g, яка дозволяє легше і наочніше здійснювати модифікацію рядка запрошення.


Тест 1 (з мінімально достатнім обсягом
трасування інформації)

У першому тесті у мене буде 4 сеансу, запущених від імені одного і того ж користувача. Перший сеанс – “Blocker” – буде створювати тестову таблицю, наповнювати її даними і блокувати командою update рядок з id = 3, при цьому не змінюючи у ній даних. Цей сеанс призначений тільки для імітації тривалості виконання команди update у другому сеансі – “Long update”, який оновлює по умові таблицю wc_test (тобто, в команді UPDATE є фраза WHERE – предикат) і який кожен раз буде зупинятися при спробі оновлення рядки з id = 3, заблокованої сеансом “Blocker”. Після цього у нас буде час для того, щоб у двох, що залишилися сеансах ми змогли зробити в wc_test деякі модифікації до закінчення команди update в “Long update”. Стовпець filler в таблиці wc_test доданий з тією метою, щоб в кожен блок даних містилося рівно по 3 рядки таблиці. Зроблено це виключно тільки для того, щоб наочніше показати всі процеси, що відбуваються в сеансі “Long update”.

Після того, як сеанс “Long update” буде заблокований, в третьому сеансі “First / Last” ми змінимо рядка в таблиці wc_test і зафіксуємо зміни таким чином, що, по-перше, рядок з id = 1, яку сеанс “Long update” пропустив як не задовольняє предикату при виконанні команди update, тепер буде задовольняти цього предикату, а рядок з id = 5 тепер не буде йому задовольняти. Як ми побачимо, зміна рядки з id = 5 викличе “міні-відкат” команди update в сеансі “Long update”.

Крім того, в цьому тесті я ввів четвертий сеанс “New rows”, який вставляє нові рядки в таблицю wc_test, які задовольняють предикату тестованої команди. Ці рядки вставляються після початку команди update в сеансі “Long update”. Моя мета в цьому випадку – показати, що, по-перше, ці рядки після “міні-відкату” будуть змінені сеансом “Long update”. По-друге, що блокування рядків сеансом “Long update” після “міні-відкату”, цілком можливо, буде відбуватися неодноразово.

Для наочності і зменшення розміру файлі трасування в цьому тесті включимо тільки трасування подій 10218 і 10219.

Отже, результати тесту:


Лістинг 1




SQL> –Session “Blocker”
SQL> conn scott/tiger@o92
Connected.
SQL> set sqlprompt “_USER”-Blocker”> ” wrap off linesize 100
SCOTT-Blocker> create table wc_test tablespace uniform_64k_block_4k as
2 select rownum id, rownum+10 y, cast(username as char(1000)) filler
3 from all_users where rownum <= 9;
Table created.
SCOTT-Blocker> column id format 99
SCOTT-Blocker> column y format 99
SCOTT-Blocker> column hex_dump_id format a18
SCOTT-Blocker> column hex_dump_y format a22
SCOTT-Blocker> column file# format 9999
SCOTT-Blocker> column block# format 99999999
SCOTT-Blocker> column row# format 9999
SCOTT-Blocker> column dba format 99999999
SCOTT-Blocker> column hex_dba format a8
SCOTT-Blocker> select id, y, dump(id,16) hex_dump_id, dump(y,16) hex_dump_y,
2 dbms_rowid.rowid_relative_fno(rowid) file#,
3 dbms_rowid.rowid_block_number(rowid) block#,
4 dbms_rowid.rowid_row_number(rowid) row#,
5 dbms_utility.make_data_block_address(dbms_rowid.rowid_relative_fno(rowid),
6 dbms_rowid.rowid_block_number(rowid)) dba,
7 to_char(dbms_utility.make_data_block_address(dbms_rowid.rowid_relative_fno(rowid),
8 dbms_rowid.rowid_block_number(rowid)),”FMxxxxxxxx”) hex_dba
9 from wc_test;
ID Y HEX_DUMP_ID HEX_DUMP_Y FILE# BLOCK# ROW# DBA HEX_DBA
— — —————— ———————- —– ——— —– ——— ——-
1 11 Typ=2 Len=2: c1,2 Typ=2 Len=2: c1,c 10 18 0 41943058 2800012
2 12 Typ=2 Len=2: c1,3 Typ=2 Len=2: c1,d 10 18 1 41943058 2800012
3 13 Typ=2 Len=2: c1,4 Typ=2 Len=2: c1,e 10 18 2 41943058 2800012
4 14 Typ=2 Len=2: c1,5 Typ=2 Len=2: c1,f 10 19 0 41943059 2800013
5 15 Typ=2 Len=2: c1,6 Typ=2 Len=2: c1,10 10 19 1 41943059 2800013
6 16 Typ=2 Len=2: c1,7 Typ=2 Len=2: c1,11 10 19 2 41943059 2800013
7 17 Typ=2 Len=2: c1,8 Typ=2 Len=2: c1,12 10 20 0 41943060 2800014
8 18 Typ=2 Len=2: c1,9 Typ=2 Len=2: c1,13 10 20 1 41943060 2800014
9 19 Typ=2 Len=2: c1,a Typ=2 Len=2: c1,14 10 20 2 41943060 2800014
9 rows selected.
SCOTT-Blocker> update wc_test set y=y where id=3;
1 row updated.
SCOTT-Blocker>

Лістинг 2




SQL> –Session “Long update”
SQL> conn scott/tiger@o92
Connected.
SQL> set sqlprompt “_USER”-Long update”> ”
SCOTT-Long update> alter session set max_dump_file_size
= unlimited tracefile_identifier=”Long_Test_1″;
Session altered.
SCOTT-Long update> alter session set events “10218 trace name context forever, level 10:
2 10219 trace name context forever, level 10”;
Session altered.
SCOTT-Long update>
–alter session set events “10200 trace name context forever:10228 trace name context forever”;
SCOTT-Long update> update wc_test set y=88 where y=15 or id in(2,3,4) – Ми чекаємо … ;

Сеанс “Long update” чекає завершення транзакції в сеансі “Blocker”.

Тепер в сеансі “First / Last” виробляємо модифікації і відразу ж фіксуємо зміни. Після цього, коли “Long update” добереться до рядка з id = 5, він виявить, що на момент старту команди update цей рядок задовольняла предикату (тобто, повинна бути змінена, оскільки y = 15 на момент старту команди), але в даний момент вона не задовольняє предикату, так як y = 99 і транзакція, яка змінила це значення, вже зафіксувала зміни:


Лістинг 3


 





SQL> –Session “First/Last”
SQL> conn scott/tiger@o92
Connected.
SQL> set sqlprompt “_USER”-First/Last”> ”
SCOTT-First/Last> update wc_test set y=15 where id=1;
1 row updated.
SCOTT-First/Last> update wc_test set y=99 where id=5;
1 row updated.
SCOTT-First/Last> commit;
Commit complete.
SCOTT-First/Last>

Під час “New rows” вставляємо 3 нові рядки, що задовольняють предикату “Long update” (y = 15), фіксуємо транзакцію, після чого модифікуємо рядок з id = 10 так, що вона тепер не задовольняє предикату. Оновлення викличе блокування “Long update”, коли той дійде до рядка з id = 10. Це оновлення поки не фіксуємо:


Лістинг 4


 





SQL> –Session “New rows”
SQL> conn scott/tiger@o92
Connected.
SQL> set sqlprompt “_USER”-New rows”> ”
SCOTT-New rows> insert into wc_test values(10,15,” “);
1 row created.
SCOTT-New rows> insert into wc_test values(11,15,” “);
1 row created.
SCOTT-New rows> insert into wc_test values(12,15,” “);
1 row created.
SCOTT-New rows> commit;
Commit complete.
SCOTT-New rows> update wc_test set y=99 where id=10;
1 row updated.
SCOTT-New rows>

Повертаємося в сеанс “Blocker” і фіксуємо транзакцію. В результаті сеанс “Long update” продовжить виконання команди update, виявить, що рядок з id = 5 тепер не задовольняє предикату і почне виконання команди update спочатку, зробивши “міні-відкат”. Але, як ми побачимо, дійшовши до заблокованої рядки з id = 10, він буде чекати завершення транзакції в сеансі “New rows”:




SCOTT-Blocker> commit;
Commit complete.
SCOTT-Blocker>

Нарешті, в сеансі “New rows”: теж зафіксуємо транзакцію:




SCOTT-New rows> commit;
Commit complete.
SCOTT-New rows>

Тепер сеансу “Long update” ніхто не заважає завершити команду update:


Лістинг 5




6 rows updated.
SCOTT-Long update> select id, y from wc_test;
ID Y
———- ———-
1 88
2 88
3 88
4 88
5 99
6 16
7 17
8 18
9 19
10 99
11 88
12 88
12 rows selected.
SCOTT-Long update>

В результаті ми бачимо, що сеанс “Long update” модифікував рядки з id = 2,3,4, які ми не змінювали під час тестів, рядок з id = 5 не змінена, хоча на момент початку виконання update вона задовольняла предикату. Крім того, видно, що змінені рядки з id = 11 і 12, яких взагалі не було на момент початку виконання команди update в сеансі “Long update”.

Тепер подивимося, яку ж інформацію можна почерпнути з файлів трасувань. Як я вже говорив, колір тексту файлі трасування буде відповідати кольору, яким виділено опис кожної події на початку статті. Ключові місця будуть виділятися жовтим фоном, коментарі та пояснення до вмісту ключових місць трасувань файлів я буду писати з відступом, чорним кольором відразу за коментованим текстом. Нагадую, що трассіровочние файли, які представлені в лістингах 6 і 7, створені сеансом “Long update”, який запускався мною під Oracle 9.2.0.7. Зверніть увагу на те, що і обсяг трасування інформації, і її вміст у вас можуть відрізнятися від отриманих мною. Це залежить від багатьох факторів, у тому числі від версії Oracle, установки деяких параметрів ініціалізації (наприклад, TRANSACTION_AUDITING), установок режиму SUPPLEMENTAL LOGGING і т.д.


Лістинг 6




updaul mode is UPDATE NOT LOCKED snap oldsnap env: (scn: 0x0004.00f32abf  xid:
0x0000.000.00000000 uba: 0x00000000.0000.00 statement num=0 parent xid: xid:
0x0000.000.00000000 scn: 0x0000.00000000 0sch: scn: 0x0000.00000000) env: (scn:
0x0000.00000000
xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 statement num=0 parent xid: xid:
0x0000.000.00000000 scn: 0x0000.00000000 0sch: scn: 0x0000.00000000)


Бачимо, що сеанс почав оновлення ще не заблокованих рядків з SCN = 0x0004.00f32abf

updrow: kauupd table 0 00018545.02800012.1 code 0
updrow: kauupd table 0 00018545.02800012.2 code 0
updrow: kauupd table 0 00018545.02800013.0 code 0
updrow: kauupd table 0 00018545.02800013.1 code 1

Відбувається оновлення рядків у таблиці. 0x00018545 = 99653 – це OBJECT_ID таблиці  wc_test в DBA_OBJECTS. table 0 – це номер таблиці у блоці даних. В нашому випадку в блоці одна таблиця. 02800012.1 і т.д. – Це dba (Data Block Address)  і номера змінюваних рядків. Порівняйте це з HEX_DBA і ROW # в лістингу 1. Процес оновлення був заблокований сеансом “Blocker” на рядку з id = 3  (Рядок 00018545.02800012.2). Після фіксації не змінила значення стовпця y  транзакції в сеансі “Blocker” сеанс “Long update” продовжив роботу. При модифікації рядка з id = 5 виявилося, що рядок перестала задовольняти  предикату команди update (code 1) і …
uba: 0x00c01256.3c42.54
uba: 0x00c01256.3c42.53
uba: 0x00c01256.3c42.52

… “Long update” справив відкат всіх змін, що нам показало подія 10218.  При цьому, що цікаво і цілком розумно, незважаючи на те, що блокування з усіх раніше  заблокованих рядків зняті, “Long update” не “розбудив” інші сеанси,  які чекають заблокованих при виконанні фази “mode is UPDATE NOT LOCKED” рядків.  У цьому можна переконатися, наприклад, спробувавши ще в одному сеансі в момент після  блокування “Long update” (відповідний кінця лістингу 2) виконати  оновлення рядки з id = 2 – цей сеанс буде заблокований до закінчення транзакції  в сеансі “Long update”.
updaul mode is LOCK snap oldsnap env: (scn: 0x0004.00f32ae9  xid: 0x0002.05f.0001658f
uba: 0x00000000.0000.00 statement num=0 parent xid: xid: 0x0000.000.00000000 scn:
0x0004.00f32ae9 0sch: scn: 0x0000.00000000) env: (scn: 0x0004.00f32abf xid:
0x0000.000.00000000
uba: 0x00000000.0000.00 statement num=0 parent xid: xid: 0x0000.000.00000000 scn:
0x0000.00000000 0sch: scn: 0x0000.00000000)
Після відкоту всіх змін, які зробила команда update, Oracle намагається заблокувати всі рядки, що задовольняють предикату. Зверніть увагу, що SCN команди змінився (збільшився), тобто команда update перезапускається як би в новий момент часу,  щоб “побачити” все відбулися в таблиці зміни задовольняють  предикату рядків з моменту свого попереднього запуску.

updrow: kddlkr table 0 00018545.02800012.0 code 0
updrow: kddlkr table 0 00018545.02800012.1 code 0
updrow: kddlkr table 0 00018545.02800012.2 code 0
updrow: kddlkr table 0 00018545.02800013.0 code 0
updrow: kddlkr table 0 00018545.02800015.0 code 17
updrow: kddlkr table 0 00018545.02800015.1 code 0
updrow: kddlkr table 0 00018545.02800015.2 code 0

Тепер перша фаза команди update – фаза узгодженого читання – виявить,  що рядок з id = 1 теж задовольняє умові команди update, що нові рядки з id = 10,11  і 12 – теж, а рядок c id = 5 – ні. Але рядок з id = 10 (00018545.02800015.0) буде  заблокована командою update в “New rows”, тому “Long update” продовжить  роботу тільки після фіксації змін в “New rows”. Після цієї фіксації “Long  update “виявляє, що рядок з id = 10 була змінена так, що перестала задовольняти  предикату (про це свідчить code 17), але, незважаючи на це,
продовжує проводити пошук і блокування всіх задовольняють предикату рядків таблиці. Після закінчення перегляду всієї таблиці перевіряється, виникала хоч раз ситуація, коли code був> 0 при спробі блокування рядків. Якщо це так, як і сталося в нашому випадку, то …
updaul mode is LOCK snap oldsnap env: (scn: 0x0004.00f32af1  xid: 0x0002.05f.0001658f
uba: 0x00c01256.3c42.5a statement num=0 parent xid: xid: 0x0000.000.00000000 scn:
0x0004.00f32af1 0sch: scn: 0x0000.00000000) env: (scn: 0x0004.00f32ae9 xid:
0x0002.05f.0001658f
uba: 0x00000000.0000.00 statement num=0 parent xid: xid: 0x0000.000.00000000 scn:
0x0004.00f32ae9 0sch: scn: 0x0000.00000000)

Відбувається знову спроба блокування всіх задовольняють предикату рядків з
новим SCN. При цьому блокування з уже заблокованих на фазі “mode is LOCK” рядків не знімаються, ніяких змін і ніякої redo-інформації для таких рядків не генерується. Зверніть увагу, що ідентифікатор (XID) транзакції не змінюється. Такі спроби блокувань будуть відбуватися або до тих пір, поки всі виклики функції kddlkr не завершаться з code 0, або до тих пір, поки число таких спроб не перевищить 5000 (подробиці див далі в описі алгоритмів “міні-відкатів”).
updrow: kddlkr table 0 00018545.02800012.0 code 0
updrow: kddlkr table 0 00018545.02800012.1 code 0
updrow: kddlkr table 0 00018545.02800012.2 code 0
updrow: kddlkr table 0 00018545.02800013.0 code 0
updrow: kddlkr table 0 00018545.02800015.1 code 0
updrow: kddlkr table 0 00018545.02800015.2 code 0

І тільки тоді, коли всі code при черговій спробі блокування рядків  на фазі “mode is LOCK” будуть рівні нулю, відбувається оновлення всіх раніше  заблокованих рядків на завершальній фазі “mode is UPDATE ALL LOCKED”.
updaul mode is UPDATE ALL LOCKED snap oldsnap env: (scn: 0x0004.00f32af1  xid:
0x0002.05f.0001658f uba: 0x00c01256.3c42.5a statement num=0 parent xid: xid:
0x0000.000.00000000 scn: 0x0004.00f32af1 0sch: scn: 0x0000.00000000) env: (scn:
0x0004.00f32ae9
xid: 0x0002.05f.0001658f uba: 0x00000000.0000.00 statement num=0 parent xid: xid:
0x0000.000.00000000 scn: 0x0004.00f32ae9 0sch: scn: 0x0000.00000000)
updrow: kauupd table 0 00018545.02800012.0 code 0
updrow: kauupd table 0 00018545.02800012.1 code 0
updrow: kauupd table 0 00018545.02800012.2 code 0
updrow: kauupd table 0 00018545.02800013.0 code 0
updrow: kauupd table 0 00018545.02800015.1 code 0
updrow: kauupd table 0 00018545.02800015.2 code 0

Тепер це оновлення обов’язково завершиться успішно (зрозуміло, якщо не виникне  форс-мажорних обставин у вигляді SHUTDOWN ABORT, доступу до даних через пошкоджений  індекс і т.д.), так як: 1) ця фаза виконується з тим же SCN, що і остання  фаза “mode is LOCK”. Що в свою чергу означає, що після виконання  узгодженого читання блоків таблиці wc_test на цій завершальній фазі ми отримаємо
Того ж набір рядків, що підлягають зміні, що й на останній фазі “mode is LOCK”. Крім того, ці рядки вже заблоковані нашої транзакцією на попередніх кроках, що гарантує їх незмінність з моменту цієї блокування.

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


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

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

Ваш отзыв

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

*

*