2009-09-04 5 views
0

У меня есть большая таблица, 1B + записи, которые мне нужно снести и запустить алгоритм на каждой записи. Как я могу использовать ADO.NET для асинхронного выполнения «select * from table» и начать чтение строк один за другим, пока ado.net получает данные?Асинхронный считыватель ADO.NET (обработка очереди)

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

Мои источники данных - оракул и mssql. Я должен сделать это для нескольких источников данных.

ответ

8

Для этого вам необходимо использовать SSIS.

Вам нужно немного узнать о том, как работают поставщики данных ADO.Net, чтобы понять, что вы можете делать, и что вы не можете сделать. Например, возьмите поставщика. Верно, что асинхронно выполнять запросы с BeginExecuteReader, но это асинхронное выполнение выполняется до тех пор, пока запрос не начнет возвращать результаты. На уровне проводки текст SQL отправляется на сервер, сервер начинает опрокидывать выполнение запроса и в конечном итоге начнет толкать строки результатов обратно клиенту. Как только первый пакет возвращается клиенту, выполняется асинхронное выполнение и выполняется обратный вызов завершения. После этого клиент использует метод SqlDataReader.Read() для продвижения набора результатов. В SqlDataReader нет асинхронных методов. Эта модель работает с запросами на сложные запросы, которые возвращают мало результатов после того, как будет выполнена серьезная обработка. Пока сервер занят производством результата, клиент простаивает без блокировки потоков. Однако для простых запросов, которые производят большие результирующие наборы (как вам кажется), все совершенно по-другому: сервер будет немедленно производить обновления и будет продолжать возвращать их клиенту. Асинхронный обратный вызов будет почти мгновенным, и основная часть времени будет потрачена клиентом, итератором по SqlDataReader.

Вы говорите, что думаете о размещении записей в очереди памяти в первую очередь. Какова цель очереди? Если обработка алгоритма медленнее, чем пропускная способность результирующего набора результатов DataReader, то эта очередь начнет нарастать. Он будет потреблять живую память и, в конечном итоге, будет исчерпывать память на клиенте. Чтобы этого не произошло, вам нужно будет построить механизм управления потоком, т. Е. если размер очереди больше N, не помещайте в него больше записей. Но для этого вам придется приостановить итерацию чтения данных, и если вы сделаете это, вы нажмете управление потоком на сервер, который приостановит запрос до тех пор, пока канал связи не будет доступен снова (пока вы не начнете читать с читателя). В конечном счете, управление потоком необходимо прокладывать до сервера, что всегда имеет место в любом соотношении производителей и потребителей, продюсер должен остановиться, иначе промежуточные очереди заполняются. Ваша очередь в очереди не имеет никакой цели, кроме усложнения. Вы можете просто обрабатывать элементы из считывателя по одному, и если скорость обработки слишком медленная, считыватель данных будет применять управление потоком в запросе, запущенном на сервере.Это происходит автоматически, потому что вы не вызываете метод DataReader.Read.

Чтобы подвести итог, для обработки большого набора вы не можете выполнять асинхронную обработку, и нет необходимости в очереди.

Теперь сложная часть.

Является ли ваша обработка любой вид обновления в базе данных? Если да, то у вас есть гораздо большие проблемы:

  • Вы не можете использовать то же соединение для записи результата, потому что оно занято устройством чтения данных. SqlClient для SQL Server поддерживает MARS, но это решает проблему только с SQL 2005/2008.
  • Если вы собираетесь регистрировать чтение и обновление в транзакции, если ваши обновления происходят в другом соединении (см. Выше), то это означает использование распределенных транзакций (даже если оба конэнцитона связаны с одним и тем же сервером) , Распределенные транзакции медленны.
  • Вам нужно будет разделить обработку на несколько партий, потому что очень плохо обрабатывать записи 1B + в одной транзакции. Это также означает, что вы должны будете иметь возможность возобновить обработку прерывистой партии, что означает, что вы должны иметь возможность идентифицировать записи, которые уже были обработаны (если обработка не является идемпотентной).
+0

* Этот ответ заслуживает большего количества голосов! * – JohnB

1

Комбинация DataReader и iterator block (a.k.a. generator) должна хорошо подходить для решения этой проблемы. По умолчанию DataReaders предоставлены Microsoft pull data one record за один раз из источника данных.

Вот пример в C#:

static IEnumerable<User> RetrieveUsers(DbDataReader reader) 
{ 
    while (reader.NextResult()) 
    { 
     User user = new User 
         { 
          Name = reader.GetString(0), 
          Surname = reader.GetString(1) 
         }; 
     yield return user; 
    } 
} 
+0

Я не думаю, что DbDataReaders извлекать данные одной записи в то время, из базы данных. Для этого потребуется слишком много круговых поездок. Они берут несколько строк по умолчанию по умолчанию (если только записи не очень большие). – tuinstoel

+0

Добавлена ​​ссылка, в которой указано, что реализация по умолчанию получает одну строку за раз. –

+0

OracleDataReader извлекает по умолчанию блоки по 64 кб, поэтому он будет извлекать более одной строки, если записи не очень большие. Читайте здесь: http://www.oracle.com/technology/oramag/oracle/06-jul/o46odp.html – tuinstoel

0

Хороший подход к этому будет тянуть обратно данные в блоках, перебирать добавления в очередь снова звонит. Это будет лучше, чем ударить БД для каждой строки. Если вы потянете их обратно с помощью числового ПК, это будет легко, если вам нужно заказать что-то, что вы можете использовать ROW_NUMBER(), чтобы сделать это.

0

Просто используйте DbDataReader (точно так же, как поврежденно сказано). Это простой способ прокрутки полученных данных. Вам не нужно удалять данные, потому что DbDataReader доступен только вперед.

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

Это, однако, немного сложнее:

Oracle (и, возможно, MySQL) будет получать несколько 100 строк в то время, чтобы уменьшить количество обходов в базу данных. Вы можете настроить размер выборки datareader. В большинстве случаев неважно, вы получаете 100 строк или 1000 строк за поездку в оба конца. Однако очень низкое значение, такое как 1 или 2 строки, замедляет работу, потому что при низком значении, которое требуется для получения данных, требуется много раундов.

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

edit1: Смотрите здесь для примера Oracle: http://www.oracle.com/technology/oramag/oracle/06-jul/o46odp.html

Смежные вопросы