さまざまな分散ロックの欠点を比較し、Redis分散ロックを実装するための鍵をつかむ

さまざまな分散ロックの欠点を比較し、Redis分散ロックを実装するための鍵をつかむ

過去 2 年間で、マイクロサービスはますます普及し、分散環境に導入されるアプリケーションも増えています。分散環境では、データの一貫性は常に注意を払って解決する必要がある問題です。分散ロックは広く使用される技術になりました。一般的に使用されている分散実装方法は Redis と Zookeeper であり、その中でも Redis に基づく分散ロックが最も広く使用されています。

しかし、私は職場やインターネット上で、Redis 分散ロック実装のさまざまなバージョンを見てきました。それぞれの実装には不正確な点があり、コードも含めて誤った実装である可能性もあります。分散ロックが正しく使用されていない場合、本番環境に重大な障害が発生する可能性があります。この記事では主に、現在遭遇するさまざまな分散ロックとその欠陥をまとめ、適切な Redis 分散ロックを選択する方法についての提案を示します。

[[258219]]

さまざまなバージョンのRedis分散ロック

  • バージョン1.0
  1. ロックを試みる(){
  2. SETNXキー1
  3. 期限切れキー秒数
  4. }
  5. リリース(){
  6. 消去  
  7. }

このバージョンは最もシンプルなバージョンであり、最も頻繁に使用されるバージョンでもあります。まず、ロックに有効期限を追加するのは、アプリケーションの再起動後や例外発生後にロックを解除できない状況を回避するためです。

このソリューションの問題点は、Redis リクエストが送信されるたびに、最初のコマンドの実行後にアプリケーションが失敗したり再起動したりすると、ロックが期限切れにならないことです。改善点の 1 つは、Lua スクリプト (SETNX コマンドと EXPIRE コマンドを含む) を使用することです。ただし、Redis がクラッシュしたり、コマンドを 1 つだけ実行した後にマスターとスレーブの切り替えが発生したりすると、ロックは期限切れにならず、最終的には解放できなくなります。

もう 1 つの問題は、ロックが正常に取得されたかどうかに関係なく、多くの学生が分散ロックを解放するプロセス中に最終的にロックを解放することです。これはロックの誤った使用法です。この問題は、後続のV3.0バージョンで解決される予定です。

ロックが解除されない問題の解決策は、GETSET コマンドに基づいています。

  • V1.1 GETSETベース
  1. ロックを試みる(){
  2. 新しい有効期限 = 現在のタイムスタンプ + 有効期限の秒数
  3. if(SETNXキーNewExpireTime 秒){
  4. oldExpireTime = GET(キー)
  5. if(古い有効期限 < 現在のタイムスタンプ){
  6. 新しい有効期限 = 現在のタイムスタンプ + 有効期限の秒数
  7. CurrentExpireTime=GETSET(キー,NewExpireTime)
  8. 現在の有効期限 == 古い有効期限の場合{
  9. 1 を返します
  10. }それ以外{
  11. 0を返します
  12. }
  13. }
  14. }
  15. }
  16. リリース(){
  17. 消去  
  18. }

アイデア:

  1. SETNX(Key,ExpireTime) はロックを取得します
  2. ロックの取得に失敗した場合は、GET(Key)によって返されたタイムスタンプを使用してロックが期限切れになっているかどうかを確認します。
  3. GETSET(Key,ExpireTime)は値をNewExpireTimeに変更します
  4. GETSET によって返された古い値を確認します。 GET によって返された値と等しい場合、ロックは正常に取得されたとみなされます。

注: このバージョンでは EXPIRE コマンドが削除され、代わりに Value タイムスタンプ値を使用して有効期限を決定します。

質問:

1. ロックの競合が高い場合、値は継続的に上書きされますが、どのクライアントもロックを取得できません。

2. ロック取得プロセス中に元のロック データを継続的に変更します。 C1 と C2 がロックを競い合うシナリオを想像してください。 C1 はロックを取得し、C2 は GETSET 操作を実行して C1 のロック有効期限を変更します。 C1 がロックを正しく解放しない場合、ロックの有効期限が延長され、他のクライアントはより長く待機する必要があります。

  • V2.0 SETNXベース
  1. ロックを試みる(){
  2. SETNXキー1秒
  3. }
  4. リリース(){
  5. 消去  
  6. }

Redis バージョン 2.6.12 以降では、SETNX に有効期限パラメータが追加され、2 つのコマンドがアトミック性を保証できない問題が解決されました。しかし、次のようなシナリオを想像してみてください。

1. C1 はロックを正常に取得しますが、その後 C1 は GC を待機するか、不明な理由によりタスクの実行に時間がかかりすぎます。 ***C1 は、ロックの有効期限が切れる前にロックを積極的に解除しません。

2. C1 のロックがタイムアウトした後、C2 がロックを取得し、実行を開始します。このとき、C1 と C2 は両方とも同時に実行されており、繰り返し実行することでデータの不整合やその他の不明な状況が発生する可能性があります。 3. C1 が最初に実行されると、C2 のロックが解放され、別のプロセス C3 がロックを取得する可能性があります。

大まかなフローチャート

問題点:

1. C1 の一時停止により、C1 と C2 の両方がロックを取得して同時に実行するため、間接的にビジネス実装で冪等性を保証する必要があります。

2. C1はC1に属さないロックを解除する

  • バージョン3.0
  1. ロックを試みる(){
  2. SETNXキーUnixTimestamp 秒
  3. }
  4. リリース(){
  5. 評価(
  6. //Luaスクリプト
  7. redis.call( "get" ,KEYS[1]) == ARGV[1]の場合 
  8. redis.call( "del" ,KEYS[1])を返す
  9. それ以外 
  10. 0を返す
  11. 終わり 
  12. }

このソリューションは、値をタイムスタンプとして指定し、ロックを解除するときにロックの値がロックの値と同じかどうかを確認することで、C1 が C2 によって保持されているロックを解除するというバージョン V2.0 で言及された問題を回避します。さらに、ロックの解放には複数の Redis 操作が含まれ、Check And Set モデルの同時実行性の問題を考慮すると、同時実行性の問題を回避するために Lua スクリプトが使用されます。

問題点:

赤い封筒を掴むなど、同時実行性が極めて高いシナリオでは、Unix タイムスタンプの重複問題が発生する可能性があります。さらに、分散環境では物理クロックの一貫性が保証されないため、Unix タイムスタンプの重複問題も発生する可能性がありますが、これは非常にまれなケースでのみ発生します。

  • V3.1
  1. ロックを試みる(){
  2. セット キーUniqId秒
  3. }
  4. リリース(){
  5. 評価(
  6. //Luaスクリプト
  7. redis.call( "get" ,KEYS[1]) == ARGV[1]の場合 
  8. redis.call( "del" ,KEYS[1])を返す
  9. それ以外 
  10. 0を返す
  11. 終わり 
  12. }

Redis 2.6.12 以降では、SET は SETNX コマンドと同等の NX パラメータも提供します。公式ドキュメントでは、以降のバージョンでは SETNX、SETEX、および PSETEX が削除され、代わりに SET コマンドが使用される可能性があることを通知しています。もう 1 つの最適化は、タイムスタンプの代わりに自己増分する一意の UniqId を使用して、V3.0 で説明されているクロックの問題を回避することです。

このソリューションは現在、最良の分散ロック ソリューションですが、Redis クラスター環境にまだ問題がある場合は、次のようになります。

Redis クラスターのデータ同期は非同期であるため、ロックを取得した後、データ同期が完了する前にマスターノードがクラッシュした場合でも、新しいマスターノードはロックを取得できるため、複数のクライアントが同時にロックを取得できます。

分散 Redis ロック: Redlock

バージョン V3.1 は単一インスタンスのシナリオでのみ安全です。海外の分散専門家は、分散 Redis ロックの実装方法について白熱した議論を交わしてきました。 Antirez は分散ロック アルゴリズム Redlock を提案しました。 Redlock の詳細な説明は、distlock トピックで参照できます。以下はRedlockアルゴリズムの中国語の説明です(引用)

N個の独立したRedisノードがあると仮定する

  1. 現在の時刻をミリ秒単位で取得します。
  2. N 個の Redis ノードに対してロック取得操作を順番に実行します。この取得操作は、単一の Redis ノードに基づいてロックを取得する前のプロセスと同じです。ランダム文字列 my_random_value と有効期限 (たとえば、ロックの有効期間である PX 30000) が含まれます。 Redis ノードが利用できない場合でもアルゴリズムが継続して実行されるようにするため、ロック取得操作にはタイムアウトが設定されています。このタイムアウトは、ロックの有効時間よりもはるかに短くなっています (数十ミリ秒程度)。クライアントが Redis ノードからのロックの取得に失敗した場合は、すぐに次の Redis ノードを試行する必要があります。ここでの障害には、Redis ノードが利用できない、Redis ノードのロックがすでに他のクライアントによって保持されているなど、あらゆるタイプの障害が含まれます (注: 元の Redlock テキストでは、Redis ノードが利用できない状況のみが記載されていますが、他の障害状況も含まれる必要があります)。
  3. 手順 1 で記録した時間を現在の時刻から減算して、ロック取得プロセス全体にかかった合計時間を計算します。クライアントが Redis ノードの大多数 (>= N/2+1) からロックを正常に取得し、ロックの取得に費やされた合計時間がロックの有効時間を超えない場合、クライアントはロックが正常に取得されたと見なします。それ以外の場合は、ロックが失敗したとみなされます。
  4. 最終的にロックが正常に取得された場合、ロックの有効時間を再計算する必要があります。これは、ロックの初期有効時間から、手順 3 で計算されたロックの取得に費やされた時間を差し引いた値に等しくなります。
  5. 最終的にロックの取得に失敗した場合 (ロックを取得した Redis ノードの数が N/2+1 未満であるか、ロック取得プロセス全体がロックの初期有効期間よりも長くかかるため)、クライアントはすべての Redis ノードに対してロック解放操作を直ちに開始する必要があります (つまり、前に紹介した Redis Lua スクリプト)。
  6. ロックを解除: すべてのRedisノードでロック解除操作を開始します。

しかし、Martin Kleppmann はこのアルゴリズムに疑問を呈し、フェンシング トークン メカニズム (リソースに対するすべての操作にはトークンの検証が必要) に基づくべきだと提案しました。

1. Redlock は、システム モデル、特に分散クロックの一貫性の問題について仮定を立てます。実際のシナリオでは、クロックの不整合やクロックジャンプの問題があり、Redlock はタイミングに基づいた分散ロックです。

2. さらに、Redlock は自動有効期限メカニズムに基づいているため、長期間の GC 一時停止などの問題によるロックの自動失敗によって発生するセキュリティの問題は依然として解決されておらず、セキュリティ上の問題を引き起こします。

その後、antirez は Martin Kleppmann 氏の質問に答え、有効期限メカニズムの合理性と、一時停止が発生して複数のクライアントが同時にリソースにアクセスする実際のシナリオでの状況への対処方法について説明しました。

要約する

SETNXバージョンに基づくRedis単一インスタンス分散ロックであっても、Redlock分散ロックであっても、以下の機能を保証する必要があります。

1. セキュリティ: 複数のクライアントが同時にロックを保持することは許可されません

2. 活動

デッドロック: クライアントがクラッシュしたり、ネットワーク分割が発生したりしても、ロックは最終的に解除される必要があります (通常はタイムアウト メカニズムに基づきます)

フォールト トレランス: Redis ノードの半分以上が利用可能である限り、ロックを正しく取得および解放できます。

したがって、分散ロックを開発または使用する場合、予期しない結果を避けるためにセキュリティとアクティビティを確保する必要があります。

さらに、分散ロックの各バージョンにはいくつかの問題があります。ロックを使用する場合は、ロックの実際のシナリオに応じて適切なロックを選択する必要があります。通常、ロックの使用シナリオは次のとおりです。

効率性: 操作を完了するには 1 つのクライアントのみが必要であり、繰り返し実行する必要はありません。これは緩い分散ロックであり、ロックのアクティビティのみが保証される必要があります。

正確性: 複数のクライアントは厳密な相互排他性を確保する必要があり、ロックを保持したり、同時に同じリソースを操作したりすることはできません。このシナリオでは、ロックの選択と使用をより厳密にし、ビジネス コードをべき等にする必要があります。

Redis 分散ロックの実装には、解決すべき問題がまだ多く残っています。これらの問題を認識し、Redis 分散ロックを正しく実装する方法を理解した上で、作業時に分散ロックを合理的に選択して正しく使用する必要があります。

<<:  プライベートクラウドプラットフォームは絶滅の危機から脱しつつある

>>:  MWC 2019: アリババクラウド、全方位的なエンタープライズインテリジェントテクノロジーを提供する7つの主要製品をリリース

推薦する

昇る太陽か、衰える太陽か?We-mediaは今後の発展の道について考えたことがあるだろうか?

国内の自主メディアが新星であろうと、すでに衰退期に入っているかにかかわらず、自主メディア連盟リストは...

Seoerさん、まだブロググループ投稿を続けていますか?

国内ではブログプラットフォームはSEO外部リンクの重要なチャネルであり、海外でもさらに強力です。最近...

華龍郷の銭宇:地域コミュニティ運営者の物語

彼は常州で地元の暴君としての地位を固めるために5年を費やし、現在は第2、第3のインターネット市場の影...

上位にランクインするために、コアキーワードを中心にウェブサイトを構築しましょう

SEO はますます競争が激しくなっています。業界の競争が比較的少なく、コンテンツと外部リンクに頼るだ...

外部リンクやプロモーションを行う際には時間的要素に注意する必要があります

なぜ外部リンクとプロモーションを一緒に議論する必要があるのでしょうか? こう尋ねる友人もいるかもしれ...

エッジコンピューティングを推進する 4 つのデータ要件

エッジ コンピューティングは、データ処理をネットワークのエッジに移動することでクラウド コンピューテ...

地元の小さなタレント事務所を運営する方法

今日のウェブマスターはますます専門化しており、つまり、さまざまなサイトを作成することにそれほど無謀で...

ECサイト構築時に注意すべきこと

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

WordPress ホスティングの推奨事項: WordPress が公式に推奨する専用ホスティング

WordPress を使用してブログを構築している多くの友人は、「どの WordPress ホストが...

SEO 人生の浄土はあなたの考え方次第

SEO はどれほど神話的でしょうか? 分かりません。神話的という言葉は気軽に使えるものではありません...

IDC レビュー: 2012 年 12 月の全国 IDC ブランド ランキング

中国IDCレビューネットワークは1月4日に次のように報じた。IDCレビューネットワークの最新データに...

ウェブサイト運営の核となる価値

友達からよくメッセージを受け取りますが、ウェブサイトを構築するにはどうすればいいですか?今では、イン...

今日の話題: Alibaba のオンライン バンキングは登場するのか?伝統的な銀行にとって挑戦となるのでしょうか?

中国ビジネスニュース(ブログ、微博)の9月9日の報道によると、アリファイナンスは最近、金融規制当局に...

VMware が IT チームの負担を軽減し、従業員のエクスペリエンスを向上させる新しい Anywhere Workspace 機能を発表

今日のハイブリッドな作業環境において、IT チームはさまざまな新たな課題に取り組んでいます。従業員は...

アリババ:注目を集めるSEO

実のところ、この記事は 2013 年の終わりには書くべきだったのですが、今日まで延期されました。もち...