У меня есть таблица с 64-метровыми строками, занимающая 4,3 ГБ на диске для своих данных.
Каждая строка составляет около 30 байтов целочисленных столбцов плюс переменный NVARCHAR(255)
столбец для текста.
Я добавил столбец NULLABLE с типом данных Datetimeoffset(0)
.
Затем я ОБНОВИЛ этот столбец для каждой строки и убедился, что все новые вставки помещают значение в этот столбец.
Как только не было записей NULL, я запустил эту команду, чтобы сделать новое поле обязательным:
ALTER TABLE tblCheckResult
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL
Результатом стал ОГРОМНЫЙ рост размера журнала транзакций - с 6 ГБ до более 36 ГБ, пока он не исчерпал пространство!
Кто-нибудь знает, что делает SQL Server 2008 R2 для этой простой команды, чтобы привести к такому огромному росту?
NOT NULL
столбец со значением по умолчанию в качестве операции метаданных. Также см. «Добавление столбцов NOT NULL в качестве оперативной операции» в документации .Ответы:
Когда вы изменяете столбец на NOT NULL, SQL Server должен касаться каждой отдельной страницы, даже если значения NULL отсутствуют. В зависимости от вашего коэффициента заполнения это может привести к большому количеству страниц. Конечно, каждая затронутая страница должна регистрироваться, и я подозреваю, что из-за разделения на двух страницах может потребоваться регистрация двух изменений. Так как все это делается за один проход, журнал должен учитывать все изменения, поэтому, если вы нажмете «Отмена», он точно знает, что нужно отменить.
Пример. Простая таблица:
Теперь давайте посмотрим на детали страницы. Для начала нам нужно выяснить, с какой страницей и DB_ID мы имеем дело. В моем случае я создал базу данных с именем
foo
, а DB_ID оказался равным 5.Вывод показал, что меня заинтересовала страница 159 (единственная строка в
DBCC IND
выводе сPageType = 1
).Теперь давайте посмотрим на некоторые детали страницы, пока мы шагаем по сценарию ОП.
Теперь у меня нет ответов на все вопросы, так как я не глубоко внутренний парень. Но ясно, что - хотя операция обновления и добавление ограничения NOT NULL, несомненно, записывают на страницу - последняя делает это совершенно по-другому. Похоже, что это на самом деле меняет структуру записи, а не просто возится с битами, заменяя обнуляемый столбец на ненулевой столбец. Почему я должен это делать, я не совсем уверен - наверное, хороший вопрос для команды разработчиков хранилищ . Я верю, что SQL Server 2012 справляется с некоторыми из этих сценариев намного лучше, FWIW - но я еще не провел какого-либо исчерпывающего тестирования.
источник
При выполнении команды
Кажется, это реализовано как операция добавления столбца, обновления, удаления столбца.
sys.sysrscols
для представления нового столбца.status
Бит128
установлен , указывающий столбец не допускаетNULL
секsys.sysrscols
.rscolid
Обновляется большим целым числом, аstatus
бит 2 устанавливается равным указанному отброшенному)sys.sysrscols
для нового столбца изменяется, чтобы присвоить емуrscolid
старый столбец.Операция, которая может вызвать много журналирования, - это
UPDATE
запись всех строк в таблице, однако это не означает, что это будет происходить всегда . Если образы строки «до» и «после» идентичны, то это будет рассматриваться как обновление без обновления и не будет зарегистрировано в моем тестировании.Таким образом, объяснение того, почему вы получаете много журналирования, будет зависеть от того, почему версии строки «до» и «после» не совпадают.
Для столбцов переменной длины, хранящихся в
FixedVar
формате, я обнаружил, что установкаNOT NULL
всегда вызывает изменение в строке, которая должна быть зарегистрирована. Количество столбцов и количество столбцов переменной длины увеличиваются, и новый столбец добавляется в конец раздела переменной длины, дублируя данные.datetimeoffset(0)
однако это фиксированная длина, и для столбцов фиксированной длины, сохраняемых вFixedVar
формате, старый и новый столбцы, похоже, имеют одинаковый интервал в части данных фиксированной длины строки, и, поскольку они оба имеют одинаковую длину и значение «до» и «после» версии строки одинаковы . Это можно увидеть в ответе @ Аарона. Обе версии строки до и после того , какALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
этоЭто не зарегистрировано.
Логично, что, исходя из моего описания событий, строка на самом деле здесь должна отличаться, так как количество столбцов
02
должно быть увеличено,03
но на практике такого изменения не происходит.Некоторые возможные причины того, почему это может происходить в столбце фиксированной длины:
SPARSE
тогда новый столбец будет сохранен в другой части строки, отличной от исходного, в результате чего изображения строк до и после будут отличаться.ALTER TABLE
операция, которая была реализована как изменение только метаданных и еще не была применена к строке. Например, если был добавлен новый столбец переменной длины со значением NULL, то он первоначально применяется как изменение только метаданных и фактически записывается в строки только при следующем обновлении (запись, которая фактически происходит в этом последнем экземпляре, просто обновляется до секция подсчета столбца и вNULL_BITMAP
качествеNULL
varchar
столбца в конце строки не занимает никакого пространства)источник
Я столкнулся с той же проблемой, что и таблица с 200.000.000 строк. Сначала я добавил столбец, который можно обнулять, затем обновил все строки и, наконец, изменил столбец с
NOT NULL
помощьюALTER TABLE ALTER COLUMN
оператора. В результате две огромные транзакции невероятно взорвали лог-файл (рост 170 ГБ).Самый быстрый способ, который я нашел, был следующим:
Добавьте столбец, используя значение по умолчанию
Удалите ограничение по умолчанию, используя динамический SQL, так как ограничение не было названо ранее:
Время выполнения сократилось с> 30 минут до 10 минут, включая репликацию изменений с помощью репликации транзакций. Я использую установку SQL Server 2008 (SP2).
источник
Я провел следующий тест:
Я считаю, что это связано с зарезервированным пространством, которое хранится в журнале на тот случай, если вы откатите транзакцию. Посмотрите в функции fn_dblog столбца 'Log Reserve' для строки LOP_BEGIN_XACT и посмотрите, сколько места она пытается зарезервировать.
источник
select * FROM fn_dblog(null, null) where AllocUnitName='dbo.tblCheckResult' AND Operation = 'LOP_MODIFY_ROW'
вы увидите 10000 строк обновлений.Поведение для этого отличается в SQL Server 2012. См. Http://rusanu.com/2011/07/13/online-non-null-with-values-column-add-in-sql-server-11/
Количество записей журнала, созданных для SQL Server 2008 R2 и более поздних выпусков, будет значительно выше, чем количество записей журнала для SQL Server 2012.
источник
NOT NULL
вызывает ведение журнала. Изменение в 2012 году касается добавления новогоNOT NULL
столбца со значением по умолчанию.