5

行がトランザクションで追加される (更新も削除もされない) だけのテーブルがあります (これが重要である理由を説明します)。クローン。

どうすればいいですか?どのプログラミング言語でも (私は Perl を使用していますが、それは関係ありません。)

この問題を解決する方法について私が考えた方法をリストアップし、正しい方法を教えてください (あるはずです...)。

私の頭に浮かんだ最初の方法は、フェッチされた行の最大の auto_incrementing ID を (ファイルに) 保存することでしたWHERE id > $last_id。しかし、それは行を見逃す可能性があります。新しい行がトランザクションに挿入されるため、id = 4 の行を保存するトランザクションよりも前に、id = 5 の行を保存するトランザクションがコミットされる可能性があります。行 4 が 1 秒後にコミットされると、フェッチされることはありません (4 は $last_id である 5 よりも大きいため)。

次に、過去 2 分間に日付フィールドを持つすべての行を cron ジョブで取得できると考えました。これらの行のうち、前回の cron ジョブの実行で再度取得された行を確認します (これを行うには、どこかに保存する必要があります)。どの行 ID が取得されたか)、比較し、新しいものだけを処理します。残念ながら、これは複雑であり、特定の挿入トランザクションが奇妙なデータベースの理由でコミットするのに 2 分半かかる場合に発生する問題も解決しません。取得する cron ジョブ。

次に、RabbitMQ などのメッセージ キュー (MQ) をインストールすることを考えました。トランザクションの挿入を行う同じプロセスが、RabbitMQ に新しい行を通知し、RabbitMQ が新しい行を処理する常時実行プロセスに通知します。そのため、直前に挿入された行のバッチを取得する代わりに、そのプロセスは新しい行が書き込まれるたびに 1 つずつ取得します。これは良さそうに思えますが、障害点が多すぎます。RabbitMQ が (再起動などで) 一瞬ダウンする可能性があり、その場合、受信プロセスが新しい行を受信することなく挿入トランザクションがコミットされます。したがって、新しい行は見逃されます。良くない。

もう1つの解決策を考えました.受信プロセス(30個あり、まったく同じデータに対してまったく同じジョブを実行しているため、同じ行が各受信プロセスで1回ずつ30回処理されます)は別のテーブルに書き込むことができます処理時に行 X を処理した後、時が来たら、OUTER JOIN クエリを使用して、「have_processed」テーブルに存在しないメイン テーブルのすべての行を要求できます。しかし、新しいエントリを見つけるために 2 つのテーブルの ID のリスト全体を比較する必要があるため (およびテーブルは巨大で、毎分大きくなっています)。受信プロセスが 1 つしかない場合は高速でした。その場合、「have_read」という名前のインデックス付きフィールドを追加できたはずです。

それを行う正しい方法は何ですか?何を指示してるんですか?質問は簡単ですが、(私にとって) 解決策を見つけるのは難しいようです。

ありがとうございました。

4

5 に答える 5

1

私はこれについてしばらく考えてきました。それで、私がそれを正しく理解したかどうか見てみましょう。あなたは巨大なテーブルを持っています.Nは、時間によって変化する可能性がありますが、プロセスが書き込みます(プロデューサーと呼びましょう)。さて、これらのM個の量が時間とともに変化し、少なくともこれらのレコードが追加されるたびに処理する必要がある他のプロセスがあります(それらを消費者と呼びましょう)。

検出された主な問題は次のとおりです。

  • ソリューションが動的な N および M で機能することを確認する
  • 各消費者の未処理の記録を追跡する必要があります
  • 膨大な量のレコードがあるため、ソリューションは可能な限りエスカレートする必要があります

それらの問題に取り組むために、私はこれについて考えました。このテーブルを作成します (太字の PK):

  • PENDING_RECORDS( ConsumerID , HugeTableID )

コンシューマーを変更して、HUGE_TABLE にレコードを追加するたびに M レコードを PENDING_RECORDS テーブルにも追加して、HugeTableID とその時点で存在する各 ConsumerID を持つようにします。コンシューマーが実行されるたびに、PENDING_RECORDS テーブルにクエリが実行され、それ自体に少量の一致が見つかります。次に、HUGE_TABLE に対して結合し (左結合ではなく内部結合になることに注意してください)、処理する必要がある実際のデータを取得します。データが処理されると、コンシューマは PENDING_RECORDS テーブルからフェッチされたレコードを削除し、適度に小さく保ちます。

于 2013-09-06T00:21:17.200 に答える
0

興味深い、私は言わなければならない:)

1) まず、行のみが追加されたテーブルにフィールドを追加することは可能ですか (「transactional_table」と呼びましょう)。つまり、それは設計パラダイムであり、このテーブルに対して何らかの更新を行わない理由があるのでしょうか、それとも「構造的に」ブロックされているのでしょうか (つまり、db に接続しているユーザーには、このテーブルで更新を実行する権限がありません) ?

それを行う最も簡単な方法は、このテーブルに「have_read」列をデフォルト 0 で追加し、フェッチされた行でこの列を 1 で更新することです (30 個のプロセスが同時にこれを実行しても、非常に高速であるため問題ありません)。データが破損することはありません)。30 のプロセスが同じ 1000 行をフェッチ済みとしてマークしたとしても、何も壊れていません。ただし、InnoDB を操作しない場合、パフォーマンスに関する限り、これは最善の方法ではない可能性があります (MyISAM は更新時にテーブル全体をロックしますが、InnoDB は更新された行のみをロックします)。

2)これが使用できない場合-最後のソリューションとして提供したソリューションを少し変更して確認します。テーブルを作成し (例: fetched_ids)、フェッチされた行の ID をそのテーブルに保存します。次に、次のようなものを使用できます。

SELECT tt.* from transactional_table tt 
RIGHT JOIN fetched_ids fi ON tt.id = fi.row_id 
WHERE fi.row_id IS NULL

これにより、トランザクション テーブルから、まだフェッチされていない行が返されます。(tt.id) と (fi.row_id) の両方に (理想的には一意の) インデックスがある限り、これは大規模なデータ セットでも問題なく機能するはずです。MySQL は、インデックス付きフィールドの JOINS をうまく処理します。試してみることを恐れないでください - 新しいテーブルを作成し、それに ID をコピーし、それらのいくつかを削除して、クエリを実行してください。結果が表示され、満足できるかどうかがわかります:)

PS もちろん、この 'fetched_ids' テーブルへの行の追加は、不要な重複を作成しないように慎重に実行する必要があります (30 の同時プロセスは、必要なデータの 30 倍を書き込む可能性があります。パフォーマンスが必要な場合は、このケースに注意する必要があります)。

于 2013-09-05T23:46:51.697 に答える
0

次のような構造を持つ 2 番目のテーブルはどうでしょうか。

source_fk - 読み取りたいデータ行の ID を保持します。process_id - 30 個のプロセスのいずれかの一意の ID です。

次に、LEFT JOIN を実行し、指定された process_id に一致するエントリを持つアイテムをソースから除外します。

結果を取得したら、戻って取得した結果ごとに source_fk と process_id を追加します。

これに関するプラスの 1 つは、後で問題なくプロセスを追加できることです。

于 2013-09-06T00:06:57.427 に答える