Header RSS-подписка на обновления сайта eMail-подписка на обновления сайта

Как перестать называть журнал транзакций SQL Server лог-файлом и прекратить борьбу за его размер. Часть 5/12.


366bef3a

Итого, различие между двумя видами возобновлений, рассмотрением которых завершилась часть предыдущая, кроется в числе фаз составляющий каждый из процессов, их порядке и возможности явного указания того места где надлежит случиться фазе undo в варианте «возобновления-с-носителя». С точки зрения автора у обоих процессов больше общего, чем различий. Хотя считать их процессами совпадающими абсолютно будет несомненной ошибкой. Вторичным их отличием является тот факт, что «фича» fast recovery неприменима ко второму из рассматриваемых вариантов (то есть как раз к «возобновлению-с-носителя») ни в какой редакции. Объясняется это тем, что по окончании обработки последнего звена бэкапа движку сервера может потребоваться внести корректировку в метаданные восстановленной базы. Пока необходимость таких изменений (и, если они требуются, сами изменения) не будет установлена в точности доступ к базе запрещен. Иными словами, восстановление базы тотально несовместимо с «быстрым» возобновлением, все без исключения (в том смысле, что «крутизна» редакции сервера здесь совершенно не играет роли) ждут «прогона» всех фаз.

И снова checkpoint...

Возвращаемся к разговору прерванному в результате экскурса в анализ сходства и различия восстановления (restore) и возобновления (recovery). Если помните, нашей главной целью в настоящий момент являются контрольные точки, а прервались мы на том месте где опция сервера recovery interval поменяла (не без нашей помощи) свое значение на 25 минут. Так вот — в чем суть данной опции и какое ее отношение к исследуемому вопросу?

Поиск связей мы начнем с осознания того факта, что в любой момент времени движок сервера может вычислить ту длительность, которую предположительно ему же придется потратить на наш трех-фазный restart recovery. Это, разумеется, в случае «ежели чего». Если все OK — никакого recovery не будет, работаем дальше. Такой просчет, образно выражаясь, есть «расстилание соломки», не более того. Тут нужно учесть, что restart recovery начинается именно с последней контрольной точки. И чем она дальше (по времени) отстоит от restart recovery «в прошлое», тем сложнее серверу «прошерстить» все последние транзакции и сделать необходимые действия в каждой из 3-х фаз. Так вот движок сервера время от времени «прикидывает»: «случись крэш прямо сейчас — сколько будет длиться restart recovery после перезапуска»? Такой вопрос движок «задает» каждой из баз данных. И для тех из них, что ответят цифрой (в минутах) большей чем опция recovery interval, командует — CHECKPOINT.

Иными словами движок пытается убедиться, что в случае проблем, перезапуск и доступ пользователей к базам будет «быстрым». А что есть «быстро» устанавливаем мы, через эту самую опцию.

На текущий момент нам важно понять, что автоматический CHECKPOINT не происходит потому что:

  • в RAM (buffer pool) заканчивается свободное место (очередная распространенная в SQL-среде «мисконцепция»);
  • от момента последнего предыдущего CHECKPOINT-а прошло определенное время (вариант — прошло «много» времени);
  • число грязных страниц стало «значительным».

Нет, критерий запуска автоматического CHECKPOINT-а всего один: число данных модифицированных транзакциями (иными словами — «масса» измененных данных) стало таким, что их повторное применение в случае restart recovery превысит временной (а не какой-то еще!) порог recovery interval. Все, других критериев нет (если отбросить причины играющие исчезающе малое значение вроде CHECKPOINT-а при превышении занятым местом в логе отметки в 70%).

С практической точки зрения, все это значит что? Что в нашем, допустим, примере, автоматические контрольные точки будут случаться столь часто (или редко — уж судите сами), что перезапуск сервера может занять до 25-ти минут. А если эту опцию не трогать? Тогда она останется «по дефолту» — 0. Однако это не означает что restart recovery будет мгновенным (это физически невозможно, сами понимаете), а лишь то, что интервал между соседними CHECKPOINT-ми будет подбираться автоматически, исходя из текущей ситуации. Время же на restart recovery будет при этом «разумно-коротким», без уточнения цифр. А следующее возможное значение — 1, будет означать перезапуск в районе минуты, плюс/минус. Ну и так далее... Еще раз выпишите себе на листок нюанс: значение опции recovery interval 1, 2, 3,... не означает что CHECKPOINT для всех баз будет случаться каждые одну-две-три минуты. При одном и том же значении этой опции для базы A сервера MyServ контрольные точки будут случаться каждую секунду, для базы B того же сервера — каждый час. Причем назавтра ситуация может измениться диаметрально. Почему так? Ответ прост: различаются «шаблоны» эксплуатации этих баз нашими «юзерами».

Что гораздо интереснее чем разбор конкретных значений опции recovery interval, так это осознание того факта, что, во-первых, они носят оценочный характер (точное измерение времени на restart recovery было бы простым «прожиганием» ресурсов сервера), а, во-вторых, то что и приблизительно просчитываются только две из трех фаз указанного процесса! Причины этого оставим «за рамками», изменить положение вещей не в нашей власти, ограничимся констатацией: время на фазу undo движком сервера не учитывается. В этой точке повествования у читателя может сложится ощущение, что recovery interval — это разновидность «сферического коня». Особенно это ощущение усилится, если мы вспомним (из предыдущей части статьи), что как раз таки undo является наиболее «тяжеловесной» (по времени исполнения) фазой. Иными словами, если для undo требуется, скажем, 10 минут — то весь restart recovery будет продолжаться не менее указанного времени. И не важно что там «говорит» recovery interval.

Чем можно утешиться после получения подобных знаний?

  • вам говорили фразу «транзакция должна быть настолько короткой насколько это вообще возможно»? Самое время осознать, что это едва ли не главное правило построения надежного итогового решения на SQL Server. По многим причинам. И в частности по той, что фаза undo работает только с «подвисшими» транзакциями. А чем каждая из них, транзакций наших, короче, тем... Ну — вы понимаете;
  • определение интервала межу соседними CHECKPOINT методом «сферического коня» все же лучше «лобового» метода «штампуем контрольные точки ровно каждые 12 (почему не 4? не 53?) секунд»;
  • если у нас есть редакция Enterprise, то у нас есть fast recovery! И «конь» получается уже не столь «сферическим» как на первый взгляд. ;)
  • наконец прибавляет бодрости и тот факт, что наш славный Microsoft время от времени возвращается к вопросу «как сделать рестарт сервера еще более быстрым» и даже выпускает по этому отдельному поводу «фиксы» и апдейты. Последний из таковых (на момент написания статьи) решает проблему медленного recovery в момент начального старта базы (или нескольких баз) для весьма специфического, но вполне возможного в реальности окружения (подробности, если они вам интересны, смотрите по ссылке в описании самого «фикса»). Не «прорыв в будущее», конечно, но уже кое-что. Замечу, что для SQL Server 2008 R2 этот последний «фикс» был включен в кумулятивный апдейт Cumulative Update package 6, который, в свою очередь, является составной частью SP1. Так что если вы установили первый сервис-пак для данной версии сервера (а если нет — то кого ждем?) то вы, в том числе, получили и этот «улучшенный быстрый старт для специфических условий».

Горячие головы могут в этот момент предложить: «а давайте CHECKPOINT-ить каждую секунду! И restart recovery будет мгновенным, и recovery interval вообще не нужен!». Однако, подумав, успокоятся и они. Весь сыр-бор с логом, «грязными» страницами, и их отложенной записью был придуман как раз для того что бы вот так не происходило. А то можно было бы и вообще изменение каждой ячейки каждой таблицы сразу в MDF писать — куда уж проще! Да вот только подсистема Disk I/O нашего сервера была бы в этом случае перегружена мгновенно и из «пика» выходила бы только по очень большим праздникам. Проблема с предложением «контрольная точка каждую секунду» в том, что каждый очередной CHECKPOINT (про прямые изменения отдельных ячеек на диске не говорим вообще, в виду изначальной бредовости идеи) ощутимо «подгружает» Disk I/O. Не скажу что это прямо-таки «кладет» данную подсистему, нет, современные диски вполне «прокачивают» заданный объем информации за вменяемое время, но нагрузка повышается — это несомненно и даже не требует доказательств со счетчиками производительности (хотя если вам интересны конкретные цифры добавочной нагрузки можете провести пару несложных экспериментов).

Итого — что у нас в «сухом остатке»?

  • чем значение recovery interval меньше — тем чаще происходит CHECKPOINT, тем чаще «подтормаживает» (как минимум, с точки зрения системных счетчиков, если не с точки зрения конечного пользователя) сервер, но и тем короче интервал в течении которого пользователи получат доступ к перезапущенному серверу;
  • чем тоже самое больше — тем наоборот.

Вдумчивые читатели могут привести два контр-довода, могущие, на первый взгляд, вообще исключить высокие значения опции recovery interval из рассмотрения, не говоря уже о применении таких значений в реальных системах. Во-первых, скажут они — разве понижая значение recovery interval мы не заботимся о сохранности данных, и, тем самым, о надежности нашего решения? Этот довод совершенно ложен. Надежность произошедших транзакций гарантируется записями лога, и ничем иным. Обсуждаемая опция тут — никоим боком. Хорошо, скажут те же читатели, а тогда — во-вторых: разве при большем значении recovery interval в buffer pool не будет собираться большее число «грязных» страниц ожидающих сброса на HDD? Иными словами, если у нас при recovery interval=1 автоматический CHECKPOINT случается четко каждые 3 сек. и сбрасывает ровно 1 тыс. dirty data page, то в чем метафизический смысл смены значения на 2? Только то, что CHECKPOINT будет происходить вдвое реже, но и обработать ему придется вдвое большее число страниц? Не шило ли это на мыло? Довод кажется убедительным, но лишь до той поры, пока мы не узнаем, что dirty data page сбрасываются на диск не только при CHECKPOINT-е! Еще 2 механизма работают в том же «направлении»:

  • Lazy writing
  • Eager writing

Любой из них может привести ровно к такому же результату, что и CHECKPOINT, а именно к тому, что версия страницы данных на диске синхронизируется с ней же в RAM. Описание этих двух механизмов лежит совершенно за рамками данной статьи, однако отметим, что только CHECKPOINT гарантирует безусловный сброс всех (без исключения) страниц данных помеченных в настоящий момент как «грязные».

Так вот, возвращаясь к «доводу №2»: если в течении первых 3-х секунд образовалось 1 тыс. страниц подлежащих сбросу — не факт что к истечению 6-й секунды их будет 2 тыс. Это не исключено, но не более. Их, вполне возможно, будет не только меньше двух, но даже и одной тысячи! А поскольку на два альтернативных механизма мы никак не влияем — грех не воспользоваться их «побочным эффектом» посредством увеличения значения recovery interval.

На самом деле, практический совет касательно данной опции автор вам даст такой: если вы не готовы к интенсивным и трудоемким тестам конкретного, законченного решения в конкретном же окружении — оставляйте все «по дефолту», не ошибетесь. Оптимальное значение для recovery interval не может быть рассчитано «на бумаге», тут можно только брать и пробовать. Напомню, что значение по умолчанию (0) символизирует подстройку сервера «по месту», и в большинстве случаев дает вполне вменяемые результаты. Однако на практике (и автор тому свидетель) не раз отмечалось значительное повышение производительности сервера при выставлении этой опции в некоторое иное значение. Допустим в практике того же автора, для данной опции, фигурировали (в разное время и на разных системах) цифры 7, 15-18 (тут точно не вспомню, но порядок таков) и даже... 40!! Конечно, тут все зависит от отдельно взятой базы, от T-SQL кода запросов, к ней обращающихся, от тщательности тестов при подборе оптимального значения и т.д. И тем не менее факт: последствия смены значения опции recovery interval могут (но не обязаны!) превзойти ваши самые смелые ожидания. В общем это та еще опция, для «true»-исследователей...

Итак, теперь вы знаете как оценить периодичность возникновения автоматических контрольных точек на реальной системе. В наших практических экспериментах мы будем использовать «ручной» их вариант только потому, что в большинстве скриптов у нас слишком мало измененных данных, что бы указанная процедура была запущена без нашего вмешательства. И — да, напомню, на нормально работающем сервере «ручные» CHECKPOINT-ы абсолютно лишены смысла и строго противопоказаны.

Начнем с простого. Убедимся что CHECKPOINT делает «грязную» страницу «чистой». Напомню, что наша тестовая TstLog база после исходного скрипта сейчас пребывает в таком состоянии: создана она сама, создана первая ее таблица T1 и в последнюю вставлены первые 3 записи. Очевидно, что вся страница на которой разместилась таблица T1 обязана в настоящий момент быть «грязной», не так ли? Причем мы даже установили номер этой страницы — 79-я. И знаем как убедится в текущем статусе (т.е. «чиста» она или «грязна») этой страницы. Проверьте, что скрипт по последней ссылке возвращает YES — страница была модифицирована, и, вполне законно, считается «грязной». Находясь в контексте нашей тестовой базы TstLog (важно!) выполним «ручной» CHECKPOINT:

1
CHECKPOINT

Вновь проверяем статус той же страницы — получаем вполне ожидаемое NO. Страница «чиста», и сейчас ее RAM- и HDD-варианты полностью эквивалентны. И второй факт тоже вполне ожидаем: поскольку скрипт проверки статуса вернул нам не пустой резалт-сет, 79-я страница осталась в буферном пуле. То есть фиксируя страницы на HDD контрольные точки не вычищают их из RAM. И правильно делают, ведь хорошо когда каждый занимается своей работой, не так ли? Вот пусть вычищением памяти займется предназначенный для этого механизм, а не CHECKPOINT.

Теперь у нас вновь время разоблачений и «срывания покровов». Бытует мнение, что если транзакция начала изменения данных, то пока не произойдет их фиксация (commit) соответствующие страницы данных сброшены на диск не будут. Иными словами, данный миф говорит о том, что CHECKPOINT игнорирует страницы содержащие данные активных транзакций. Разоблачается же он очень просто, вам даже не обязательно быть в числе ведущих телепрограммы MythBusters. ;) Достаточно запустить такой, к примеру, скрипт:

1
2
3
4
5
6
7
8
9
BEGIN TRANSACTION
INSERT INTO T1 VALUES (40, 'T')
UPDATE T1 SET C3='R' WHERE C2=30
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'
CHECKPOINT
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'
COMMIT TRANSACTION

Что мы наблюдаем? Да, то что до CHECKPOINT-а наша 79-я страница «грязна», а после него — «чиста аки снег». И все это — прямо в середине транзакции! Да и BOL, собственно, в статье посвященной одноименной T-SQL команде CHECKPOINT нас поддерживает:

Записывает все «грязные» страницы в текущей базе данных на диск.

Видите вы уточнения насчет «активных транзакций»? Вот и автор не видит. Итого: при наступлении контрольной точки все без исключения «грязные» страницы фиксируются на HDD, конец прений.

Вы можете спросить: «ну а если у транзакции откат (rollback) вместо фиксации»? Тогда что же — на HDD попадают «не подтвержденные» данные? И на это автор вам ответит — да! Именно такие данные, именно на HDD и попадают! А как же все «устаканивается» когда сервер узнает о нежелательности такой транзакции? А вот как.

Допустим, наш последний скрипт заканчивается не COMMIT-ом, а ROLLBACK-ом. Тогда движок сервера не только ставит соответствующую пометку в файле лога (и о чем речь впереди), но и немедленно меняет значения 79-й (а равно и любых иных) страницы на оригинальные. В нашем случае 4-я строка таблицы была бы стерта, а колонка C3 третьей строчки вернула бы себе оригинальное значение. Все — транзакции как и не было.

Однако положение может и усложниться! Что если транзакция имеет столь длительное время исполнения, что 79-я страница не только будет сброшена на диск, но и вычищена из buffer pool соответствующим механизмом? Ведь, как минимум теоретически, такое возможно! И как тогда вернутся оригинальные значения? И даже, еще важней, куда они вернутся? Страницы-то в памяти нет! Да, ситуация заслуживает отдельного «расследования»:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BEGIN TRANSACTION -- <--1
INSERT INTO T1 VALUES (50, 'V')
UPDATE T1 SET C3='P' WHERE C2=20
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'
CHECKPOINT -- <--2
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'
DBCC DROPCLEANBUFFERS -- <--3
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'
ROLLBACK TRANSACTION -- <--4
SELECT CASE WHEN is_modified=1 THEN 'YES' ELSE 'NO' END AS 'T1_Page_Dirty?'
FROM sys.dm_os_buffer_descriptors WHERE page_id=79 AND db_name(database_id)='TstLog'

Ключевые места помечены цифрами в комментариях, разберем их:

  1. открываем транзакцию и вносим пару изменений. Проверка статуса 79-й страницы неопровержимо свидетельствует — последняя стала «грязной»;
  2. CHECKPOINT — и страница «чиста». Однако транзакция продолжается;
  3. центральный момент эксперимента — мы принудительно вычищаем buffer pool. И дабы убедиться в том вновь запрашиваем статус 79-й. На этот раз нам возвращается не NO/YES, а попросту пустой резалт-сет. Указанной страницы нет в памяти!
  4. кульминация — откат транзакции и финальный запрос статуса, который возвращает нам... YES!

Вывод? Совершенно верно: ROLLBACK, при необходимости, не только вновь загрузит необходимую страницу в память, не только вернет в нее оригинальные значения, но и вновь пометит ее же как «грязную»! И ведь что на это возразить? Версии RAM- и HDD- в настоящий момент различны? Очевидно, да. Значит указанная страница не синхронизирована? Придется согласится и с этим. Ну так получите уведомление о факте ее «загрязненности»! Отсюда — правило: для того, что бы ваш buffer pool был буквально «зафлужен» десятками тысяч «грязных» страниц вовсе не обязательно проводить массовые модификации данных (хотя и они «выполнят задачу», конечно). Достаточно скромного ROLLBACK-а в конце длительной транзакции и грязные страницы «возникают прямо из воздуха», как жалуются потом на форумах наши коллеги не читавшие данную статью. :) А ведь каждая новая dirty page это очевидный «клиент» ближайшего будущего CHECKPOINT-а, и чем больше таких «клиентов», тем... да, тем «ничего хорошего». Кстати, если вы начинаете подозревать, что ROLLBACK операция не столь безобидная (в плане ее затратности по ресурсам, или, более точно, в плане затрат ресурсов на компенсацию последствий ее применения) как кажется — то вы на верном пути. Однако «вся правда» об этой коварной операции еще только ожидает вас, продолжайте чтение!

Checkpoint и журнал транзакций.

Ну а как операция контрольной точки воздействует на лог? На самом деле воздействия эти обширны и многочисленны, часть из них будут представлены далее начиная с 6-й части статьи. Сейчас же мы только лишь снимем «первую стружку» с этого многогранного вопроса. Выполним вот такой скрипт (во все той же тестовой базе TstLog, разумеется):

1
2
3
4
5
BEGIN TRANSACTION
INSERT INTO T1 VALUES (60, 'U')
UPDATE T1 SET C3='Q' WHERE C2=60
CHECKPOINT
COMMIT TRANSACTION

А теперь уже известной нам командой посмотрим последние записи нашего транзакционного лога:

1
DBCC LOG(TstLog, 1)

Отбросив «лишнее» мы увидим:

Current LSN             Operation       Transaction ID  Previous LSN
00000044:000000a5:0001  LOP_BEGIN_XACT  0000:00000573   00000000:00000000:0000
...
00000044:000000a5:0002  LOP_MODIFY_ROW  0000:00000573   00000044:000000a5:0001
...
00000044:000000a5:0009  LOP_BEGIN_CKPT  0000:00000000   00000044:0000009e:000b
00000044:000000a8:0001  LOP_XACT_CKPT   0000:00000000   00000000:00000000:0000
00000044:000000a8:0002  LOP_END_CKPT    0000:00000000   00000044:000000a5:0009
00000044:000000a9:0001  LOP_COMMIT_XACT 0000:00000573   00000044:000000a5:0001

Возможно самый интересный вопрос тут — где, собственно, записи-то лога? Мы провели уже довольно много модификаций данных, а последний резалт-сет говорит что у нас всего порядка десяти записей в логе — где остальное?! Этот вопрос мы так же разберем, но не сейчас, а когда дойдем до темы изменения размеров лога. Сейчас же сосредоточимся на ином, а именно на анализе полученных результатов:

  • сначала операция LOP_BEGIN_XACT открывает нашу транзакцию, причем последняя получает идентификационный номер 573. Помимо этого, эта же операция открывает новую «цепочку» (см. колонку Previous LSN) записей лога;
  • затем идут несколько строк операций меняющих данные, из которых показана одна, с кодом операции LOP_MODIFY_ROW. Все они исполняются в рамках той же 573-й транзакции и являются «звеньями» все той же «цепочки», начало коей было положено операцией LOP_BEGIN_XACT;
  • затем операция LOP_BEGIN_CKPT знаменует собой начало контрольной точки. Обратите внимание, что CHECKPOINT — это «растянутая во времени» операция не происходящая в одну микросекунду (впрочем, это-то нам уже и без всякого лога известно). Кроме того, несмотря на то что согласно T-SQL коду операция вроде входит в ту же 573-ю транзакцию, согласно логу она ни в какие транзакции не входит (0 в колонке Transaction ID). И это совершенно верно, и мы уже об этом говорили. Как вы себе представляете процедуру отката страниц зафиксированных на диске? Обратно в RAM их считаете? :) Поэтому — никаких транзакций, страницы фиксируются и точка. По той же причине LOP_BEGIN_CKPT не является звеном цепочки начатой операцией LOP_BEGIN_XACT. Однако нельзя сказать что LOP_BEGIN_CKPT открывает свою цепочку. Она является очередным звеном отдельной цепочки, а именно: значение в колонке Previous LSN обсуждаемой операции указывает не куда-нибудь, а на точно такую же но предыдущую операцию! Понимаете? Все CHECKPOINT-ы выстраиваются в логе в этакие «отрезки цепи» (обыкновенно 3-х звенные, как и в нашем случае), причем первое звено каждого отрезка имеет указатель на первое же звено отрезка предыдущего. Таким образом, «разматывая» лог можно узнать историю как происходили в данной базе CHECKPOINT-ы, по крайней мере их последняя часть. Разумеется, через год «жизни» базы от ее первого CHECKPOINT-а в логе и следа не останется (и не только от первого). Но обычно «первые» и не нужны, нужны «последние». И даже в подавляющем большинстве случаев — ровно последняя точка, и не более того. Однако ссылки, как видите, есть и на более ранние варианты!
  • операция LOP_XACT_CKPT будет представлена в логе только если на момент CHECKPOINT-а в базе были активные транзакции. Это как раз наш случай (573-я транзакция открылась, но не была завершена к этому моменту). На нормальной системе с умеренной (хотя бы) загрузкой большинство CHECKPOINT-ов будет сопровождаться такой записью, и она станет вторым звеном нашего «3-х-звенного отрезка». Кстати, назначение данной операции — перечислить эти самые активные (на момент CHECKPOINT-а) транзакции. Если вы найдете в последнем резалт-сете колонку Description (в примере выше эта колонка была автором удалена, но в оригинальном резалт-сете она, разумеется, присутствовала), то она, для этой строки, будет содержать запись вида XdesIDs: 0000:00000573. Улавливаете связь с активными транзакциями? Еще кстати — именно эти записи критически важны для правильной и (самое главное) оптимальной работы процесса изученного нами чуть ранее — restart recovery. Разумеется, здесь мы снова «работаем» вне любых транзакций.
  • операция LOP_END_CKPT, как вы правильно догадались, символизирует окончание работы контрольной точки. Это завершающее, 3-е звено нашего «отдельного отрезка». Вновь транзакции нам совершенно безразличны.
  • ну и операция LOP_COMMIT_XACT завершает как транзакцию начатую операцией LOP_BEGIN_XACT, так и цепочку начатую ею же. Код операции не оставляет сомнений в том, что 573-я транзакция была именно COMMIT, и никак иначе.

Какое краткое резюме по увиденному мы делаем?

  • что каждый CHECKPOINT довольно подробно «протоколируется» в логе;
  • что для каждого CHECKPOINT-а как минимум помечаются его начало и конец, а в большинстве случаев и некоторые промежуточные операции типа той же LOP_XACT_CKPT;
  • что в логе всегда есть «мостик» от данного CHECKPOINT-а к его предшественнику;
  • наконец, что CHECKPOINT никогда не может быть привязан ни к какой транзакции и отменить его действие невозможно (да и кто захочет?).