Redis 分散ロックの進化の解釈 + 欠陥分析

Redis 分散ロックの進化の解釈 + 欠陥分析

Redis 分散ロックの進化

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

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

[[255985]]

さまざまなバージョンの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 はロックを正常に取得しますが、その後、GC のため、または不明な理由によりタスクの実行に時間がかかりすぎるため、C1 は待機状態になります。 ***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.  
  13. }

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ノードがあると仮定する

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

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

  1. Redlock は、システム モデル、特に分散クロックの一貫性の問題について仮定を立てます。実際のシナリオでは、クロックの不整合やクロック ジャンプの問題が発生します。 Redlock はタイミングに基づいた分散ロックです。
  2. さらに、Redlock は自動有効期限メカニズムに基づいているため、長期間の GC 一時停止などの問題によるロックの自動失敗によって発生するセキュリティの問題は依然として解決されておらず、セキュリティ上の問題を引き起こします。その後、antirez は Martin Kleppmann 氏の質問に答え、有効期限メカニズムの合理性と、一時停止が発生して複数のクライアントが同時にリソースにアクセスする実際のシナリオでの状況への対処方法について説明しました。

Redlock の問題への対応として、「Redis に基づく分散ロックは安全か?」という記事が公開されました。詳細な中国語の説明を提供し、Redlock アルゴリズムに存在する問題を分析します。

要約する

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

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

アクティブ デッドロック: クライアントがクラッシュしたり、ネットワーク パーティションが発生したりした場合でも (通常はタイムアウト メカニズムに基づいて)、最終的にはロックが解除される必要があります。フォールト トレランス: Redis ノードの半分以上が利用可能である限り、ロックを正しく取得および解放できます。したがって、分散ロックを開発または使用する場合、予期しない結果を避けるために安全性とアクティビティを確保する必要があります。

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

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

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

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

<<:  クラウド コンピューティングではなぜ運用と保守が重要なのでしょうか?

>>:  VLANとVXLANテクノロジーの詳細な説明

推薦する

SEO 欲張りすぎると行き詰まってしまいます

深センの天気は相変わらず晴れ。珍しい週末なのでゆっくり休めると思っていたのですが、電話がかかってきて...

インターネットの観点から人材ネットワークのブランディングを定義する方法

ウェブサイトを構築したことがある人なら誰でも、ウェブサイトの開発におけるブランディングの重要性を知っ...

コミュニケーションとチャネルの5つの側面から知乎と鑑書の違いを分析します。

知乎と建書は全く違うようです。最も明らかな違いは、知乎はエリートでいっぱいですが、建書は草の根の執筆...

ブラウザベースのページデザインツールであるEaselは、製品のプロトタイプページを素早く作成します。

インターネットに不慣れな起業家の多くは、頭の中に多くの創造的なアイデアを持っているかもしれませんが、...

徐志新:企業向けサービスで20年の経験を持つFutong CloudとTeng Cloud Management Servicesは、より説得力があります

[51CTO.comからのオリジナル記事] 伝統的なソリューションディストリビューターからデジタルト...

今日最もホットでターゲットを絞ったマーケティング: 検索エンジンマーケティング SEM/SEO

ネットユーザーがインターネットに参入する主要な入り口として、検索エンジンが企業のプロモーションにとっ...

全体的な状況に基づいてウェブサイト広告モデルを選択する方法

多くの SEO 担当者は、ウェブサイトを構築するときに独自の目的を持っています。おそらく、これらの目...

2020年の優秀事例が発表され、天一クラウドがリストに載る

1月20日、2020年度クラウド管理・クラウドネットワーク優秀事例選定結果が正式に発表されました。今...

GO言語のパフォーマンス問題の発見と解決

事件の原因この事件は、社内の同僚が社内メーリンググループに質問を投稿したことから始まりました。 go...

ご自宅にいながら展覧会をご覧いただけます! Huawei Cloudが世界VR産業会議クラウドサミットの成功に貢献

オフライン展示会の固定観念を打ち破り、移動の手間を省き、疫病下での安全リスクを回避する、新しいオンラ...

sharktech-24g メモリ/4t ハードディスク/32ip/無制限ギガビット ポート/無料 cpanel または directadmin/無料 windows/月額 199 ドル

Sharktech は、ホスト キャットが評判を上げるたびに「米国で最も耐性のあるサーバー サプライ...

中国最大のリンクプラットフォーム「アリウェイ」が数日間連続DDos攻撃を受けた

中国最大のリンク交換プラットフォーム「AliVV」の公式サイトが数日連続で開けない状態が続いており、...

Pinterest の B2B マーケティング戦略の簡単な分析

インターネットの急速な発展に伴い、ビッグデータの時代が静かに到来しました。しかし、膨大なデータ情報は...

UCloud 人工知能と Intel の背後にある技術的なストーリーを分析する (パート 1)

「企業が独自のAIオンラインサービスシステムを構築するのは簡単ではありません。ITインフラストラクチ...

インターネットのビッグデータ時代は本当に到来したのでしょうか?それとも、ずっとそこにあったのでしょうか?

(文/ヘブン)最近、WeChatでインターネットビッグデータに関する記事をよく見かけます。ビッグデー...