Есть ли способ многопоточности SqlDataReader? - PullRequest
7 голосов
/ 27 мая 2009

У меня есть Sql Query, который возвращает мне более полумиллиона строк для обработки ... Процесс не занимает много времени, но я хотел бы немного ускорить его с помощью многопроцессорной обработки. Учитывая приведенный ниже код, возможно ли многопоточность легко сделать что-то подобное?

using (SqlDataReader reader = command.ExecuteReader())
{
    while (reader.Read())
    {
        // ...process row
    }
}

Было бы идеально, если бы я мог просто получить курсор в начале и в середине списка результатов. Таким образом, у меня может быть два потока, обрабатывающих записи. Однако SqlDataReader не позволяет мне это делать ...

Есть идеи, как мне этого добиться?

Ответы [ 3 ]

6 голосов
/ 27 мая 2009

Настройка очереди производителя / потребителя, с одним процессом производителя, который извлекает данные из считывателя и записи очереди настолько быстро, насколько это возможно, но не выполняет "обработку". Затем какое-то другое число процессов (сколько вы хотите, зависит от вашей системы) для удаления из очереди и обработки каждой записи в очереди.

3 голосов
/ 27 мая 2009

Вы не должны читать столько строк на клиенте.

При этом вы можете разбить ваш запрос на несколько запросов и выполнить их параллельно. Это означает, что запускайте несколько SqlCommands в отдельных потоках, и каждый из них будет перетекать часть результата. Вопрос A + - как разделить результат, и это в значительной степени зависит от ваших данных и вашего запроса:

  1. Вы можете использовать диапазон клавиш (например, ID betweem 1 and 10000, ID between 10001 and 20000 и т. Д.)
  2. Вы можете использовать атрибут (например, RecordTypeID IN (1,2), RecordTypeID IN (3,4) и т. Д.)
  3. Вы можете использовать синтетический диапазон (т. Е. ROW_NUMBER() BETWEEN 1 and 1000 etC), но это очень проблематично для правого
  4. Вы можете использовать хеш (например, BINARY_CHECKSUM(*)%10 == 0, BINARY_CHECKSUM(*)%10==1 и т. Д.)

Вам просто нужно быть очень осторожным, чтобы запросы на разделы не перекрывались и блокировали во время выполнения (т. Е. Сканировали одни и те же записи и получали блокировки X), тем самым сериализуя друг друга.

0 голосов
/ 27 мая 2009

Это простой ранжированный запрос, такой как WHERE Id между 1 и 500000? Если это так, вы можете просто запустить N запросов, каждый из которых возвращает 1 / N диапазона. Но это помогает узнать, где вы находитесь в узком месте благодаря однопоточному подходу. Если вы выполняете непрерывное чтение с одного шпинделя диска для выполнения запроса, то вам, вероятно, следует придерживаться одного потока. Если он разделен между шпинделями по некоторому диапазону, то вы можете разумно настроить свои запросы, чтобы максимизировать пропускную способность с диска (то есть читать с каждого диска параллельно с отдельными запросами). Если вы ожидаете, что все строки находятся в памяти, вы можете распараллелить по желанию. Но если запрос является более сложным, вы не сможете легко разбить его на части без дополнительных затрат. В большинстве случаев описанные выше варианты не будут применяться должным образом, и производитель / потребитель, о котором упоминал Джоэл, будет единственным местом для распараллеливания. В зависимости от того, сколько времени вы тратите на обработку каждой строки, это может обеспечить только тривиальные выгоды.

...