Обновление таблицы занимает очень много времени - PullRequest
5 голосов
/ 08 января 2011

У меня есть таблица в SQL Server 2008 (SP2), содержащая 30 миллионов строк, размер таблицы 150 ГБ, есть пара столбцов типа int и два столбца nvarchar (макс.): Один содержит текст (из 1-30000 символов), а другой - один.содержащий xml (до 100000 символов).

Таблица не имеет первичных ключей или индексов (это промежуточная таблица).Итак, я выполняю запрос:

UPDATE [dbo].[stage_table] 
SET [column2] = SUBSTRING([column1], 1, CHARINDEX('.', [column1])-1);

запрос выполняется в течение 3 часов (и он все еще не завершен), что я считаю слишком длинным.Это?Я вижу постоянную скорость чтения 5 МБ / с и скорость записи 10 МБ / с в файл .mdf.

Как я могу узнать, почему запрос выполняется так долго?«Сервер» - это i7, 24 ГБ ОЗУ, диски SATA на RAID 10.

Обновлено:

Таблица содержит один столбец int, два столбца nvarchar (20) и два столбца nvarchar (max),Column1 и Columns2 в приведенном выше предложении об обновлении являются столбцами nvarchar (20).«Большие» столбцы не обновляются.

Большое спасибо!

Ответы [ 6 ]

3 голосов
/ 08 января 2011

Честно говоря, это огромный объем работы, который вы выполняете (поиск и замена текста на 150 гигабайт). Если промежуточные данные происходят за пределами базы данных, вы можете рассмотреть возможность выполнения там текстовых операций без каких-либо дополнительных затрат на базу данных.

1 голос
/ 09 января 2011

Я не выполнял такого рода обработку в SQL Server, поэтому не уверен, что совет полностью применим. Но я достаточно уверен, чтобы предложить вам попробовать.

Что я обычно делаю в Oracle, так это полностью избегаю обновлений при обработке ВСЕХ строк в ситуации, подобной той, которую вы описываете (однопользовательский, пакетное событие).

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

UPDATE [dbo].[stage_table] 
SET [column2] = SUBSTRING([column1], 1, CHARINDEX('.', [column1])-1);

Я бы сделал:

create table stage_table2 as
   select column1
         ,substring(column1, 1, charindex('.', column1)-1) as column2
         ,column3
         ,column4
     from stage_table;

drop table stage_table;

alter table stage_table2 rename to stage_table;
-- re-create indexes and constraints, optionally gather statistics

Я мог бы также сделать это с параллельным запросом и опцией nologging, чтобы генерировать очень мало повторов и вообще не отменять, что превзошло бы оператор обновления с таким большим запасом, что это даже не смешно :) Конечно, это из-за внутренних компонентов Oracle , но я думаю, что можно было бы реплицировать его и с SQL Server. В вашем описании есть что-то, что может сделать этот подход менее эффективным. У вас было несколько действительно больших текстовых столбцов, которые вам нужно было бы «перетащить» в операторе CTAS.

Кроме того, вам необходимо изучить настройку вашего оборудования, поскольку оно не подходит для работы с объемом данных, которые вы на него выбросили. Либо что-то не так с конфигурацией, либо у вас много других действий:

Я вижу, что есть постоянное чтение скорость 5 МБ / с и скорость записи 10 МБ / с в файл .mdf.

Я могу победить это на двухлетнем ноутбуке моей подруги. При скорости чтения 5 МБ / с и таблице в 150 ГБ сканирование одной таблицы займет 8,5 часов. Предполагается, что база данных добавляет 0% накладных расходов, что составляет , а не .

1 голос
/ 09 января 2011

Здесь есть несколько вариантов. Но без дополнительной информации о том, что вы собираетесь делать с данными после выполнения этого обновления, ответ Ларри Люстиг звучит как наиболее подходящий. Но следуют и другие варианты:

  • Создать столбец 2 как вычисляемый столбец вместо физического столбца.
  • Выполните вычисление, когда вы извлекаете данные из промежуточной таблицы (что также произойдет, если вы перейдете к предыдущему пункту.
  • Индексируйте столбец 2, а затем выполняйте обновления порциями по 10 000 записей или около того, где столбец 2 равен нулю. Это снизит неявный размер транзакции, , что, вероятно, в настоящее время и убивает вашу производительность .
1 голос
/ 08 января 2011

Практический способ проверить, не является ли что-то необычным, - это обновить только некоторые данные. Напишите представление, которое выбирает, скажем, первые 10000 строк, и запустите обновление для представления.

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

Если эти небольшие обновления кажутся излишне длинными, то займитесь расследованиями.

По крайней мере, это дает вам достойную испытательную площадку.

1 голос
/ 08 января 2011

Вы выполняете некоторые манипуляции со строками в поле - что-то, в чем SQL, как известно, плохо работает. Попробуйте написать SQL CLR функцию, которая делает то, что вам нужно, и используйте ее вместо SUBSTRING([column1], 1, CHARINDEX('.', [column1])-1).

0 голосов
/ 08 января 2011

Практический способ проверить, не является ли что-то необычным, - это обновить только некоторые данные.Напишите представление, которое выбирает, скажем, первые 10000 строк, и запустите обновление для этого представления.

Если 10000 строк обновятся в том виде, в котором вы ожидаете, что это будет «нормально» для вашего сервера, то из этого следует, что этопросто «много данных для обновления».

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

По крайней мере, это дает вам достойную основу для тестирования.

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