19.10.25

Поговорим ещё о циклической природе журнала транзакций

Автор: Paul Randal, Inside the Storage Engine: More on the circular nature of the log

Периодически (ладно, честно говоря, очень часто) мой внутренний список «надо бы об этом написать» разрастается до таких размеров, что меня охватывает почти непреодолимое желание. К счастью, эти порывы заканчиваются тем, что я публикую сразу несколько записей — иначе кто знает, в какие неприятности я мог бы вляпаться? :-)

Для начала сегодня — довольно глубокие внутренности о том, как работает журнал транзакций в одной конкретной ситуации. Этот вопрос всплывал несколько раз на последних курсах по внутреннему устройству и обслуживанию, которые я вёл, в модуле про журнал транзакций, так что я решил написать статью, подтверждающую, что мой ответ верен. Проще всего сформулировать вопрос с картинкой, позаимствованной из моей статьи в журнале TechNet Magazine (Ведение журнала и восстановление в SQL Server).

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

Вопрос таков: что произойдёт, если записи в журнал продолжат писаться в VLF 1 и 2, и в итоге будет достигнут конец VLF 2, но VLF 3 всё ещё активен и не может быть перезаписан? База данных просто остановится?

Давайте разберёмся.

Я создам базу данных и затем смоделирую описанную ситуацию.

CREATE DATABASE LogTestDB ON PRIMARY (
NAME = LogTestDB_data,
FILENAME = N’C:\SQLskills\LogTestDB_data.mdf’)
LOG ON (
NAME = ‘LogTestDB_log’,
FILENAME = N’C:\SQLskills\LogTestDB_log.ldf’,
SIZE = 5MB);
GO

База данных в модели восстановления SIMPLE. Если быть совсем точным, она в модели восстановления FULL, но ещё не было полного резервного копирования базы, поэтому с точки зрения поведения журнала транзакций она ведёт себя так, как если бы была в SIMPLE (журнал может очищаться командой CHECKPOINT). Снова: если смысл непонятен, прочтите статью в TechNet Magazine.

А теперь посмотрим, сколько у неё VLF, с помощью команды DBCC LOGINFO (и это единственный способ узнать их число):

DBCC LOGINFO (‘LogTestDB’);
GO

FileId   FileSize   StartOffset   FSeqNo   Status   Parity CreateLSN
——– ———- ————- ——– ——– —— ———–
2        1245184    8192          31       2        64     0
2        1245184    1253376       0        0        0      0
2        1245184    2498560       0        0        0      0
2        1499136    3743744       0        0        0      0

У нас 4 VLF. Столбец Status показывает, активен VLF или нет. Значение 2 означает, что VLF активен; 0 — что нет (Примечание от 30.08.2018: возможно значение 4 на вторичной реплике AG, когда VLF существует на первичной, но увеличение файла журнала ещё не было воспроизведено на вторичной). Номер последовательности (FSeqNo) — это логический порядок VLF внутри журнала. Размер файла (FileSize) указан в байтах (то есть каждый VLF примерно по 1,25 МБ). Сейчас активен только один VLF.

Теперь я смоделирую ситуацию, изображённую на рисунке. Я заполню журнал так, чтобы VLF 1, 2 и 3 стали активными. Затем начну явную транзакцию, которая будет удерживать активными VLF 3 и далее. Потом продолжу заполнять журнал так, чтобы он обернулся и начал снова заполнять VLF 1.

USE LogTestDB;
GO
CREATE TABLE BigRows (c1 INT IDENTITY, c2 CHAR (8000) DEFAULT ‘a’);
GO

SET NOCOUNT ON;
INSERT INTO BigRows DEFAULT VALUES;
GO 300

Я заполнил VLF 1 и 2 и начал заполнять 3. Проверим DBCC LOGINFO:

DBCC LOGINFO (‘LogTestDB’);
GO

FileId   FileSize   StartOffset   FSeqNo   Status   Parity CreateLSN
——– ———- ————- ——– ——– —— ———–
2        1245184    8192          31       2        64     0
2        1245184    1253376       32       2        64     0
2        1245184    2498560       33       2        64     0
2        1499136    3743744       0        0        0      0

Как видно, первые три VLF теперь со статусом 2 (активны) и идут по порядку. Теперь я создам явную транзакцию, которая помешает очистить VLF 3 и последующие.

BEGIN TRAN
INSERT INTO BigRows DEFAULT VALUES;
GO

Если я сейчас явно выполню CHECKPOINT, VLF 1 и 2 очистятся:

CHECKPOINT
GO

DBCC LOGINFO (‘LogTestDB’);
GO

FileId   FileSize   StartOffset   FSeqNo   Status   Parity CreateLSN
——– ———- ————- ——– ——– —— ———-
2        1245184    8192          31       0        64     0
2        1245184    1253376       32       0        64     0
2        1245184    2498560       33       2        64     0
2        1499136    3743744       0        0        0      0

Теперь я продолжу заполнять таблицу BigRows, чтобы журнал начал повторное использование VLF и снова начал заполнять VLF 1 и 2.

INSERT INTO BigRows DEFAULT VALUES;
GO 300

DBCC LOGINFO (‘LogTestDB’);
GO

FileId   FileSize   StartOffset   FSeqNo   Status   Parity CreateLSN
——– ———- ————- ——– ——– —— ———-
2        1245184    8192          35       2        128    0
2        1245184    1253376       32       0        64     0
2        1245184    2498560       33       2        64     0
2        1499136    3743744       34       2        64     0

Видно, что журнал уже обернулся, но VLF 3 и 4 всё ещё активны. Посмотрите на номера последовательностей активных VLF… активная часть журнала — это VLF 3, затем 4, затем 1, с номерами 33, затем 34, затем 35. Теперь если я продолжу заполнять таблицу, что произойдёт, когда журнал упрётся в VLF 3, который всё ещё активен? Он остановится или увеличится?

INSERT INTO BigRows DEFAULT VALUES;
GO 300

DBCC LOGINFO (‘LogTestDB’);
GO

FileId   FileSize   StartOffset   FSeqNo   Status   Parity CreateLSN
——– ———- ————- ——– ——– —— ——————
2        1245184    8192          35       2        128    0
2        1245184    1253376       36       2        128    0
2        1245184    2498560       33       2        64     0
2        1499136    3743744       34       2        64     0
2        253952     5242880       37       2        64     36000000049300052
2        270336     5496832       38       2        64     36000000049300052
2        253952     5767168       0        0        0      36000000107500066
2        335872     6021120       0        0        0      36000000107500066
2        253952     6356992       0        0        0      36000000190700020
2        401408     6610944       0        0        0      36000000190700020
2        253952     7012352       0        0        0      37000000037300040
2        466944     7266304       0        0        0      37000000037300040

Ответ: журнал увеличился — и как бы «перепрыгнул» активные VLF! Посмотрите на номера последовательностей. Новая последовательность активной части журнала — VLF 3, затем 4, затем 1, затем 2, затем 5 и 6, как видно по номерам. Как только созданная мной активная транзакция зафиксируется или откатится, VLF 3, 4, 1 и 2 можно будет очистить, и тогда «нормальная» последовательность VLF в журнале возобновится.

У каждого VLF есть небольшой заголовок, в котором хранится номер его последовательности в журнале транзакций, поэтому журнал способен как бы «изгибаться», обходя активные VLF посередине. Очень здорово.

Ладно — это было весело — теперь мне полегчало!

P.S. В последних паре выводов DBCC LOGINFO, где журнал уже обернулся, вы можете увидеть, что биты чётности для блоков журнала в VLF изменились — как я объяснял в одном из предыдущих постов: «Заблуждения вокруг мгновенной инициализации файлов».



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

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