並行テーブルベースのキューを実装する最良の方法


10

MySQLに、処理されるリンクのキューを表すテーブルがあります。リンクは外部アプリによって1つずつ処理され、最後に削除されます。これは大量のキューであり、処理アプリの複数のインスタンスが複数のサーバーに分散しています。

各レコードが1つのアプリのみによって選択されるようにするにはどうすればよいですか?レコードにフラグを付ける/ロックする方法はありますか?

現在、2つ以上が同じリンクを取得しないようにするために、各インスタンスが特定のレコードセット(IDのMODに基づく)のみを取得することを許可していますが、これはキューの処理を増やす透過的な方法ではありません新しいインスタンスを追加するだけでスピードアップ。


私のマントラ:「それをキューに入れないでください、それをしてください」。つまり、タスクをキューに投入する代わりに、プロセスを起動してタスクを実行します。
リックジェームズ

回答:


7

まず、MySQLは、これを実装するために考えられる最悪のソフトウェアの1つです。非常に動的な場合は特にそうです。その理由は、MEMORYやMyISAMのようなエンジンは全テーブルロックのみであり、InnoDBのようなより適切なエンジンは(ACIDプロパティを提供するために)書き込みペナルティが高く、空間的および時間的に近いレコードにアクセスするために最適化されている(これらはメモリに設定されている)ためです。 )。MySQLには適切な変更通知システムもありません。これはポーリングとして実装する必要があります。そのタスクのためにさらに最適化された数十のソフトウェアがあります

そうは言っても、パフォーマンス/効率の要件がそれほど高くない場合は、この種のアクセスを正常に実装するのを見てきました。多くの人々は、ビジネスロジックのほんの一部のために、完全に別個のテクノロジーを導入して維持する余裕はありません。

SELECT FOR UPDATEあなたが探しているものです-シリアル化を読み取​​ります。UPDATE / DELETEは、実行中のMYSQLトランザクション中に常に行をロックしますが、プロセスが進行している間は大きなトランザクションを回避したい場合があります。

START TRANSACTION;
SELECT * FROM your_table WHERE state != 'PROCESSING' 
  ORDER BY date_added ASC LIMIT 1 FOR UPDATE;
if (rows_selected = 0) { //finished processing the queue, abort}
else {
UPDATE your_table WHERE id = $row.id SET state = 'PROCESSING'
COMMIT;

// row is processed here, outside of the transaction, and it can take as much time as we want

// once we finish:
DELETE FROM your_table WHERE id = $row.id and state = 'PROCESSING' LIMIT 1;
}

MySQLは、行を選択するときに、1つを除くすべての同時選択をロックします。これにより、同時に多くのロックされた接続が発生する可能性があるため、初期トランザクションをできるだけ小さくし、一度に複数の行を処理するようにしてください。


ありがとう。ロックを大きくすると(LIMITを10に変更することで)パフォーマンスが向上すると思いますか?
ミゲルE

@MiguelE一般に、はい、処理に費やす時間が長く、他のトランザクションと衝突する可能性が低いほど、より良いです。ただし、場合によっては、逆の影響(より多くのトランザクションがロックされる)を引き起こす可能性もあります。常に最初にテストしてください。テーブルに適切にインデックスを付けることも重要です。そうしないと、一部の分離モードでテーブルが完全にロックされる可能性があります。
jynus

1
また、プロセスがハングしてタイムアウトメカニズムを実装したい場合に備えて、行の処理を開始した日付を追跡することをお勧めします。
ジュリアン

3

この記事で説明したように、MySQL 8ではSKIP LOCKEDとNO WAITの両方のサポートが導入されました。

SKIP LOCKEDは、ジョブキュー(バッチキューとも呼ばれます)の実装に役立ちます。これにより、他の同時トランザクションによって既にロックされているロックをスキップできます。

NO WAITは、同時トランザクションがロックにも関係しているロックを解放するまで待機しないようにするのに役立ちます。NO WAITがない場合、ロックが解放される(コミットまたは解放時に、現在ロックを保持しているトランザクションによる)か、ロック獲得がタイムアウトするまで待機する必要があります。したがって、NO WAITは、値がのロックタイムアウトのように機能します0

スキップロックと待機なしの詳細については、こちらの記事をご覧ください。


0

私はオフラインのDBCCチェックで同様のことを行いました(バックアップの復元を実行する2つのサーバー、次にDBCC checkdb)。1台のサーバーが31台のサーバーのバックアップをすべて昨日収集し、それらをキューに入れてから、そのサーバーと別のサーバーがそのキューからプルします。サーバーの数は多くありませんが、方法は同じである必要があります。アプリケーションサーバーにキューに対して更新クエリを実行させ、日付/時刻フィールドと「アプリサーバー」フィールドをそのアプリサーバーの名前またはより良い数値のIDで更新します。これによりロックが発生するか、別のサーバーから次の行を取得しているロックがすでに存在する場合、そのロックはブロックされ、他のアプリが次の行の取得を完了するまで待機します。次に、アプリがアプリフィールドのキューから最新のレコードをプルバックして、必要な情報を取得するようにします。MySQLの使用 '

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.