Пример WAL, прочие структуры данных.
Настало время привести пример. Для упрощения я опущу работу со страницами, полями pageId и покажу как связаны все записи в WAL.
Пусть есть некая страница X с двумя поля
k и n. Есть также две параллельные транзакции, одна увеличивает k на 2, а вторая уменьшает n на 3. Затем первая транзакция снова увеличивает k на 9. Пока ни одна не успела сделать коммит. Каждая запись в WAL я обозначу числом, которое равно LSN, но как вы помните, писать LSN не обязательно — для этого подойдёт и адрес лога. Начну писать лог с середине, чтобы тоже лишний раз не путаться. Вместо "update" будет просто "u", "commit" — "c", а "compensation" заменю на "clr"Напомню, что запись с типом update содержит поля prevLSN, TrId, pageId (опустим), redoData и undoData:
77. [-, 1, "u", k+=2, k-=2]
78. [-, 2, "u", n-=3, n+=3]
79. [77, 1, "u", k+=9, k-=9]
Первые две записи содержат "-" вместо поля prevLSN, поскольку они являются первыми изменениями в своих транзакциях. Запись 3 (LSN = 79) в поле prevLSN содержит 77 — это указание на LSN предыдущей записи, сделанной этой транзакцией, т.е. записи с LSN = 77.
Теперь пусть транзакция 2 сделать коммит, а транзакция 1 сделает полный роллбек предыдущих обновлений, затем увеличивать k на 13, а затем сделает коммит. Получится следующая ситуация:
77. [-, 1, "u", k+=2, k-=2]
78. [-, 2, "u", n-=3, n+=3]
79. [77, 1, "u", k+=9, k-=9]
...
88. [78, 2, "c"]
89. [79, 1, "clr", k-=9, 77]
90. [89, 1, "clr", k-=2, -]
91. [90, 1, "u", k+=13, k-=13]
92. [91, 1, "c"]
Для начала посмотрим на запись 89. В поле redoTheUndoData содержится
k-=9 — ровно тоже самое, что содержалось в предыдущей записи 79 с типом update в поле undoData. Фактически здесь при создании CLR записи происходит копирование данных. В поле undoNextLSN содержится 77, т.е. указатель на следующая запись, которую нужно откатить во время этого роллбека. Оно скопировано из prevLSN той записи, которая откатывается. У записи с LSN 79 в поле prevLSN записано 77. Если бы в undoNextLSN не было бы данных, это означало бы, что роллбек частичный, т.е. откатывается только одна запись из двух. Другими словами, наличие записи в undoNextLSN означает, что роллбек ещё не завершён. Когда роллбек откатит последнюю запись в CRL в undoNextLSN не будет ссылки. Именно этот случай показан в записи под номером 90. Здесь тоже CLR запись, но следующий за ней записи нет, поэтому в поле undoNextLSN стоит "-".
Помимо особого способа заполнения WAL, ARIES ещё нужно поддерживать рядом с журналом две таблицы. Их можно собрать по WAL, поэтому достаточно держать их в памяти.
Первая — это таблица грязных страниц dirty pages table (DPT). В ней содержится всего два поля: pageId для номера страницы и указатель на первую запись в WAL, после которой страница стала грязной. Это поле называется recoveryLSN и оно используется для поиска той записи, с которой нужно начинать восстановление. Таблица меняется в двух случаях. Если очередная запись в WAL изменила страницу, которая была чистой — сюда добавляется номер этой страницы и номер LSN записи. Если же страница синхронизируется на диск, запись из DPT удаляется.
Вторая нужная для работы ARIES структура — это таблица транзакций transactions table (TT). В ней находятся все незавершённые транзакции с указанием их идентификатора TrID, а также последнего изменения, произошедшего в рамках этой транзакции lastLSN. У каждой записи в WAL есть указатель на предыдущую запись в поле prevLSN, т.е. все записи объединены в связный список. В TT указывается транзакция и её последнее изменение, т.е. фактически запись здесь — это указатель на голову связного списка. Записи в TT обновляются всякий раз когда в рамках транзакции происходит изменение. Запись удаляется при коммите или полном роллбеке.