Периодически (ладно, честно говоря, очень часто) мой внутренний список «надо бы об этом написать» разрастается до таких размеров, что меня охватывает почти непреодолимое желание. К счастью, эти порывы заканчиваются тем, что я публикую сразу несколько записей — иначе кто знает, в какие неприятности я мог бы вляпаться? :-)
Для начала сегодня — довольно глубокие внутренности о том, как работает журнал транзакций в одной конкретной ситуации. Этот вопрос всплывал несколько раз на последних курсах по внутреннему устройству и обслуживанию, которые я вёл, в модуле про журнал транзакций, так что я решил написать статью, подтверждающую, что мой ответ верен. Проще всего сформулировать вопрос с картинкой, позаимствованной из моей статьи в журнале 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 изменились — как я объяснял в одном из предыдущих постов: «Заблуждения вокруг мгновенной инициализации файлов».
Комментариев нет:
Отправить комментарий