0

私は簡単だと思っていたことに着手しました: 順番に (行ごとに) 読み取り、いくつかの値を計算し、同じ行を更新してから、テーブル全体の次の行に進みます。

コンテキスト: 1 つのフラット テーブル、2,600 万レコード、複合 PK (4 つの数値)。 物理テーブル サイズ 1.3 GB。レコードが処理される順序は関係ありません。これは、予見可能な将来のために一度だけ行われます。計算が複雑すぎてSQLで実行できません(少なくとも私にとっては:-)

これを行うための推奨される効率的な方法は何ですか?

私が試したこと: in を使用datareaderADO.NETます (古き良き VB6 の結果セットがなくなったため、はるかに簡単になりました)。reader.Read()ADO.NET は同じ接続でそれを好まないため、各ループ内で更新ステートメント (statement.ExecuteNonQuery) と組み合わせることは注意が必要でした。そのため、2つの接続を開く必要がありました。(更新クエリは WHERE 句で複合 PK を使用します。これは高速である可能性がありますが、更新しようとしているレコードにカーソルが既にあるため、まだ効率が悪いと思います。)

SELECT * FROM MyTableこのアプローチは機能しますが、クエリに基づくリーダーでは機能しません。LIMITタイムアウト エラーを回避するために、一度に数千行のチャンクを読み取る必要がありました。初期の実験から、2,600 万件のレコードの処理に 9 時間かかると見積もっています。一晩実行するように設定しましたが、戻ってきたとき、プロセスの 3 分の 1 で再びタイムアウトしていました。再起動した後SELECT、オフセットが大きくなると、LIMIT 句によってクエリが遅くなることがわかりました。残りの 65% の新しい見積もりは、さらに 20 時間を超えており、LIMIT オフセットが増加するにつれて長くなる可能性があります。

もっといい方法があるはず!?

(私はまた、エレガントであるがもちろんタイムアウトしたEFを試しました:-)

4

2 に答える 2

0

エリックとの上記の議論とさらなる実験の後、これが私の物語の結論です:

  • 実際、リレーショナル データベースは順次処理には適しておらず、そのようなプロセスをリレーショナル DBMS で実行すると、パフォーマンスが低下します。
  • データベースの歴史のある段階で、VB6 のようなプラットフォームは、テーブルの "カーソルベース" のトラバース、進行中のレコードの読み取りと更新を可能にする "Recordset" のようなツールを提供しました。これらは、ODBC や OLE (およびこれらに接続された DBMS) などのサポートされているプロバイダーで機能していました。レコードセットは当面の仕事に非常に魅力的に見えましたが、ADO.NET では使用できなくなりました (2013 年現在)。
  • 小規模から中規模のテーブルでは、設計エラーが許容されます。
  • OSはデータテーブル全体をキャッシュし、小規模から中規模のテーブルを処理しながらDBの非効率性をマスクします
  • テーブルのサイズ (および/または行数) が増加すると、システムがスラッシングし始め、異常な動作をしているように見えます。以前はパフォーマンスが悪かった可能性がありますが、上記の点により気付かないでしょう。
  • SELECT...LIMIT (1000 行のブロックを取得するため) を使用する私の方法は、2600 万行のテーブルの約 75% で停止しました。つまり、1000 行の各 SELECT が完了するまで数分かかります。
  • http://www.codeproject.com/Articles/8435/Simulating-Recordsets-with-ADO-NETに基づいてカーソルベースのレコードセットをエミュレートすることに手を出しましたが、MySQL はカーソルの UPDATE をサポートしておらず、サポートしているだけです私の計算はDBMSの外で実行されなければならなかったので、目的を破ったストアドプロシージャ内のカーソル。(SQL Server ではうまくいくかもしれません)
  • (私のテーブルは4つの部分の複合キーで構成されていたため)Ericが提案したように、最終的に単一の人工AutoIncrementキー/インデックスを作成したので、計算された範囲(0-999、1000-1999など)を使用してレコードをトラバースできました.これは、LIMIT を使用する場合とは異なり、テーブル トラバースの最初と最後で同じように高速でした。遅い 2 コアの Atom ネットブックで、AutoIncrement フィールドとインデックス/キーを (1 つのコマンド内で) 作成するのに、MySQL は 1 時間弱かかりました (150 バイト/レコードで 2600 万以上のレコードの場合)。
  • 上記の構成では、2,600 万件以上のレコードを完全にトラバーサルするのに約 9 時間かかりました。

これが同様の状況にある人に役立つことを願っています。コメントは大歓迎です。

于 2013-01-15T11:38:38.930 に答える
0

小さなバッチ (1000 レコード程度) でデータベースを更新することは、行 (またはページ) を長時間ロックすることを回避し、タイムアウトを回避するため、一般的には適切なアプローチです。アプローチのその部分は素晴らしいです。

開始値が大きい場合、LIMIT のパフォーマンスを向上させることができます。さまざまなアプローチがあります。これまでに見つけた最良の方法は、LIMIT をまったく使用せず、主キーの範囲を選択することです。

https://stackoverflow.com/a/1911210/141172

于 2013-01-11T01:33:23.613 に答える