Самый быстрый способ сделать массовое обновление - PullRequest
16 голосов
/ 02 июня 2010

Допустим, у вас есть таблица с 5 миллионами записей и столбец nvarchar(max), заполненный большими текстовыми данными. Вы хотите установить для этого столбца значение NULL, если SomeOtherColumn = 1, как можно быстрее.

Грубая сила UPDATE здесь работает не очень хорошо, потому что она создаст большую неявную транзакцию и будет длиться вечно.

Одновременное выполнение обновлений небольшими партиями записей по 50 КБ, но на мощном 32-ядерном / 64-ГБ сервере требуется 47 часов.

Есть ли способ сделать это обновление быстрее? Существуют ли какие-либо магические подсказки / параметры таблицы запросов, которые жертвуют чем-то другим (например, параллелизмом) в обмен на скорость?

ПРИМЕЧАНИЕ. Создать временную таблицу или временный столбец нельзя, поскольку этот столбец nvarchar(max) содержит много данных и поэтому занимает много места!

PS: Да, SomeOtherColumn уже проиндексирован.

Ответы [ 7 ]

8 голосов
/ 08 июня 2010

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

Ключ, кажется, в том, что ваше поле nvarchar (max) содержит "много" данных. Подумайте о том, что должен сделать SQL для выполнения этого обновления.

Поскольку обновляемый столбец, вероятно, содержит более 8000 символов, он сохраняется вне страницы, что требует дополнительных усилий при чтении этого столбца, если он не равен NULL.

Когда вы запускаете пакет из 50000 обновлений, SQL должен поместить это в неявную транзакцию, чтобы сделать возможным откат в случае каких-либо проблем. Для отката необходимо сохранить исходное значение столбца в журнале транзакций.

Предполагая (для простоты), что каждый столбец содержит в среднем 10 000 байтов данных, это означает, что 50000 строк будут содержать около 500 МБ данных, которые должны храниться временно (в простом режиме восстановления) или постоянно (в режиме полного восстановления) ).

Невозможно отключить журналы, так как это нарушит целостность базы данных.

Я провел быстрый тест на медленном рабочем столе своей собаки, и запуск пакетов из даже 10 000 стал слишком медленным, но уменьшение размера до 1000 строк, что подразумевает временный размер журнала около 10 МБ, сработало просто замечательно.

Я загрузил таблицу с 350 000 строк и отметил 50 000 из них для обновления. Это завершается примерно за 4 минуты, и, поскольку он линейно масштабируется, вы сможете обновить целые 5 миллионов строк на медленном рабочем столе моей собаки примерно за 6 часов на моем 1-процессорном 2-гигабайтном рабочем столе, так что я бы ожидал чего-то гораздо лучшего на вашем мощном сервере. по SAN или что-то.

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

Конечно, узким местом могут быть другие пользователи, блокирующие вещи или конфликты в вашем хранилище или памяти на сервере, но, поскольку вы не упомянули других пользователей, я предполагаю, что для этого у вас есть БД в однопользовательском режиме.

В качестве оптимизации вы должны убедиться, что журналы транзакций находятся на другой физической группе дисков / дисков, чем данные, чтобы минимизировать время поиска.

4 голосов
/ 02 июня 2010

Надеюсь, вы уже удалили все индексы для столбца, для которого вы устанавливаете значение NULL, включая полнотекстовые индексы. Как было сказано ранее, временное отключение транзакций и файла журнала поможет. Резервное копирование ваших данных, как правило, также усекает ваши файлы журналов.

3 голосов
/ 02 июня 2010

Вы можете установить режим восстановления базы данных на Simple, чтобы уменьшить ведение журнала, НО не делайте этого без учета всех последствий для производственной среды.

Какие индексы имеются в таблице? Учитывая, что пакетные обновления ок. 50 000 строк занимают так много времени, я бы сказал, вам нужен индекс.

1 голос
/ 02 июня 2010

Это действительно помогло мне. С этим у меня ушло от 2 часов до 20 минут.

/* I'm using database recovery mode to Simple */
/* Update table statistics */

set transaction isolation level read uncommitted     

/* Your 50k update, just to have a measures of the time it will take */

set transaction isolation level READ COMMITTED

По моему опыту, работая в MSSQL 2005, ежедневное (автоматически) перемещение 4 миллионов 46-байтовых записей (без nvarchar (max)) из одной таблицы в другую в другую базу данных занимает около 20 минут. сервер QuadCore 8 ГБ, 2 ГГц, и это не снижает производительность приложений. Под перемещением я подразумеваю INSERT INTO SELECT, а затем DELETE. Загрузка ЦП никогда не превышает 30%, даже если удаляемая таблица содержит 28 МБ записей, и она постоянно выполняет вставку со скоростью около 4 КБ в минуту, но не обновляется. Ну, это мой случай, он может варьироваться в зависимости от нагрузки на ваш сервер.

ЧИТАТЬ НЕОГРАНИЧЕННЫЙ

"Указывает, что операторы (ваши обновления) могут читать строки, которые были изменены другими транзакциями, но еще не зафиксированы." В моем случае записи только для чтения.

Я не знаю, что означает rg-tsql, но здесь вы найдете информацию об уровнях изоляции транзакций в MSSQL.

1 голос
/ 02 июня 2010

Вы пытались разместить индекс или статистику в некотором другом столбце?

0 голосов
/ 02 июня 2010

Если вы работаете в производственной среде, в которой недостаточно места для дублирования всех ваших таблиц, я считаю, что вы рано или поздно ищете проблему.

Если вы предоставите некоторую информацию о количестве строк с SomeOtherColumn = 1, возможно, мы можем подумать иначе, но я предлагаю:

0) Сделайте резервную копию таблицы 1) Индексируйте столбец флага 2) Установите опцию таблицы «без логарифмических переходов» ... если возможно 3) написать хранимую процедуру для запуска обновлений

0 голосов
/ 02 июня 2010

Попробуйте индексировать SomeOtherColumn ... 50K записей должны обновляться в одно мгновение. Если индекс уже существует, посмотрите, нужно ли его реорганизовать и собрана ли для него статистика.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...