Подтвердить что ты не робот

Самый быстрый способ вставить в таблицу SQL Server из .NET-кода?

Каков самый быстрый способ сделать это:

  • Одна таблица, никаких ссылок, которые я не могу предварительно заполнить (т.е. там есть один ссылочный ключ, но у меня есть все заполненные данные)
  • Количество данных. Мы говорим о сотнях миллионов строк в день, динамически вступая через API
  • Запросы должны/должны быть обработаны как можно скорее в сценарии почти реального времени (т.е. не выписывать файл для загрузки в день). 2 секунды - это нормальная максимальная задержка
  • Отдельные машины для данных/приложений и SQL Server

Что я делаю сейчас:

  • Совокупность до 32 * 1024 строк в массиве, а затем очередь.
  • Прочитайте очередь в 2-3 потоках. Вставьте в базу данных с помощью SqlBulkCopy.

Я получаю около 60k-75k строк, импортированных в секунду, чего недостаточно, но довольно близко. Я хотел бы поразить 250 000 строк.

Пока ничего не используется. Я получаю 20-процентные блоки "сетевого ввода-вывода", имеют одну базовую 80-процентную загрузку процессора. Диски пишут 7mb-14mb, в основном бездействуют. Средняя длина очереди на RAID 10 из 6 raptors составляет.... 0.25.

Кто-нибудь знает, как ускорить это? Более быстрый сервер (до сих пор он виртуальный, 8 ГБ оперативной памяти, 4 ядра, физический диск для передачи данных).


Добавление некоторых пояснений:

  • Это корпоративный SQL Server 2008 R2 на сервере 2008 R2. машина имеет 4 ядра, 8 гб баранов. Все 64 бит. 80% нагрузки приходится на эту машину, показывающую нагрузку около 20% cpu.
  • Таблица проста, не имеет первичного ключа, только индекс реляционной ссылки (ссылка на инструмент) и уникальный (в пределах набора инструментов, поэтому это не соблюдается) отметка времени.
  • Полями таблицы являются: отметка времени, ссылка на инструмент (без принудительного внешнего ключа), тип данных (char 1, одно из нескольких символов, указывающих, какие данные публикуются), цена (двойной) и объем (int). Как вы можете видеть, это ОЧЕНЬ тонкий стол. Данные, о которых идет речь, являются тиковыми данными для финансовых инструментов.
  • Вопрос также в том, что касается аппаратного обеспечения и т.д. - в основном потому, что я не вижу реального узкого места. Я вставляю несколько транзакций, и это дает мне преимущество, но мало. Диски, CPU не показывают значительную нагрузку, сетевой io-ожидания высоки (300 мс/секунду, 30% на данный момент), но это на той же платформе виртуализации, которая запускает JSUT на двух серверах и имеет достаточно ядер для запуска всех. Я в значительной степени открыт для "покупки другого сервера", но сначала хочу определить узкое место... особенно учитывая, что в конце дня я не разбираюсь в узком месте. Ведение журнала не имеет значения - объемные вставки НЕ входят в журнал данных как данные (без кластеризованного индекса).

Помогло ли вертикальное разбиение на разделы, например, байтом (tinyint), который разделил бы юниверс инструмента, например, на 16 таблиц, и, таким образом, я сделал до 16 вставок одновременно? Поскольку на самом деле данные поступают из разных обменов, я могу сделать раздел на обмен. Это было бы естественным полем разделения (которое фактически является инструментом, но я мог бы дублировать эти данные здесь).


Еще несколько пояснений: скорость еще выше (90k), теперь явно ограничена сетью IO между машинами, что может быть переключением VM.

Теперь я делаю соединение на 32 тыс. строк, размещаю временную таблицу, вставляю в нее с помощью SqlBUlkdCopy, ТОЛЬКО использую ОДИН SQL-оператор для копирования в основную таблицу - сводит к минимуму любое время блокировки в главной таблице.

В большинстве случаев время ожидания остается в сети IO. Кажется, я сталкиваюсь с проблемами, когда VM мудрый. Переместится на физическое оборудование в следующие месяцы;)

4b9b3361

Ответ 1

Если вы управляете 70k строк в секунду, вам пока очень повезло. Но я подозреваю это, потому что у вас очень простая схема.

Я не могу поверить, что вы спрашиваете об этой нагрузке на

  • виртуальный сервер
  • одиночный массив
  • Диски SATA

Сеть и ЦП совместно используются, IO ограничено: вы не можете использовать все ресурсы. Любая статистика загрузки, которую вы видите, не очень полезна. Я подозреваю, что сетевая нагрузка, которую вы видите, представляет собой трафик между двумя виртуальными серверами, и вы будете привязаны к IO, если вы решите этот

Прежде чем продолжить, прочитайте 10 уроков из 35 тыс. т/с. Он не использовал виртуальную коробку.

Вот что я сделал бы, не предполагая возможности SAN и без DR, если вы хотите увеличить объемы.

  • Купите 2 больших phyical сервера, RAM RAM типа unleveant, максимальная RAM, установите x64 install
  • Диски + контроллеры = самые быстрые шпиндели, самый быстрый SCSI. Или отличное NAS-хранилище.
  • 1000 МБ + сетевые адаптеры
  • RAID 10 с диском 6-10 для файла один для только вашей базы данных
  • Оставшийся диск RAID 5 или RAID 10 для файла данных

Для справки наша максимальная нагрузка составляет 12 миллионов строк в час (16 ядер, 16 ГБ, SAN, x64), но у нас есть сложность в нагрузке. Мы не в состоянии.

Ответ 2

Есть ли какие-либо индексы на столе, с которыми вы могли бы обойтись? EDIT: спрашивайте, когда вы печатали.

Можно ли превратить цену в целое число, а затем делить на 1000 или на какие-либо запросы?

Ответ 3

Вы пытались добавить pk в таблицу? Это улучшает скорость?

Существует также основанный на наборах способ использования таблиц таблиц для импорта данных csv из http://www.sqlservercentral.com/articles/T-SQL/62867/ (рядом с нижней, требуется бесплатная регистрация, но стоит того).

Вам может понравиться попробовать и проверить его производительность... с таблицей с таблицей с минимальной таблицей.

Ответ 4

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

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

Я написал статью о теме здесь: http://www.altdevblogaday.com/2012/05/16/sql-server-high-performance-inserts/

Общий ответ: вы грубо хотите создать тип таблицы:

CREATE TYPE item_drop_bulk_table_rev4 AS TABLE (
    item_id BIGINT,
    monster_class_id INT,
    zone_id INT,
    xpos REAL,
    ypos REAL,
    kill_time datetime
)

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

CREATE PROCEDURE insert_item_drops_rev4
    @mytable item_drop_bulk_table_rev4 READONLY
AS

INSERT INTO item_drops_rev4 
    (item_id, monster_class_id, zone_id, xpos, ypos, kill_time)
SELECT 
    item_id, monster_class_id, zone_id, xpos, ypos, kill_time 
FROM 
    @mytable

Код SQL Server выглядит следующим образом:

DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("item_id", typeof(Int64)));
dt.Columns.Add(new DataColumn("monster_class_id", typeof(int)));
dt.Columns.Add(new DataColumn("zone_id", typeof(int)));
dt.Columns.Add(new DataColumn("xpos", typeof(float)));
dt.Columns.Add(new DataColumn("ypos", typeof(float)));
dt.Columns.Add(new DataColumn("timestamp", typeof(DateTime)));

for (int i = 0; i < MY_INSERT_SIZE; i++) {
    dt.Rows.Add(new object[] { item_id, monster_class_id, zone_id, xpos, ypos, DateTime.Now });
}

// Now we&#039;re going to do all the work with one connection!
using (SqlConnection conn = new SqlConnection(my_connection_string)) {
    conn.Open();
    using (SqlCommand cmd = new SqlCommand("insert_item_drops_rev4", conn)) {
        cmd.CommandType = CommandType.StoredProcedure;

        // Adding a "structured" parameter allows you to insert tons of data with low overhead
        SqlParameter param = new SqlParameter("@mytable", SqlDbType.Structured);
        param.Value = dt;
        cmd.Parameters.Add(param);
        cmd.ExecuteNonQuery();
    }
}

Ответ 5

Все медленно.

Некоторое время назад мы решили аналогичную проблему (вставляем в DB десятки тысяч данных о ценах, так как я помню, что это было около 50K за период времени, и у нас было около 8 временных рамок, с которыми все столкнулись: 00, так что это было о 400K), и он работал очень быстро для нас (MS SQL 2005). Представьте, как он будет работать сегодня (SQL 2012):

<...init...>
if(bcp_init(m_hdbc, TableName, NULL, NULL, DB_IN) == FAIL)
    return FALSE;

int col_number = 1;

// Bind columns
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.SymbolName, 0, 16, (LPCBYTE)"", 1, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Time, 0, 4, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Open, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.High, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Low, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Close, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Volume, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;


<...save into sql...>
BOOL CSymbolStorage::Copy(SQL_SYMBOL_DATA *sd)
{
    if(!m_bUseDB)
        return TRUE;

    memcpy(&m_sd, sd, sizeof(SQL_SYMBOL_DATA));

    if(bcp_sendrow(m_hdbc) != SUCCEED)
        return FALSE;

    return TRUE;
}

Ответ 6

Не могли бы вы использовать горизонтальное разбиение? См.: http://msdn.microsoft.com/en-us/library/ms178148.aspx и http://msdn.microsoft.com/en-us/library/ms188706.aspx

Вы также можете посмотреть на этот вопрос и, возможно, изменить модель восстановления: Sql Server 2008 Настройка с большими транзакциями (700k + rows/transaction)

Некоторые вопросы: Какую версию SQL Server вы используете?

Почему одно ядро ​​на 80%? Это может быть узким местом, поэтому, вероятно, стоит что-то изучить.

Какую ОС вы используете, и это 64-бит?