私は分散ロックについて何度も読み返してきました。最善の解決策をお教えします

私は分散ロックについて何度も読み返してきました。最善の解決策をお教えします

[[392389]]

分散ロックのシナリオ

フラッシュセールのシナリオケース

フラッシュセールでは、在庫の過剰販売や値引きの繰り返しなどの同時発生の問題を防ぐ必要があります。通常、共有リソースの競合によって生じるデータの不整合の問題を解決するには、分散ロックを使用する必要があります。

携帯電話のフラッシュセールのシナリオを例にとると、ラッシュ購入のプロセスには通常 3 つのステップがあります。

1. 該当する製品の在庫を減算します。 2. 製品の注文を作成します。 3. ユーザーが支払います。

このようなシナリオでは、分散ロックを使用して問題を解決できます。たとえば、ユーザーがフラッシュセールの「注文」リンクを入力すると、商品の在庫をロックし、操作が完了した後に在庫の減額などの操作を完了することができます。ロックを解除すると、次のユーザーが引き続き入ることができ、在庫のセキュリティが確保されます。また、フラッシュセールの失敗による DB ロールバックの数も削減できます。全体のプロセスを下の図に示します。

注: ロックの粒度は、特定のシナリオと要件に応じて検討する必要があります。

3種類の分散ロック

Zookeeper の分散ロック実装では、主に Zookeeper の 2 つの機能が使用されます。

  1. Zookeeperノードは繰り返し作成できません
  2. Zookeeper の Watcher 監視メカニズム

不公平なロック

不公平なロックの場合、ロックのプロセスを次の図に示します。

長所と短所

実際、上記の実装には利点と欠点があります。

アドバンテージ:

実装は比較的単純で、通知メカニズムを備えており、より高速な応答を提供できます。これはReentrantLockの考え方に少し似ています。ノードの削除が失敗するシナリオでは、セッション タイムアウトによってノードを削除できることが保証されます。

欠点:

重量が重いため、ロックの数が多いと「群れを混乱させる」という問題が発生する可能性があります。

「群れを怖がらせる」とは、ノードが削除されると、このノードの削除アクションのために Watcher にサブスクライブしている多数のスレッドがコールバックすることを意味し、これは Zk クラスターにとって非常に有害です。したがって、この現象を回避する必要があります。

「ショック集団」の解決:

「雷鳴の群れ」問題を解決するには、ノードをサブスクライブする戦略を放棄する必要がありますが、どうすればよいでしょうか?

  1. ロックをディレクトリに抽象化し、複数のスレッドがこのディレクトリの下に瞬時に連続ノードを作成します。 Zookeeper がノードの順序を保証するため、ノードの順序を使用してロックを判断できます。
  2. まず、連続ノードを作成し、次に現在のディレクトリ内の最小のノードを取得し、その最小のノードが現在のノードであるかどうかを判断します。そうであれば、ロックは正常に取得されます。そうでない場合、ロックの取得は失敗します。
  3. ロックの取得に失敗したノードは、現在のノードの前の連続ノードを取得し、このノードのリスナーを登録し、ノードが削除されたときに現在のノードに通知します。
  4. ロック解除時はノードを削除した後に次のノードに通知されます。

フェアロック

不公平なロックの欠点に基づいて、次の解決策を通じてそれを回避できます。

長所と短所

利点:前述のように、一時的なシーケンシャル ノードを使用すると、複数のノードの同時競合ロックを回避でき、サーバーへの負荷を軽減できます。

欠点:読み取り/書き込みシナリオでは、一貫性の問題を解決できません。読み取り中にロックが取得されると、パフォーマンスが低下します。このような問題には、JDK の ReadWriteLock などの読み取り/書き込みロックを使用できます。

読み取り/書き込みロックの実装

読み取り/書き込みロックの特性: 複数のスレッドが読み取りを行っている場合、読み取り/書き込みロックは同時に読み取ることができ、ロックフリーの状態になります。書き込みロックが動作中の場合、読み取りロックは書き込みロックを待機する必要があります。書き込みロックを追加する場合、前の読み取りロックが同時実行されるため、書き込みロックを実行する前に最後の読み取りロックの完了を監視する必要があります。手順は次のとおりです。

  1. 読み取り要求。前の要求が読み取りロックの場合は、監視せずに直接読み取ることができます。前に 1 つ以上の書き込みロックがある場合は、最後の書き込みロックのみを監視する必要があります。
  2. 書き込み要求の場合、前のノードを監視するだけで済みます。 Watcher メカニズムはミューテックスと同じです。

分散ロックの実践

この記事のソースコードで使用されている環境: JDK 1.8、Zookeeper 3.6.x

キュレーターコンポーネントの実装

POM 依存関係

  1. <依存関係>
  2. <グループ ID>org.apache.curator</グループ ID>
  3. <artifactId>キュレーターフレームワーク</artifactId>
  4. <バージョン>2.13.0</バージョン>
  5. </依存関係>
  6. <依存関係>
  7. <グループ ID>org.apache.curator</グループ ID>
  8. <artifactId>キュレーターレシピ</artifactId>
  9. <バージョン>2.13.0</バージョン>
  10. </依存関係>

ミューテックスロックアプリケーション

Zookeeper の不公平なロックの「集団ショック」効果により、不公平なロックは実際には Zookeeper における最良の選択ではありません。以下は、Zookeeper 分散ロックを使用するためのスパイクをシミュレートする例です。

  1. パブリッククラスMutexTest {
  2. 静的ExecutorService executor = Executors.newFixedThreadPool(8);
  3. 静的AtomicIntegerストック = 新しいAtomicInteger(3);
  4. 公共 静的void main(String[] args)はInterruptedExceptionをスローします{
  5. CuratorFrameworkクライアント = getZkClient();
  6. 文字列キー= "/lock/lockId_111/111" ;
  7. 最終的な InterProcessMutex mutex = 新しい InterProcessMutex(client, key );
  8. ( int i = 0; i < 99; i++) {
  9. 実行者.送信(() -> {
  10. (stock.get() < 0)の場合{
  11. System.err.println( "在庫が不足しています。直接返品してください" );
  12. 戻る;
  13. }
  14. 試す {
  15. ブール値 acquire = mutex.acquire(200, TimeUnit.MILLISECONDS);
  16. (取得)の場合{
  17. int s = stock.decrementAndGet();
  18. (s < 0)の場合{
  19. System.err.println( "フラッシュセール開始、在庫不足" );
  20. }それ以外{
  21. システム。 out .println( "購入が成功しました。残りの在庫: " + s);
  22. }
  23. }
  24. } キャッチ (例外 e) {
  25. e.printStackTrace();
  26. ついに
  27. 試す {
  28. (mutex.isAcquiredInThisProcess())の場合
  29. ミューテックスを解放します。
  30. } キャッチ (例外 e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. });
  35. }
  36. )の間{
  37. 実行者が終了した場合
  38. 実行者.シャットダウン();
  39. システム。 out .println( "フラッシュセール終了後、残りの在庫は次のとおりです: " + stock.get());
  40. }
  41. TimeUnit.MILLISECONDS.sleep(100);
  42. }
  43. }
  44. プライベート静的CuratorFramework getZkClient() {
  45. 文字列 zkServerAddress = "127.0.0.1:2181" ;
  46. ExponentialBackoffRetry 再試行ポリシー = 新しい ExponentialBackoffRetry(1000, 3, 5000);
  47. CuratorFramework zkClient = CuratorFrameworkFactory.builder()
  48. .connectString(zkServerアドレス)
  49. .セッションタイムアウトMs(5000)
  50. .接続タイムアウトMs(5000)
  51. .retryPolicy(再試行ポリシー)
  52. 。建てる();
  53. zkClient を起動します。
  54. zkClientを返します
  55. }
  56. }

読み取り/書き込みロックアプリケーション

読み取り/書き込みロックは、複数のスレッドによる読み取り時にロックフリーであり、書き込みロックが先行している場合にのみ、書き込みロックが完了するまで待機してからデータにアクセスするため、読み取り/書き込みロックを使用すると、キャッシュの二重書き込みの強力な一貫性を確保できます。

  1. パブリッククラスReadWriteLockTest {
  2. 静的ExecutorService executor = Executors.newFixedThreadPool(8);
  3. 静的AtomicIntegerストック = 新しいAtomicInteger(3);
  4. 静的InterProcessMutex readLock;
  5. 静的InterProcessMutex 書き込みロック;
  6. 公共 静的void main(String[] args)はInterruptedExceptionをスローします{
  7. CuratorFrameworkクライアント = getZkClient();
  8. 文字列キー= "/lock/lockId_111/1111" ;
  9. InterProcessReadWriteLock readWriteLock = 新しい InterProcessReadWriteLock(クライアント、キー);
  10. readLock は readWriteLock.readLock() です。
  11. writeLock = readWriteLock.writeLock();
  12. ( int i = 0; i < 16; i++) {
  13. 実行者.送信(() -> {
  14. 試す {
  15. ブール値read = readLock.acquire(2000, TimeUnit.MILLISECONDS);
  16. if (読み取り) {
  17. int num = stock.get();
  18. システム。 out .println( "在庫を読み取ります。現在の在庫は: " + num);
  19. (数値<0)の場合{
  20. System.err.println( "在庫が不足しています。直接返品してください" );
  21. 戻る;
  22. }
  23. }
  24. } キャッチ (例外 e) {
  25. e.printStackTrace();
  26. }ついに {
  27. readLock.isAcquiredInThisProcess() の場合 {
  28. 試す {
  29. 読み取りロックを解除します。
  30. } キャッチ (例外 e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }
  35. 試す {
  36. ブール値 acquire = writeLock.acquire(2000, TimeUnit.MILLISECONDS);
  37. (取得)の場合{
  38. int s = stock.get();
  39. (s <= 0)の場合{
  40. System.err.println( "フラッシュセール開始、在庫不足" );
  41. }それ以外{
  42. s = stock.decrementAndGet();
  43. システム。 out .println( "購入が成功しました。残りの在庫: " + s);
  44. }
  45. }
  46. } キャッチ (例外 e) {
  47. e.printStackTrace();
  48. ついに
  49. 試す {
  50. (writeLock.isAcquiredInThisProcess()) の場合
  51. ロックを解除します。
  52. } キャッチ (例外 e) {
  53. e.printStackTrace();
  54. }
  55. }
  56. });
  57. }
  58. )の間{
  59. 実行者が終了した場合
  60. 実行者.シャットダウン();
  61. システム。 out .println( "フラッシュセール終了後、残りの在庫は次のとおりです: " + stock.get());
  62. }
  63. TimeUnit.MILLISECONDS.sleep(100);
  64. }
  65. }
  66. プライベート静的CuratorFramework getZkClient() {
  67. 文字列 zkServerAddress = "127.0.0.1:2181" ;
  68. ExponentialBackoffRetry 再試行ポリシー = 新しい ExponentialBackoffRetry(1000, 3, 5000);
  69. CuratorFramework zkClient = CuratorFrameworkFactory.builder()
  70. .connectString(zkServerアドレス)
  71. .セッションタイムアウトMs(5000)
  72. .接続タイムアウトMs(5000)
  73. .retryPolicy(再試行ポリシー)
  74. 。建てる();
  75. zkClient を起動します。
  76. zkClientを返します
  77. }
  78. }

印刷結果は以下のとおりです。最初は、在庫の読み取りに対して 8 つの出力結果があります。現在の在庫は次のとおりです: 3. 次に、書き込みロックに戻り、在庫を順番に減算します。

  1. 在庫を読み取り、現在の在庫: 3
  2. 在庫を読み取り、現在の在庫: 3
  3. 在庫を読み取り、現在の在庫: 3
  4. 在庫を読み取り、現在の在庫: 3
  5. 在庫を読み取り、現在の在庫: 3
  6. 在庫を読み取り、現在の在庫: 3
  7. 在庫を読み取り、現在の在庫: 3
  8. 在庫を読み取り、現在の在庫: 3
  9. 購入完了、残り在庫数: 2
  10. 購入完了、残り在庫数: 1
  11. 購入完了、在庫残り: 0
  12. フラッシュセールに参加、在庫不足
  13. フラッシュセールに参加、在庫不足
  14. フラッシュセールに参加、在庫不足
  15. フラッシュセールに参加、在庫不足
  16. フラッシュセールに参加、在庫不足
  17. 在庫を読み取り、現在の在庫: 0
  18. 在庫を読み取り、現在の在庫: 0
  19. 在庫を読み取り、現在の在庫: 0
  20. 在庫を読み取り、現在の在庫: 0
  21. 在庫を読み取り、現在の在庫: 0
  22. 在庫を読み取り、現在の在庫: 0
  23. 在庫を読み取り、現在の在庫: 0
  24. 在庫を読み取り、現在の在庫: 0
  25. フラッシュセールに参加、在庫不足
  26. フラッシュセールに参加、在庫不足
  27. フラッシュセールに参加、在庫不足
  28. フラッシュセールに参加、在庫不足
  29. フラッシュセールに参加、在庫不足
  30. フラッシュセールに参加、在庫不足
  31. フラッシュセールに参加、在庫不足
  32. フラッシュセールに参加、在庫不足

分散ロック選択

最も一般的に使用されるのは、Redis 分散ロックと Zookeeper 分散ロックです。パフォーマンスの面では、Redis の 1 秒あたりの TPS は簡単に数万に達します。大規模で同時実行性の高いシナリオでは、推奨される技術的ソリューションとして Redis 分散ロックを使用することをお勧めします。同時実行要件が特に高くない場合は、Zookeeper 分散処理を使用できます。

参考文献

https://www.cnblogs.com/leeego-123/p/12162220.html

http://curator.apache.org/

https://blog.csdn.net/hosaos/article/details/89521537

<<:  クラウドストレージを再びシンプルにする2つのヒント

>>:  Alibaba Cloud Li Ke: Alibaba Cloud エッジクラウドコンピューティング技術と実践

推薦する

ウェブサイトへのトラフィックを導入する現在の主流の2つの方法について議論する

ウェブサイトのトラフィックは、ウェブサイトが存続するための基礎です。トラフィックがなければ、ウェブサ...

リベートネットワークの推進方法に関する実践的議論

Fanli.com は、急成長を遂げるオンライン ショッピング市場で急速に成長している新興の電子商取...

ロングテールキーワードは役に立たない?この記事を読んでから理解するまでに少し時間がかかったかもしれません。

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています顧客の印象...

2022年のクラウドコンピューティング業界の5つの主要な発展トレンド

過去18か月間、COVID-19パンデミックの発生は世界の経済と社会に大きな変化をもたらし、クラウド...

ダブル11のeコマースプロモーションは純粋な成長であり、オフラインを犠牲にすることはありません

早くから盛り上がっていた「ダブルイレブン」は、11日早朝、ついに最後の「必殺技」を開始した。開始後、...

マイクロソフト、2013 年に Bing 検索ホームページで最も美しい写真トップ 10 を発表

マイクロソフトの検索エンジン Bing のホームページでは毎日興味深い写真が提供されており、その多く...

エッジコンピューティングと産業用インターネットにおけるその応用についての簡単な説明

1. 背景Internet of Everything時代の到来により、ビッグデータ、人工知能、ブロ...

ウェブサイトの診断と分析方法についての簡単な説明

みなさんこんにちは。私は湖南省出身のキネスです。今日は、主にウェブサイト戦略、ウェブサイトディレクト...

マルチクラウドインフラストラクチャでは制御が重要

[[208125]]開発者や DevOps マネージャーにパブリック クラウドの経験について尋ねると...

ローカルウェブサイトの存続とプロモーションスキルについての簡単な議論

中国のネットユーザー数の増加とインターネットの急速な発展に伴い、わが国のインターネットは新たな春の時...

オンラインねずみ講帝国の真実:メンバーは多くの地域でその概念を広め、サイバートロールを雇った

【要点】全国30省の人員を巻き込み、680万人以上の会員を育成し、最大38億元の預金を集めた…江西ワ...

Googleの検索品質は、顧客よりも広告に重点を置いているため低下している。

テンセントテクノロジーニュース(劉学同)北京時間9月6日のニュース、海外メディアの報道によると、テク...

Juqi.comの失敗は適者生存の兆候、あるいは共同購入業界の終焉の兆しである

数年前、共同購入が初めて登場したとき、それは非常に人気があり、共同購入ウェブサイトはわずか数か月で、...

データベースマーケティングの力

現実世界とネット世界は現代社会の基本的な形態です。インターネットの急速な発展に伴い、ネットユーザーの...

「中国の声」におけるプロダクト プレイスメントの分析と JDB の成功の秘密

我が国の文化産業の発展に伴い、文化産業の重要な一分野であるテレビ番組も繁栄し始めました。その一つの現...