分散ロック・技術選択

まず、分散データベース・ロックの実装に基づいて

  1. ペシミスティック・ロック

使用すると、選択...どこ...更新排他ロックのために

注:実現と一致する他の追加機能、ここで注意すべきは、名前のインデックスフィールド行かなければならない「名前=ロックはどこ」、それ以外の場合は、テーブルをロックしますです。いくつかのケースでは、このような小さなテーブルとして、MySQLのオプティマイザは、ロック・テーブル上の問題が生じ、インデックスを行くことはありません。

  1. オプティミスティック・ロック

いわゆる楽観的ロックのフロントとの最大の違いは、相互に排他的ではありませんCASベースの考え方、ノーロック待ちながら、アップデートバージョンが知覚に失敗した後にのみ、同時実行違反があると信じていません動作時のリソースの消費量。私たちの買いが、スパイクは売られ過ぎを防止するために、この実装を使用することです。
楽観的ロックをインクリメントバージョン番号フィールドを増加させることによって達成しました

(などのRedis、)キャッシュに基づいて、第2、分散ロックを達成するために

  1. 使用するコマンド:
    (1)SETNX
    SETNXキーヴァル:とキーが存在しない場合だけ、SETキーは、valを文字列、リターン1であり、キーが存在している場合は、何もしない、0が返されます。
    (2)有効期限が切れる
    キーのタイムアウトを設定し、ユニットは、デッドロックを回避するために、第2の、より多くのこの時間より自動的に、ロックを解除しますです:キータイムアウトの期限が切れます。
    (3)削除
    Deleteキー:Deleteキーを

分散ロックのRedisの実装を使用する場合は、これら三つのコマンドに主に使用されます。

  1. 思想を実現:
    ロック、ロックsetnxの使用を取得し、タイムアウトを追加するためのコマンドを使用する場合(1)ロックの時間を有効期限が切れ、自動的にロックを解除し、それを超える時間は、ロックは、今回のリリースにより、ランダムに生成されたUUIDの値であり、裁判官へのロック時間。
    (2)あなたがロックを取得するためにあきらめた場合、それは、この時間をかけて取得しタイムアウトを設定し、ロックを取得します。
    ロックが解除されたときにロックは、ロックが削除のためにリリースされている場合(3)、それは、ロックのUUIDによって決定されていません。

  2. 分散ロックの単純な実装コード:

コピーコード
1 / *
2 *分散ロック単純な実装コード4 /
5 DistributedLock {publicクラス。
6。
7民間最終JedisPool jedisPool;
8
。9公共DistributedLock(JedisPool jedisPool){
10 = this.jedisPool jedisPool;
11}
12である
13であります/
*
14 *ロック
15 * @paramのロック名ロックキー
タイムアウト16 * @param acquireTimeout取得
17 * @paramタイムアウトロック・タイムアウト
18 * @returnロック識別子
19 /
20 lockWithTimeoutパブリック文字列(文字列ロック名、acquireTimeout長い、長いですタイムアウト){
21はJedisコネチカット= NULLである;
22はretIdentifierストリング= NULLである;
23は、try {で
24 //が接続取得します
JedisPool.getResourceコネチカット= 25();
26は、ランダムに生成//値である
27 = UUID.randomUUID識別子文字列()のtoString();.
28 //ロック名、すなわち、キー値
29文字lockKey = "ロック" +ロック名。
30ロックが自動的にロックをかけて放出される時間の後//タイムアウト期間、
31はlockExpire INT =(INT)(タイムアウト/ 1000)であり、
32
33は、ロック獲得を控えるにこの時間をかけて、//ロックタイムアウトを取得し
34の長い端を= System.currentTimeMillis()+ acquireTimeout;
35一方(のSystem.currentTimeMillis()<完){
36 IF(conn.setnx(lockKey、識別子)== 1){
37 [conn.expire(lockKey、lockExpire)
38が返される//ロック時間の確認を解除するための値値
; 39識別子= retIdentifier
40戻りretIdentifier;
41である}
42が-1 //は、代表的なキーのタイムアウトを設定しないで、タイムアウト時間設定キー
43(lockKey)IF(conn.ttlある== -1){
44 conn.expire(lockKey、lockExpire)。
45}
46
47のtry {
48のThread.sleep(10)。
49}キャッチ(InterruptedExceptionある電子){
50にThread.currentThread())(割り込み。
51}
52}
53}キャッチ(JedisException電子){
54 e.printStackTrace();
55}最後に{
56 IF(CONN = NULL!){
57はconn.close();
58}
59}
60リターンretIdentifier。
61}
62
63 /
*
64 *释放锁
65 * @paramロック名锁的キー
66 * @param識別子释放锁的标识
67 * @return
68 * /
69公共ブールreleaseLock(文字列ロック名、文字列識別子){
Jedisコン= nullを70;
71はlockKey文字列=である"ロック:" +ロック名;
偽ブールretFlagに72 =;
73は試し{ある
74 jedisPool.getResourceコネティカット=();
75(真の)しばらく{
76 //モニターロック、トランザクションを開始する準備ができて
conn.watch 77(lockKey);
ロックが削除された場合、戻り値78 //値決意は、ロックを解除、ロックの正面にない
79 IF(identifier.equals(conn.get(lockKeyを))){
80取引conn.multi =トランザクション();
81 transaction.del(lockKey);
82 = transaction.exec結果リスト();
83 IF(結果== NULL){
84続行;
85}
86 = retFlag trueに;
87}
88コネチカット。 unwatch();
89 BREAK;
90}
91}キャッチ(JedisException E)である{
92 e.printStackTrace();
最後に{} 93
94 IF(コネチカット= NULL!){
95はconn.close();
96}
97}
98リターンretFlag;
99}
100}
のコードをコピー

  1. テストだけで実現分散ロック

使用スパイク50件のスレッドアナログ製品、使用例 - 発注の結果から、生成物の低減を達成するために、オペレータがロック状態かどうかを見ることができます。

あなたはjedisスレッド・プールを構成することができますシミュレーションスパイク・サービスは、分散ロックは、その使用のために、初期化時に渡されました。

コードをコピー
パブリッククラスサービスを{

private static JedisPool pool = null;

private DistributedLock lock = new DistributedLock(pool);

int n = 500;

static {
    JedisPoolConfig config = new JedisPoolConfig();
    // 设置最大连接数
    config.setMaxTotal(200);
    // 设置最大空闲数
    config.setMaxIdle(8);
    // 设置最大等待时间
    config.setMaxWaitMillis(1000 * 100);
    // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
    config.setTestOnBorrow(true);
    pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
}

public void seckill() {
    // 返回锁的value值,供释放锁时候进行判断
    String identifier = lock.lockWithTimeout("resource", 5000, 1000);
    System.out.println(Thread.currentThread().getName() + "获得了锁");
    System.out.println(--n);
    lock.releaseLock("resource", identifier);
}

}
コードをコピー

アナログのスレッドがサービスをスパイク。

コードのコピー
Threadクラス{拡張スレッドAでパブリック
プライベート・サービス・サービスを、

public ThreadA(Service service) {
    this.service = service;
}

@Override
public void run() {
    service.seckill();
}

}

Testクラス{公共
のpublic static無効メイン(文字列[] args){
-サービス-サービス・サービス・新新=();
のための(INT I = 0; I <50; I ++){
スレッドA新しい新=( -サービス)におけるスレッドAスレッドAで、
threadA.start( );
}
}
}
コードのコピー
以下の結果を、結果が順序付けされます。

書き込み絵は、ここで説明しました

ロックの一部コメントアウトした場合:

コードをコピー
{)(公共無効seckillを
ロックを解除するときを決定するためのロック値の値を返す//
; //文字列Indentifier = lock.lockWithTimeout( "リソース"、5000、1000)
のSystem.out.println(にThread.currentThread( ).getName()+ "取得されたロック");
のSystem.out.println(-n);
//lock.releaseLock("resource」、Indentifier)は;
}
コードは、コピー
結果から分かるように、いくつかの非同期がありますの:

書き込み絵は、ここで説明しました

第三に、実現飼育係分散ロックに基づきます

それが階層型ファイルシステムディレクトリツリー構造で内部に分散アプリケーションのオープンソースコンポーネントのための一貫したサービスを提供するのZooKeeperは、同じディレクトリの規定は、唯一の一意のファイル名を持つことができています。ZooKeeperの工程は、分散ロックの実装に基づいています。

(1)ディレクトリmylockを作成し;
;(2)スレッドがmylockディレクトリ内のノードの一時的なオーダーを作成する方法のロックを取得したいん
何があった場合、(3)はmylockディレクトリの下のすべての子ノードを取得し、その後、彼らの兄弟よりも小さくなります、現在の最小スレッド・シーケンス番号、ロックを取得する;
(4)スレッドBは、すべてのノードを取得し、彼らが自ノードより提供リスニング倍小さい最下位ノードではないと判定された;
(5)スレッド処理、自ノードを削除し、スレッドそれはロックを取得その後であればBは、変更イベントをリッスンし、それが最小のノードであるかどうかを決定します。

Apacheのオープン・ソース・ライブラリーキュレーターが、それはZooKeeperのクライアントであることが推奨され、InterProcessMutexキュレーターのオファーは、分散ロックを達成するためにロックを取得するために使用される方法は、ロックを解除するための解除方法を取得することです。

利点:高可用性が提供され、リエントラント、ブロッキング・ロック特性は、障害がデッドロックの問題を解決します。

短所:ので、作成する頻繁に必要と削除ノードの、パフォーマンス上のRedisの道として。

第四に、コントラスト

達成するための分散データベースロック
欠点を:

1.db業績が悪く、ロックテーブルの危険がある
非ブロック操作が失敗した後2、世論調査に必要、占有CPUリソースは、
3長い時間が、接続がより多くのリソースを消費することがコミットまたはロングポーリングしません。

達成ロック分散のRedis(キャッシュ)
欠点を:

1.削除して失敗したロックの有効期限コントロール不良
2ノンブロッキングは、操作が失敗し、世論調査、占有CPUリソースする必要があります。

ZK分散ロックを達成
デメリット:パフォーマンスは、書き込み動作のために主に、より良いのRedisが達成され(リリースロックを取得する)リーダー上で行われ、その後、フォロワーに同期する必要があります。

要するに:ZooKeeperの優れたパフォーマンスと信頼性。

(ローからハイに)感謝しやすさの観点からデータベース>キャッシュ>飼育係

実装の観点からは複雑(低〜高)飼育係> =キャッシュ>データベース

パフォーマンスポイント(低)からキャッシュ>飼育係> =データベース

信頼性の観点(低いものから高いものへ)飼育係>キャッシュ>データベースから

公開された33元の記事 ウォンの賞賛0 ビュー846

おすすめ

転載: blog.csdn.net/ninth_spring/article/details/104806617