Как уменьшить рост журнала транзакций для пакетных обновлений nvarchar (максимум)

StackOverflow https://stackoverflow.com/questions/2014670

Вопрос

Нашему приложению необходимо добавить большие объемы текста в базу данных SQL Server 2005 (до 1 ГБ для одной записи).По соображениям производительности это делается фрагментарно, путем выполнения вызова хранимой процедуры для каждого фрагмента (скажем, usp_AddChunk).usp_AddChunk не имеет никаких явных транзакций.

Что я вижу, так это то, что уменьшение размера блока со 100 МБ до 10 МБ приводит к значительному увеличению журналов транзакций.Мне сказали, что это происходит потому, что каждый раз, когда вызывается usp_AddChunk, "неявная" (мой термин) транзакция регистрирует весь существующий текст.Итак, для записи размером 150 МБ:

Размер блока 100 МБ:100 (записано 0 байт) + 50 (записано 100 МБ) = 100 МБ

будет меньше, чем

Размер блока 10 МБ:10 (записано 0 байт) + 10 (записано 10 МБ) + 10 (записано 20 МБ) ...+ 10 (записано 140 МБ) = записано 1050 МБ

Я думал, что, открыв транзакцию в моем коде C # (до того, как я добавлю первый фрагмент, и зафиксировав после последнего фрагмента), эта "неявная" транзакция не произойдет, и я смогу избежать огромных файлов журнала.Но мои тесты показывают, что журнал транзакций увеличивается в 5 раз с использованием транзакции ADO.NET.

Я не буду публиковать код, но вот несколько деталей:

  1. Я вызываю SqlConnection.beginTransaction()
  2. Я использую другую SqlCommand для каждого фрагмента
  3. Я назначаю SqlTransaction из (1) каждой SqlCommand
  4. Обычно я закрываю соединение после каждого выполнения SqlCommand, но я также пытался не закрывать соединение с теми же результатами

В чем недостаток этой схемы?Дайте мне знать, если вам понадобится дополнительная информация.Спасибо!

Примечание:использование простой модели восстановления или модели с массовым протоколированием - это не вариант

Это было полезно?

Решение

Если под "кусками" вы подразумеваете что-то вроде:

UPDATE table
SET blob = blob + @chunk
WHERE key = @key;

Тогда вы правы в том, что операция полностью протоколирована.Вы должны следовать Рекомендации по использованию больших двоичных объектов и используйте .Методы записи для заблокированных обновлений:

UPDATE table
SET blob.Write(@chunk, NULL, NULL)
WHERE key = @key;

Это позволит минимально зарегистрировать обновление (если возможно, см. Операции, Которые Могут Быть Минимально Протоколированы):

Инструкция по ОБНОВЛЕНИЮ полностью зарегистрирована;однако частичные обновления типов данных с большими значениями, использующих предложение .WRITE , регистрируются минимально.

Мало того, что это регистрируется минимально, но поскольку обновление является явной записью в конце большого двоичного объекта, движок будет знать, что вы обновили только часть большого двоичного объекта, и будет регистрировать только это.Когда вы обновляете с SET blob=blob+@chunk движок увидит, что весь большой двоичный объект получил новое значение, и не обнаружит тот факт, что вы на самом деле изменили большой двоичный объект, добавив новые данные, поэтому он зарегистрирует весь большой двоичный объект (несколько раз, как вы уже выяснили).

Кстати, вы должны использовать фрагменты размером, кратным 8040:

Для достижения наилучшей производительности мы рекомендуем вставлять или обновлять данные в размерах блоков, кратных 8040 байт.

Другие советы

Что вам, возможно, придется сделать, так это окружить каждый "фрагмент" или группу фрагментов его собственной транзакцией и зафиксировать после каждой группы.Окружая все это вашей собственной транзакцией ADO, вы, по сути, делаете то же самое, что и неявная транзакция, так что это не поможет.Вы должны фиксировать данные меньшими порциями, чтобы уменьшить размер журнала.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top