База данных или плоский файл для 600К записей? - PullRequest
2 голосов
/ 27 июня 2009

Я пишу приложение на C #, которое должно вставлять около 600 тыс. Записей в базу данных в определенный момент времени.

Это очень простые записи: всего 3 длинных.

Я использую params для настройки команды, а затем перебираю данные в памяти для выполнения вставок, присваиваю значения параметрам команды в каждом цикле и выполняю command.ExecuteNonQuery ()

На SqlServer требуется около 50 секунд, и на MySql он работает медленнее, тогда как вставка тех же данных в плоский файл занимает всего несколько миллисекунд.

Я что-то не так делаю или база данных просто слишком медленная?

Ответы [ 12 ]

6 голосов
/ 27 июня 2009

Вы увидите большую скорость записи в плоский файл по нескольким причинам:

  • ExecuteNonQuery не группирует несколько операторов вставки в пакеты, поэтому вы выполняете полный межпроцессный обмен данными для каждой записи. Присылайте свои вставки в группах.
  • Данные, которые у вас уже есть, имеют форму плоского файла, так что вы можете запустить их все за одну запись или несколько записей с буферизацией.
  • Операции с базой данных, как правило, используют деревья, которые занимают n log n времени, в то время как простая конструкция в форме массива занимает линейное время. С другой стороны, если вы объединяетесь в отсортированный плоский файл, это займет некоторое время.
3 голосов
/ 27 июня 2009

Если все, что вам нужно, это вставить данные и никогда не читать их, тогда вы можете написать функцию noop и притвориться, что вставили их в / dev / nul. На самом деле вопрос как вы планируете использовать эти данные ? Вам нужно опросить, отфильтровать, отсортировать, ссылаться на отдельные записи? То есть. почему вы даже решили начать с базы данных, если плоский файл выглядит так же хорошо?

С SQL Server вы, безусловно, сможете добиться большей производительности с базой данных и вставлять со скоростью не менее 50-100 КБ в секунду. Ваша текущая точка удушья - вероятно, lgo flush на каждой вставке. Вы должны выполнить пакетные коммиты и убедиться, что ваш журнал находится на быстром массиве шпинделей. Начните транзакцию, вставьте достаточно записей, чтобы заполнить страницу журнала (64 КБ), затем подтвердите. Также стоит использовать батарею из 5-10 SqlCommands и соединений и использовать асинхронные команды (BeginExecuteNonReader с обратным вызовом) для параллельного запуска нескольких вставок. Таким образом, вы можете использовать все потерянные времена, которые вы сейчас теряете, при подготовке к передаче туда и обратно по сети и подготовке контекста выполнения.

2 голосов
2 голосов
/ 27 июня 2009

Так что это около 8 миллисекунд для одной строки по сравнению с этим для всего файла. Fair

База данных, безусловно, имеет гораздо больше возможностей:

  1. Анализ, проверка, выполнение SQL
  2. Расчет значений любых индексов
  3. Управление журналами отката, если это одна транзакция
  4. Запись в свой файл

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

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

1 голос
/ 27 июня 2009

У Айенде есть интересный код для пакетирования именно этих ExecuteNonQuery ситуаций. Открытие пакета запросов было вступительной статьей, где он рассказывает о SqlCommandSet, затем выпускает код в Там будут драконы: Rhino.Commons.SqlCommandSet .

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

1 голос
/ 27 июня 2009

Как сказал Алекс: используйте SqlBulkCopy, ничто не сравнится с его производительностью.

Это немного сложно, для примера кода посмотрите здесь:

http://github.com/SamSaffron/So-Slow/blob/1552b1293525bfe36f6c9b522e370de626ac6f05/Importer.cs

1 голос
/ 27 июня 2009

Я не могу вам сильно помочь с MySQL. Тем не менее, SQL Server 2005 и выше имеют довольно интригующую поддержку XML, которая может вам помочь. Я рекомендую просмотреть Updategrams, функцию, которая позволяет вам отправить пакет данных для вставки, обновления или удаления. Это может помочь вам повысить производительность с SQL Server, поскольку вам нужно всего лишь выполнить один оператор, а не 600 000 операторов. Я не уверен, что это будет так же быстро, как запись в необработанный файл, но это должно быть значительно быстрее, чем выдача отдельных операторов.

Вы можете начать изучение обновлений здесь: http://msdn.microsoft.com/en-us/library/aa258671(SQL.80).aspx

1 голос
/ 27 июня 2009

Вы делаете массовую вставку? Я бы использовал его, если вы уже знаете.

INSERT INTO dbo.NewTable(fields) 
SELECT fields 
FROM dbo.oldTable 
WHERE ...

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

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

0 голосов
/ 27 июня 2009

мое решение SQL Server 2005

StringBuilder sb = new StringBuilder();
bool bFirst = true;

foreach(Record r in myData)
{
    if (bFirst)
        sb.AppendLine("INSERT INTO tbl (f1, f2, f3)");
    else
        sb.AppendLine("UNION ALL");
    bFirst = false;

    sb.AppendLine("SELECT " + r.data1.ToString() + "," + 
        r.data2.ToString() + "," + r.data3.ToString());
}

SqlCommand cmd = new SqlCommand(sb.ToString(), conn);
cmd.ExecuteNonQuery();

Интересно, как это будет работать;)

0 голосов
/ 27 июня 2009

Я предполагаю, что вы делаете транзакционные вставки: вставки, которые выглядят так:

INSERT INTO dbo.MyTable (Field1, Field2, Field3)
VALUES (50, 100, 150)

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

Вероятно, самый простой способ сделать это с помощью BCP. Вот несколько ссылок об этом:

Далее вам нужно настроить SQL Server, чтобы вставить как можно больше записей. Ваша база данных находится в режиме полного восстановления или в простом режиме восстановления? Чтобы узнать это, перейдите в SQL Server Management Studio, щелкните правой кнопкой мыши имя базы данных и выберите Свойства. Режим полного восстановления будет регистрировать каждую транзакцию, но простой режим восстановления будет работать несколько быстрее. Находятся ли файлы данных и файлы журналов в отдельных массивах? Сколько дисков в каждом массиве и какой тип RAID (1, 5, 10)? Например, если и данные, и файлы журналов находятся на диске C, производительность будет низкой.

Далее вы тоже захотите накрыть стол. У вас есть ограничения и индексы в таблице? У вас уже есть другие записи, и есть ли другие люди, которые запрашивают это в то же время? Если это так, рассмотрите возможность создания пустой таблицы для загрузки данных без индексов или ограничений. Сбросьте все данные как можно быстрее, а затем примените ограничения или индексы или переместите данные в конечный пункт назначения.

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