SqlBulkCopy работает медленно, не использует полную скорость сети

последние пару недель я создавал общий скрипт, который может копировать базы данных. Цель состоит в том, чтобы иметь возможность указать любую базу данных на каком-либо сервере и скопировать ее в какое-то другое место, и она должна копировать только указанный контент. Точный контент, который нужно скопировать, указывается в файле конфигурации. Этот сценарий будет использоваться примерно в 10 различных базах данных и запускаться еженедельно. И в итоге мы копируем только 3–20% баз данных размером до 500 ГБ. Для этого я использовал сборки SMO. Я впервые работаю с SMO, и мне потребовалось время, чтобы создать общий способ копирования объектов схемы, файловых групп и т. Д. (На самом деле помогло найти несколько плохо сохраненных процедур).

В целом у меня есть рабочий сценарий, который не работает (а иногда и не работает), и я надеялся, что вы, ребята, сможете помочь. При выполнении команды WriteToServer для копирования большого количества данных (> 6 ГБ) мой тайм-аут достигает 1 часа. Вот основной код для копирования данных таблицы. Скрипт написан на PowerShell.

$query = ("SELECT * FROM $selectedTable " + $global:selectiveTables.Get_Item($selectedTable)).Trim()
Write-LogOutput "Copying $selectedTable : '$query'"            
$cmd = New-Object Data.SqlClient.SqlCommand -argumentList $query, $source
$cmd.CommandTimeout = 120;
$bulkData = ([Data.SqlClient.SqlBulkCopy]$destination)
$bulkData.DestinationTableName = $selectedTable;
$bulkData.BulkCopyTimeout = $global:tableCopyDataTimeout # = 3600
$reader = $cmd.ExecuteReader();
$bulkData.WriteToServer($reader); # Takes forever here on large tables

Исходная и целевая базы данных расположены на разных серверах, поэтому я также отслеживал скорость сети. Загрузка сети никогда не превышала 1%, что меня очень удивило. Но когда я просто передаю несколько больших файлов между серверами, загрузка сети возрастает до 10%. Я попытался установить для $ bulkData.BatchSize значение 5000, но на самом деле ничего не изменилось. Увеличение BulkCopyTimeout до еще большего количества решит только тайм-аут. Мне очень хотелось бы знать, почему сеть не используется в полной мере.

У кого-нибудь еще была эта проблема? Мы будем благодарны за любые предложения по сети или массовому копированию. И, пожалуйста, дайте мне знать, если вам понадобится дополнительная информация.

Спасибо.

ОБНОВЛЕНИЕ

Я настроил несколько параметров, которые увеличивают производительность SqlBulkCopy, например, установил простое ведение журнала транзакций и предоставил блокировку таблицы для SqlBulkCopy вместо блокировки строк по умолчанию. Также некоторые таблицы лучше оптимизированы для определенных размеров партии. В целом продолжительность копии сократилась примерно на 15%. И что мы будем делать, так это выполнять копию каждой базы данных одновременно на разных серверах. Но у меня все еще возникает проблема с тайм-аутом при копировании одной из баз данных.

При копировании одной из более крупных баз данных есть таблица, для которой я постоянно получаю следующее исключение:

System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding. 

Он выдается примерно через 16 минут после начала копирования таблицы, которой нет рядом с моим BulkCopyTimeout. Хотя у меня есть исключение, таблица полностью копируется в конце. Кроме того, если я усекаю эту таблицу и перезапускаю свой процесс только для этой таблицы, таблицы копируются без каких-либо проблем. Но копирование всей базы данных всегда терпит неудачу для этой единственной таблицы.

Я попытался выполнить весь процесс и сбросить соединение перед копированием этой ошибочной таблицы, но все равно возникла ошибка. Мои SqlBulkCopy и Reader закрываются после каждой таблицы. Любые предложения относительно того, что еще могло приводить к сбою сценария каждый раз?

CREATE TABLE [dbo].[badTable](
[someGUID] [uniqueidentifier] NOT NULL,
[xxx] [uniqueidentifier] NULL,
[xxx] [int] NULL,
[xxx] [tinyint] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NULL,
[xxx] [uniqueidentifier] NOT NULL,
[xxx] [uniqueidentifier] NULL,
CONSTRAINT [PK_badTable] PRIMARY KEY NONCLUSTERED 
(
[someGUID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Для этой таблицы в целевой БД нет индексов.


person Alex    schedule 15.12.2010    source источник
comment
Размер партии 5000 кажется низким, а неограниченный слишком большим. Я бы посоветовал попробовать размер партии от 50 000 до 100 000.   -  person Chad Miller    schedule 16.12.2010
comment
Спасибо за ваш вклад. Я установил размер пакета на 50000, но опять же ничего особенного не изменилось. Загрузка сети по-прежнему колеблется на уровне 1%. Просматривая файл журнала, время, необходимое для копирования больших таблиц, остается одинаковым. На самом деле для одной из таблиц, строки которой содержали большой контент, потребовалось больше времени. Кроме того, я забыл упомянуть об этом раньше, но сам скрипт запускается на сервере, на котором создается новая база данных.   -  person Alex    schedule 16.12.2010
comment
Это почти наверняка проблема с настройками ключа / индекса в целевой таблице. Поэтому, пожалуйста, опубликуйте свои определения таблиц и индексов.   -  person RBarryYoung    schedule 21.12.2010
comment
Я перестал создавать индексы для таблиц перед копированием данных, и теперь процесс копирования данных стал намного быстрее. К сожалению, эта таблица все еще вызывает ошибку.   -  person Alex    schedule 22.12.2010


Ответы (3)


Рассматривали ли вы удаление индексов, вставку и повторную индексацию?

person HLGEM    schedule 17.12.2010

Я использовал набор данных и задаюсь вопросом, будет ли это быстрее:

$ds=New-Object system.Data.DataSet
$da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
[void]$da.fill($ds)
bulkData.WriteToServer($ds.Tables[0])
person Chad Miller    schedule 16.12.2010
comment
Еще раз спасибо за ответ, в целом этот метод был медленнее. Он использовал намного больше процессора, но иногда использование сети достигало 2,5%. Проблема в том, что это не было постоянных 2,5%, а также было на 0 в течение длительного периода (во время процесса записи на диск?). Возможно, я мог бы снова поиграть с размером пакета, но я думаю, что вместо этого просто попробую bcp для передачи данных. Я слышал, это должно быть быстрее. - person Alex; 17.12.2010
comment
Хотя использование bcp начинается хорошо (полное использование скорости сети), команда bcp постепенно замедляется почти до полной остановки. Эта команда bcp значительно замедляется при копировании последних строк больших таблиц и сбрасывается на полную скорость для новых таблиц. Я должен что-то упустить из виду в базе данных sql itselt. Я слышал, что кластеризация таблиц может вызвать такие проблемы. - person Alex; 18.12.2010

SqlBulk Copy - безусловно, самый быстрый способ копирования данных в таблицы SQL.
Ваша скорость должна превышать 10 000 строк в секунду.
Чтобы проверить функциональность массового копирования, попробуйте DBSourceTools. (http://dbsourcetools.codeplex.com)
Эта утилита предназначена для создания сценариев баз данных на диске, а затем воссоздать их на целевом сервере.
При копировании данных DBSourceTools сначала экспортирует все данные в локальный XML-файл, а затем выполнит массовое копирование в целевую базу данных.
Это поможет чтобы определить, где находится ваше узкое место, разбив процесс на два этапа: один для чтения и один для записи.

person blorkfish    schedule 21.12.2010