MySQLでのフェッチ中の行ロック

はい、はい、すべての「本物の少年」は、途方もない負荷に耐えることができるWebシステムを構築できます。 「Nepatzanoff」には、常にGoogleこのトピック専用の多くのサイトがあります 。 ただし、「成長の問題」には、クライアントにデータを正しく提供する問題だけでなく、クラスターでの有能な複製/配布も含まれます。 多くの場合、すべてが正反対であるという事実から問題が発生します-それは速すぎます。 最近のプラクティスの例を考えてみましょう。







与えられた:

  1. イベントキュー
  2. イベントはチェーンでリンクできるという事実
  3. イベントが「新しい」イベントを生成できるという事実
  4. このキューを処理するプロセッサ( デーモン )(必要なクラスをキャストし、ライブラリをロードし、イベントで実行する必要があるすべての処理を行います)。キュー内の各エントリを「処理済み」としてマークし、ログを書き込み、作業を続行します。
  5. MySQL DBMS(歴史的に)




これまでのところ、すべてがかなり明確です-id(int)、event_id(int fk)、event_data(blob)、execute_at(datetime)、executed_at(datetime)のフィールドを含むキューのテーブルがあります 悪魔は一度に1つのイベントを取り、彼自身の邪悪な行為をします:)



しかし、今ではプロジェクトが成長し、システム内のユーザー、クラスター内のマシン、キュー内のジョブがそれぞれ増えています。 ユーザーは、悪魔がチェーンの次のステップを処理するように決定するまで「3秒」待機し、それが壊れて、より多くのパフォーマンスをサポートすることを求め始めました。 「引き裂かれた金属」をサポートし、最終的にさらにいくつかのプロセッサを起動することにしました。



それに応じて、キューのアーキテクチャが変更されました-「ロック」が発生しました。 簡単に言えば、イベントの処理を開始する前に、デーモン「A」はこのイベントを「ビジー」とマークし、他のすべてのデーモンは「表示しない」とマークします。 したがって、サンプリングと処理のプロセスは次のようになり始めました(擬似コード)。



 if(event = db.select( "lock = 'false'およびexecute_at <NOW()LIMIT 1"のキューからIDを選択してください)){
 db.execute( "update queue set locked = 'true' where id =" + event.id);
 [...]
 }




また、システムが超膨張マシンのクラスター上に構築されていなかった場合、および「スペース」で約100個のデーモンがアクティブでなかった場合、すべてが正常に動作します。 というのは、実践が示しているように、最初のSQLクエリと2番目のSQLクエリの間に、さらにいくつかのデーモンを「くさびで締め」、同じタスクを開始するのは簡単だったからです。 1つのタスクが新しいタスクを「キャスト」できるという考慮事項から進むと、数日でキューに1,000,000以上のタスクが実行される可能性があり、サーバー上のログは10ヘクタールの使用可能なスペースを超えます。 何歳生きますか!



誰のせいですか? どうする トランザクションは望ましい結果を与えません。 生き方 誰を倒す? 誰が私の手を引き裂くべきですか?



そして、実際に何が必要ですか? ただし、 DBMSは、他のプロセスに対して選択が行われる行を自動的にブロックする必要があります (テーブルをブロックします-提供しません)。 つまり、他の悪魔は私のサンプルに陥った行を単純に見ないようにします。



そして何? そしてどうやって? それがすべてです-基本的な、MySQLにはSELECT * FROMテーブルFOR UPDATEコンストラクトがあります-これはまさにそれを行います。 したがって、処理コードを次のように書き直します。



 db.execute( "TRANSACTION START");
 if(event = db.select( "lock = 'false'およびexecute_at <NOW()LIMIT 1 FOR UPDATEのキューからIDを選択")){
 [...]
 }
 db.execute( "COMMIT");




それがすべての愛です!



PS注意! これはすべて、InnoDBのようなテーブルでのみ機能します!



All Articles