2009-09-04 86 views
0

我有一個大表,1B +記錄,我需要下拉並在每條記錄上運行算法。如何使用ADO.NET異步執行「select * from table」並在ado.net正在接收數據時開始逐行讀取行?ADO.NET異步閱讀器(隊列處理)

我也需要處理記錄後,我讀它們以節省內存。所以我正在尋找一種通過記錄拉下記錄的方法,並且基本上將記錄推入隊列進行處理。

我的數據源是oracle和mssql。我必須爲幾個數據源做這件事。

回答

8

您應該爲此使用SSIS

您需要了解ADO.Net數據提供者如何理解您可以做什麼以及不能做什麼的背景細節。例如,讓我們以SqlClient供應商爲例。確實,可以與BeginExecuteReader異步執行查詢,但是這種異步執行只是在查詢開始返回結果之前。在有線層面,SQL文本被髮送到服務器,服務器開始攪動查詢執行,並最終開始將結果行推送回客戶端。只要第一個數據包返回給客戶端,異步執行就完成並且執行完成回調。之後,客戶端使用SqlDataReader.Read()方法推進結果集。 SqlDataReader中沒有異步方法。這種模式可以解決複雜的查詢問題,這些查詢在一些嚴重的處理完成後返回的結果很少當服務器忙於產生結果時,客戶端處於空閒狀態,沒有線程被阻塞。然而,對於產生大型結果集的簡單查詢來說,情況完全不同(服務器似乎會遇到這種情況):服務器將立即產生分支並繼續將其推回給客戶端。異步回調幾乎是即時的,並且大部分時間將由客戶端遍歷SqlDataReader所花費。

你說你想把記錄放入內存隊列中。隊列的目的是什麼?如果您的算法處理速度低於DataReader結果集迭代的吞吐量,則該隊列將開始建立。它會消耗實時內存,最終會耗盡客戶端上的內存。爲了防止這種情況,你必須建立一個流量控制機制,即。如果隊列大小大於N,則不要再放入任何記錄。但要實現這一點,您必須暫停數據讀取器迭代,如果您這樣做,則將流量控制推送到服務器,以暫停查詢,直到通信管道再次可用(直到您開始從讀取器讀取)。最終,流程控制必須一直傳送到服務器,在任何生產者 - 消費者關係中始終是這種情況,生產者必須停止,否則中間隊列將被填滿。你的內存隊列根本沒有任何用處,除了使事情複雜化之外。您可以逐個處理來自閱讀器的項目,並且如果處理速度太慢,數據閱讀器將使流量控制應用於在服務器上運行的查詢。這會自動發生,因爲您不調用DataReader.Read方法。總結一下,對於大集處理,你不能進行異步處理,也不需要隊列。

現在是困難的部分。

你正在處理的是有沒有更新回數據庫?如果是,那麼你有更大的問題:

  • 你不能使用相同的連接寫回結果,因爲它是忙於數據讀取器。 SqlClient for SQL Server支持MARS,但只能解決SQL 2005/2008的問題。
  • 如果您要在事務中註冊讀取和更新(如果您的更新發生在不同的連接上)(參見上文),那麼這意味着使用分佈式事務(即使涉及的兩個屬性指向同一個服務器) 。分佈式事務處理很慢。
  • 您需要將處理分成幾個批次,因爲在單個事務中處理1B +記錄非常糟糕。這意味着您將不得不恢復處理中止的批處理,這意味着您必須能夠識別已經處理的記錄(除非處理是冪等的)。
+0

*此答案值得更多選票!* – JohnB 2012-01-30 18:05:14

1

DataReaderiterator block(又名generator)的組合應該很適合這個問題。 Microsoft提供的默認數據讀取器每次從數據源提取數據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從數據庫的時間提取數據的一個記錄。這將需要太多的往返。他們默認一次讀取幾行hunderd行(除非記錄非常大)。 – tuinstoel 2009-09-05 07:22:11

+0

添加了引用,其中聲明默認實現一次檢索一行。 – 2009-09-05 20:34:13

+0

默認情況下,OracleDataReader獲取64kb的塊,因此它將檢索多於一行的記錄,除非記錄非常大。在這裏閱讀:http://www.oracle.com/technology/oramag/oracle/06-jul/o46odp.html – tuinstoel 2009-09-06 06:30:15

0

一個好方法,這將是給拉了回來的數據塊,遍歷添加到您的隊列,然後再次調用。這將比每行觸及DB要好。如果你通過數字PK把它們拉回來,那麼這很容易,如果你需要通過一些你可以使用ROW_NUMBER()來做到這一點。

0

只要使用DbDataReader(就像damagednoob說的那樣)。這是滾動檢索數據的前向唯一方式。您不必處理數據,因爲DbDataReader只是前向的。

當您使用DbDataReader時,似乎是從數據庫中逐一檢索記錄。

然而稍微複雜一些:

甲骨文(可能的MySQL)將在一個時間內獲取數百行的往返量降低到數據庫。您可以配置datareader的獲取大小。大多數情況下,每次往返取100行或1000行無關緊要。然而,像1或2行這樣的非常低的值會減慢速度,因爲如果檢測數據值較低,則需要進行多次往返。

您可能不需要手動設置獲取大小,默認情況就好了。

EDIT1:在這裏看到一個Oracle例如:http://www.oracle.com/technology/oramag/oracle/06-jul/o46odp.html