5

最近、WPF アプリケーションを開始しました。それを BaseX (XML ベース) データベースに接続し、そこから約 100 万のエントリを取得しました。エントリを反復処理し、エントリごとに何かを計算してから、データベースに書き戻したいと思いました。

IEnumerable<Result> resultSet = baseXClient.Query("...", "database");
foreach (Result result in resultSet) 
{
    ...
}

問題: foreach の内部には到達しません。Query() メソッドは非常に高速に戻りますが、foreach に到達すると、C# はコレクションで何かを実行しているように見えますが、コードは非常に長い間継続していません (少なくとも 10 分、それ以上実行させないでください)。何が起きてる?取得するアイテムの数を制限しようとしました。100.000 件の結果を取得すると、同じことが発生しますが、コードは約 10 ~ 20 秒後に続行されます。100 万件の結果を完全に取得すると、C# は永遠にスタックしているように見えます...

何か案は?よろしく

編集:なぜこれが起こっ ているのか ご指摘のとおり、この動作の理由は、実際にMoveNext()は Enumerable 内の Enumerator が呼び出されたときにのみクエリが評価されるためと思われます。私のデータベースは一度に 1 つの値を返すことができないようですが、代わりに 100 万のデータセット全体を一度に返します。別のデータベース (全文検索をサポートしているため、可能であれば Apache Lucene) に切り替えて、この投稿を編集して何か変更があったかどうかをお知らせします。
PS: はい、100 万件の結果が多いことは承知しています。これはライブでの使用を意図したものではなく、データを準備するためのステップにすぎません。コードが数秒で実行されるとは思っていませんでしたが、それでもデータベースのパフォーマンスが非常に低いことに驚きました。

編集: 解決策そこで、XML データベースを Apache Lucine に移行しました。魔法のように動作します!もちろん、Lucine はテキストベースのデータベースであり、すべてのユースケースに適しているわけではありませんが、私にとっては驚異的に機能しました。数秒で 100 万を超えるエントリを反復処理できます。ループごとに 1 つのエントリがフェッチされます。非常にうまく機能します。

4

5 に答える 5

5

質問させてください-rsultSetを作成するときにデータをロードしていませんが、最初にアクセスしたとき(実行の遅延)、100万個のエントリをロードすると、それらをメモリに逆シリアル化するのに多くの時間がかかります。

XML データベースの非効率性へようこそ。

于 2012-07-02T18:50:22.250 に答える
3

100 万個というのは大変なことです。そのため、その数のアイテムを取得する操作には、かなりの時間がかかることが予想されます。使用するライブラリは、絶対に必要になるまでアイテムの取得を延期しないようです。そのため、「foreach」ステートメントの背後にあるすべてのアイテムを非表示にすることの影響がわかります。

何が起こるのですか:

「foreach」は単一の操作ではなく、IEnumerable と IEnumerator に対するいくつかの呼び出し: IEnumerable.GetEnumerator、IEnumerator.MoveNext への繰り返し呼び出し。

最初の呼び出しGetEnumeratorは、遅延実行 (LINQ クエリの記述方法で最も一般的な方法) または即時実行 (コレクションの場合のようです) で実装できます。

MoveNext を呼び出すと、1 つのアイテムだけを要求している場合や、各呼び出しで 1 つのアイテムしか取得できない場合でも、クエリ全体の即時実行がトリガーされる可能性があります。つまり、ほとんどの LINQ クエリは、イテレータから次の項目を 1 つだけ取得します。

于 2012-07-02T19:00:33.917 に答える
2

ここでの回答はすべて、foreach (遅延実行) で認識された問題の原因を示していますが、可能な解決策ではありません。このデータベースがサポートしているかどうかはわかりませんが、1 つの解決策は、データのチャンク全体を一度に取得するのではなく、小さなバッチで結果をページングすることです。

別の方法として、必要な計算を実行するデータベース クエリを記述して、データベースが 100 万レコードを送信する必要がないようにすることもできます。(繰り返しますが、このデータベースがそれをサポートしているかどうかはわかりません)

于 2012-07-02T19:21:02.103 に答える
0

foreach の前にクエリを強制的に評価するには、resultSet で ToList 関数を呼び出します。(問題がデータベースに永遠にかかる場合、問題は解決しません)

于 2012-07-02T18:51:06.323 に答える
-1

より伝統的な for ループを試して、それが機能するかどうかを確認しましたか?

IEnumerable<Result> resultSet = baseXClient.Query("...", "database");
for (int x =0; x < resultSet.Count; x++)
{
    Result result = resultSet[x];
    ...
}
于 2012-07-02T18:49:47.983 に答える