17.10.25

Ghost Cleanup возвращается

Автор: Paul Randal, Ghost cleanup redux

Ещё в первых статьях блога я написал подробное описание «фантомных» записей (ghost records) и процесса их очистки — см. статью «Подробно об очистке фантомных строк». На занятии возник вопрос, который стоит разобрать отдельно: возникают ли «фантомные» записи в кучах (heap)? Ответ — нет, не при обычной работе.

Когда включена изоляция «снимком» (snapshot isolation), удаления из кучи действительно выполняются через «фантомы» — как часть общего механизма версионирования, что может иметь любопытные побочные эффекты. Версионированная запись получает дополнительный 14‑байтовый «хвост», то есть запись в куче, внезапно ставшая версионированной, становится на 14 байт длиннее — и может больше не помещаться на текущую страницу. Это может привести к её перемещению и появлению пары «перенаправляющая/перенаправленная» (forwarding/forwarded) записи — просто из‑за того, что запись была удалена! Разумеется, страница должна быть заполнена, чтобы такое могло случиться, и механизм хранения (Storage Engine) предпринимает шаги, чтобы избежать этого для строк короче 32 байт — но это уже слишком в глубину. Пол Уайт разбирает это и ещё более тонкий случай в статье: Deletes that Split Pages and Forwarded Ghosts.

Но я отвлёкся. Хочу показать разницу между удалением из кластеризованного индекса и из кучи. Я создам по одной такой таблице, затем удалю по строке из каждой и откатываю изменения.

CREATE TABLE t1 (c1 CHAR (10));
CREATE CLUSTERED INDEX t1c1 on t1 (c1);
GO

CREATE TABLE t2 (c1 CHAR (10));
GO

INSERT INTO t1 VALUES ('PAUL');
INSERT INTO t1 VALUES ('KIMBERLY');

INSERT INTO t2 VALUES ('PAUL');
INSERT INTO t2 VALUES ('KIMBERLY');
GO

-- prevent random background transactions
ALTER DATABASE GhostTest SET AUTO_CREATE_STATISTICS OFF;
GO

BEGIN TRAN DelFromClust;
DELETE FROM t1 WHERE c1='KIMBERLY';
ROLLBACK TRAN;
GO

BEGIN TRAN DelFromHeap;
DELETE FROM t2 WHERE c1='KIMBERLY';
ROLLBACK TRAN;
GO

SELECT * FROM ::fn_dblog (null, null);
GO

Ниже — часть результатов просмотра журнала транзакций. Строка кода, где я отключаю автообновление статистики, нужна лишь для того, чтобы транзакции автообновления не «засоряли» обзор журнала.

Первая (выделенная) транзакция соответствует удалению/откату в кластеризованном индексе. Отчётливо видно, что в третьем столбце указан контекст «фантома» (ghosting) для записи журнала LOP_DELETE_ROWS, а также установка в байте PFS признака «на этой странице есть как минимум одна фантомная запись».

Вторая (невыделенная) транзакция — это удаление/откат в куче. Здесь видно, что выполняется «прямое» удаление.

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



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

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