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

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

[[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 エッジクラウドコンピューティング技術と実践

推薦する

「無料旅行」からインターネットマーケティングを考える

先週末、友人と私はキッチンキャビネットブランドが主催する無料ツアーに参加しました。旅程は主に、安徽省...

ウェブマスターはどのようにして、排除法によって良質で安価な SEO サービス プロバイダーを選択できるのでしょうか?

SEO サービス プロバイダーを選択することは、ウェブマスターにとって徐々に一般的なことになってきて...

製品プロモーションの2つの中核要素:コンテンツとチャネル

商品のプロモーションは難しい問題です。見た目はシンプルで簡単に実行できますが、結果は大きく異なります...

#ルーマニア VPS# rootsensor-$6/KVM/1g メモリ/20g ハードディスク/1Gbps/無制限トラフィック

新しいマーチャントである rootsensor.com は、主にルーマニアのデータセンターで、仮想ホ...

Zhiboba のドメイン名が盗まれました。これは、すべてのウェブマスターにドメイン名を保護するよう警告するものです。

4月1日、いつものように生放送バーにログインして、ゲームの具体的な時間と生放送メディアを確認してくだ...

SEO 3 年目のベテランが教える初心者のための最適化のヒント

私はフルタイムのSEO最適化担当者で、3年以上SEO最適化をフルタイムで行っています。学ぶことが大好...

エネルギー分野における IoT エッジ コンピューティングの課題と機会

IoT デバイスの増加により、企業がデジタル化を進める中で処理できる、また処理しなければならないデー...

中順益はNetEase Cloudの専用クラウドを使用して、シナリオベースの金融テクノロジーサービスを積極的に推進しています。

最近、インターネット金融会社が大量に株式を公開し、市場全体が少し熱くなっています。こうした状況を受け...

より一般的なパブリッククラウドやハイブリッドクラウドに比べて、プライベートクラウドの利点は何ですか?

プライベート クラウドには、ハイブリッド クラウドを構築したり、パブリック クラウドのみに依存したり...

オンラインショッピング、グループ購入、ショッピングガイドから始まるクローズドビジネスループの価値と課題

多くの人が「クローズドループは誤った命題だ」と話すとき、多くの本当の業界関係者は「ははは」と反応する...

5G、エッジコンピューティング、IoTが従来の企業を近代化する方法

過去 2 年間、世界的なパンデミックとロックダウンによりデジタル化が促進され、数え切れないほどの従来...

南京ビッグデータ管理局のZhai Shengqiang氏率いる代表団が華雲データグループを視察

3月25日、南京市ビッグデータ管理局の翟盛強副局長と曹海斌副局長は、無錫市ビッグデータ管理局の関係幹...

zji専用サーバーセール:香港アリババクラウド - 480元、香港連邦 750元、日本大阪 - 550元

zji の 7 月の専用サーバープロモーションのオファー: (1) 香港 Alibaba Cloud...

ブランドに対する 5 つの質問: コミュニケーションにおける認知上の誤解をどのように修正できるでしょうか?

19 世紀以降、影響力の拡散媒体の変化とともに、ブランド理論は徐々に一般に知られるようになりました。...