mysql分散ロックの実装

3183 ワード

純データベーステーブル実装方式
まず、テーブルを作成します.
CREATE TABLE `methodLock` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '  ',
  `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '      ',
  `desc` varchar(1024) NOT NULL DEFAULT '    ',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '      ,    ',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='      ';

メソッドをロックしたい場合は、次のSQL:insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)を実行します.
なぜなら私たちはmethod_nameは一意性制約を行い,ここで複数の要求が同時にデータベースにコミットされると,データベースは1つの操作のみが成功することを保証し,操作に成功したスレッドがこの方法のロックを獲得し,メソッドボリュームコンテンツを実行できると考えられる.
メソッドの実行が完了した後、ロックを解除するには、以下のSqlを実行する必要があります.delete from methodLock where method_name ='method_name'のこのような簡単な実装には、以下のいくつかの問題があります.
  • このロックはデータベースの可用性に強く依存し、データベースは単一のポイントであり、データベースが停止すると、ビジネスシステムが使用できなくなります.
  • このロックには失効時間がなく、ロック解除操作に失敗すると、ロックレコードがデータベースに存在し続け、他のスレッドがロックを取得できなくなります.
  • このロックは、データのinsert操作によって、挿入に失敗すると直接エラーが報告されるため、非ブロックのみです.ロックを取得していないスレッドはキューキューに入りません.ロックを再取得するには、ロック取得操作を再トリガーします.
  • このロックは再入力できません.同じスレッドは、ロックを解除する前にロックを再取得できません.データにはすでにデータが存在しているからです.

  • もちろん、私たちは他の方法で上記の問題を解決することもできます.
  • データベースは単一ポイントですか?2つのデータベースを作って、データの前に双方向に同期します.いったん切るとすぐにバックアップに切り替えます.
  • は失効時間がありませんか?1つのタイミングタスクを行う限り、一定時間ごとにデータベース内のタイムアウトデータを
  • にクリーンアップします.
  • 非ブロック?whileループをしてinsertが成功するまで成功に戻ります.
  • 非再入?データベーステーブルにフィールドを付けて、現在ロックを取得しているマシンのホスト情報とスレッド情報を記録すると、次回ロックを取得するときにまずデータベースを照会し、現在のマシンのホスト情報とスレッド情報がデータベースで調べられるなら、直接ロックを彼に割り当てることができます.

  • データベースベースの排他ロック
    データテーブルのレコードを削除操作できるほか,データに付属するロックにより分散ロックを実現できる.
    作成したばかりのデータベーステーブルも使用します.分散ロックは、データベースの排他ロックによって実現できます.MySqlベースのInnoDBエンジンでは、以下の方法でロック操作を実現できます.
    public boolean lock(){
        connection.setAutoCommit(false)
        while(true){
            try{
                result = select * from methodLock where method_name=xxx for update;
                if(result==null){
                    return true;
                }
            }catch(Exception e){
    
            }
            sleep(1000);
        }
        return false;
    }
    

    クエリー文の後にfor updateを追加すると、データベースはクエリー中にデータベーステーブルに排他ロックを追加します.あるレコードに排他ロックが追加されると、他のスレッドはそのローレコードに排他ロックを追加できません.(ここでもう1つ言うと、InnoDBエンジンは、ロックをかけたときにインデックスで検索したときのみ行レベルロックを使用します.そうでなければ表レベルロックを使用します.ここでは、method_nameにインデックスを追加したいのですが、このインデックスは必ず一意のインデックスとして作成しなければなりません.そうしないと、複数のリロードメソッド間で同時にアクセスできないことに注意してくださいという形式のものになります.リロード方法はパラメータタイプも加算することをお勧めします)
    排他ロックを取得したスレッドは分散ロックを得ることができ、ロックを取得した後、方法のビジネスロジックを実行することができ、方法を実行した後、以下の方法でロックを解除することができると考えられます.
    public void unlock(){
        connection.commit();
    }
    
    

    ここにはもう一つの問題があるかもしれませんが、method_nameは一意のインデックスを使用し、for updateを使用して行レベルロックを使用することを示します.ただし、MySqlはクエリを最適化します.条件にインデックスフィールドが使用されていても、インデックスを使用してデータを取得するかどうかは、MySQLが異なる実行計画の代価を判断することによって決定されます.MySQLが、ロー・ロックではなくテーブル全体のスキャン効率が高いと考えている場合、インデックスは使用されません.こんなことが起こったら悲劇だ...
    もう1つの問題は、分散ロックのlockを行うために排他ロックを使用すると、1つの排他ロックが長時間コミットされないと、データベース接続が占有されます.類似の接続が多くなると、データベース接続プールが爆発する可能性があります.