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