18.3.26

Журнал транзакций SQL Server. Часть 3: Циклическая природа

Автор: Paul Randal, The SQL Server Transaction Log, Part 3: The Circular Nature of the Log

(Эта статья впервые появился на SQLperformance.com четыре года назад как часть серии блогов, до того как этот сайт был закрыт позднее в 2022 году, а серия была прервана. Переопубликовано здесь с разрешения, с небольшими правками.)

Во второй части этой серии я описал структурную иерархию журнала транзакций. Поскольку эта статья в основном касается описанных мной виртуальных файлов журнала (VLF), я рекомендую вам прочитать вторую часть, прежде чем продолжить.

Когда всё хорошо, журнал транзакций будет бесконечно циклически повторяться, повторно используя существующие VLF. Это поведение я называю циклической природой журнала. Однако иногда происходит что-то, что мешает этому, и журнал транзакций растёт и растёт, добавляя всё больше и больше VLF. В этой статье я объясню, как всё это работает, и почему иногда и не работает.

VLF и усечение журнала

Все VLF имеют структуру заголовка, содержащую метаданные о VLF. Одно из самых важных полей в структуре — это статус VLF, и значения, которые нас интересуют: ноль, означающий, что VLF неактивен, и два, означающий, что VLF активен. Это важно, потому что неактивный VLF можно повторно использовать, а активный — нельзя. Обратите внимание, что VLF является полностью активным или полностью неактивным.

VLF будет оставаться активным, пока в нём находятся требуемые записи журнала, поэтому его нельзя повторно использовать и перезаписывать (я расскажу о самих записях журнала в следующий раз). Примеры причин, по которым записи журнала могут быть необходимы, включают:

  • Существует долго выполняющаяся транзакция, частью которой являются записи журнала, поэтому они не могут быть освобождены, пока транзакция не будет зафиксирована или не завершит откат.
  • Резервная копия журнала ещё не скопировала эти записи журнала.
  • Эта часть журнала ещё не была обработана агентом чтения журнала (Log Reader Agent) для транзакционной репликации или отслеживания изменений данных (CDC).
  • Эта часть журнала ещё не была отправлена на асинхронное зеркальное отображение базы данных или реплику группы доступности.

Важно отметить, что если нет причин для того, чтобы VLF оставался активным, он не переключится обратно в неактивное состояние, пока не произойдёт процесс, называемый усечением журнала (log truncation) — подробнее об этом ниже.

Используя простой гипотетический журнал транзакций только с пятью VLF и номерами последовательности VLF, начиная с 1 (помните из прошлого раза, что в реальности они никогда так не начинаются), при создании журнала транзакций VFL 1 немедленно помечается как активный, так как в журнале транзакций всегда должен быть по крайней мере один активный VLF — VLF, в который в данный момент записываются блоки журнала. Наш пример сценария показан на рисунке 1 ниже.

(Рисунок 1: Гипотетический, совершенно новый журнал транзакций с 5 VLF, номера последовательности с 1 по 5)

По мере создания новых записей журнала и записи новых блоков журнала в журнал транзакций VLF 1 заполняется, поэтому VLF 2 должен стать активным для записи дополнительных блоков журнала, как показано на рисунке 2 ниже.

(Рисунок 2: Активность перемещается по журналу транзакций)

SQL Server отслеживает начало самой старой незафиксированной (активной) транзакции, и этот LSN сохраняется на диске каждый раз, когда происходит операция контрольной точки (checkpoint). LSN самой последней записи журнала, записанной в журнал транзакций, также отслеживается, но только в памяти, так как нет способа сохранить его на диске без возникновения различных состояний конкуренции. Это не имеет значения, так как он используется только во время восстановления после сбоя (crash recovery), и SQL Server может вычислить LSN «конца» журнала транзакций во время восстановления после сбоя. Контрольные точки и восстановление после сбоя — темы для будущих постов в этой серии.

В конце концов, VLF 2 заполнится, и VLF 3 станет активным, и так далее. Суть циклической природы журнала транзакций заключается в том, что более ранние VLF в журнале транзакций становятся неактивными, чтобы их можно было повторно использовать. Это делается с помощью процесса, называемого усечением журнала, который также часто называют очисткой журнала. К сожалению, оба этих термина являются ужасными неправильными названиями, потому что на самом деле ничего не усекается и не очищается.

Усечение журнала (truncation) — это просто процесс проверки всех VLF в журнале транзакций и определения того, какие активные VLF теперь можно снова пометить как неактивные, так как их содержимое больше не требуется SQL Server. Когда выполняется усечение журнала, нет гарантии, что какие-либо активные VLF могут быть сделаны неактивными — это полностью зависит от того, что происходит с базой данных.

Существует два распространённых заблуждения об усечении журнала:

  • Журнал транзакций становится меньше (заблуждение об «усечении»). Нет, не становится — размер журнала от усечения не меняется. Единственное, что может уменьшить журнал транзакций, — это явная команда DBCC SHRINKFILE.
  • Неактивные VLF каким-то образом обнуляются (заблуждение об «очистке»). Нет — в VLF ничего не записывается, когда он становится неактивным, за исключением нескольких полей в заголовке VLF.

На рисунке 3 ниже показан наш журнал транзакций, где VLF 3 и 4 активны, а усечение журнала смогло пометить VLF 1 и 2 как неактивные.

(Рисунок 3: Усечение журнала помечает более ранние VLF как неактивные)

Когда происходит усечение журнала, зависит от того, какая модель восстановления используется для базы данных:

  • Простая модель (Simple): усечение журнала происходит при завершении операции контрольной точки.
  • Полная модель (Full) или модель с неполным протоколированием (Bulk-logged): усечение журнала происходит при завершении резервного копирования журнала (при условии, что не выполняется одновременное полное или дифференциальное резервное копирование, в этом случае усечение журнала откладывается до завершения резервного копирования данных).

Исключений из этого правила нет.

Циклическая природа журнала

Чтобы журналу транзакций не приходилось расти, усечение журнала должно иметь возможность помечать VLF как неактивные. Первый физический VLF в журнале должен быть неактивным, чтобы журнал транзакций имел циклическую природу.

Рассмотрим рисунок 4 ниже, который показывает, что VLF 4 и 5 используются, а усечение журнала пометило VLF с 1 по 3 как неактивные. Генерируется больше записей журнала, больше блоков журнала записывается в VLF 5, и в конце концов он заполняется.

(Рисунок 4: Активность заполняет самый высокий физический VLF в журнале транзакций)

В этот момент менеджер журнала для базы данных смотрит на статус первого физического VLF в журнале транзакций, которым в нашем примере является VLF 1 с номером последовательности 1. VLF 1 неактивен, поэтому журнал транзакций может замкнуть круг и начать заполняться снова с начала. Менеджер журнала изменяет первый VLF на активный и увеличивает его номер последовательности на единицу больше, чем текущий самый высокий номер последовательности VLF. Таким образом, он становится VLF 6, и протоколирование продолжается с записью блоков журнала в этот VLF. Это и есть циклическая природа журнала, как показано ниже на рисунке 5.


(Рисунок 5: Циклическая природа журнала транзакций и повторное использование VLF)

Когда что-то идёт не так

Когда первый физический VLF в журнале транзакций не является неактивным, журнал транзакций не может замкнуть круг, поэтому он будет расти (при условии, что он настроен на это и есть достаточно места на диске). Это часто происходит из-за того, что что-то препятствует усечению журнала деактивировать VLF. Если вы обнаружили, что журнал транзакций для базы данных растёт, вы можете запросить SQL Server, чтобы выяснить, есть ли проблема с усечением журнала, используя этот простой код:

SELECT [log_reuse_wait_desc] FROM [master].[sys].[databases] WHERE [name] = N'MyDatabase';

Если усечение журнала смогло деактивировать один или несколько VLF, то результатом будет NOTHING. В противном случае вы получите причину, по которой усечение журнала не смогло деактивировать какие-либо VLF. Существует длинный список возможных причин, описанных в статье документации, в разделе Факторы, которые могут вызвать задержку усечения журнала.

Важно понимать семантику того, что означает результат: это причина, по которой усечение журнала не смогло ничего сделать в прошлый раз, когда оно пыталось запуститься. Например, результатом может быть ACTIVE_BACKUP_OR_RESTORE, но вы знаете, что эта долго выполняющаяся полная резервная копия уже завершилась. Это просто означает, что в прошлый раз, когда была предпринята попытка усечения журнала, резервное копирование всё ещё выполнялось.

По моему опыту, самая распространённая причина, препятствующая усечению журнала, — это LOG_BACKUP; т.е. выполните резервное копирование журнала! Но есть также интересное, странное поведение, связанное с LOG_BACKUP. Если вы постоянно видите результат LOG_BACKUP, но знаете, что резервное копирование журнала происходит успешно, это связано с тем, что в базе данных очень мало активности и текущий VLF тот же самый, что и в прошлый раз, когда выполнялось резервное копирование журнала. Таким образом, LOG_BACKUP означает либо «выполните резервное копирование журнала», либо «все скопированные записи журнала находятся в текущем VLF, поэтому он не мог быть деактивирован». Когда происходит последнее, это может сбивать с толку.

Возвращаясь к циклической природе…

Поддержание циклической природы журнала транзакций очень важно для избежания дорогостоящего роста журнала и необходимости принятия корректирующих мер. Обычно это означает обеспечение регулярного резервного копирования журнала для содействия усечению журнала и определение размера журнала транзакций таким образом, чтобы он мог вместить любые крупные, долго выполняющиеся операции, такие как перестроение индексов или ETL-операции, без роста журнала.

В следующей части серии я расскажу о записях журнала, о том, как они работают, и о некоторых интересных примерах.




Комментариев нет:

Отправить комментарий