Redis 分散ロック

Redis 分散ロック

[[405785]]

分散ロックは「スレッド同期」の延長です

最近、「分散ロック」が初めて適用されました。考えてみてください。分散ロックは孤立したスキルポイントではなく、実際にはホスト間のスレッド同期です。

進行中クロスプロセスクロスホスト
ロック/モニター、SemaphoreSlim メトゥクス、セマフォ分散ロック
ユーザーモードのスレッドセーフティカーネルスレッドの安全性

スタンドアロン サーバーは、特定のメモリ ヒープを共有することで、ロック/ロック解除をマークできます。スレッドの同期は、最終的にはスタンドアロン オペレーティング システムのユーザー状態/カーネル状態における共有メモリのアクセス制御に基づいています。

分散サーバーは同じマシン上にはなく、複数のホストにまたがっているため、ロック タグはすべてのマシン プロセスが参照できる場所に保存する必要があります。

ロックは、在庫管理や宝くじなど、多くのビジネス シナリオの開発に使用されます。

たとえば、在庫が 1 つしか残っておらず、3 人のユーザーが同時に購入する予定の場合、最初に購入したユーザーの在庫はすぐにクリアされ、他の 2 人のユーザーは購入できなくなります。

分散ロックの理解

この分散ロックを含め、私たちがよく話題にするスレッドセーフティとスレッド同期のソリューションはすべて、

「複数のスレッド/プロセスが同時に特定のリソースを更新しています。」

基本的な考慮事項

1. 分散システムでは、ロックは一度に 1 つのサーバーによってのみ取得できます (これが分散ロックの基礎です)

2. デッドロックを防止するロック解除機構を装備(特定の事故を防止するため、ロックが解除されず、他の人がロックを取得できない)

Redis SET リソース名 任意の文字列 NX EX 最大ロック時間

これは最もシンプルな分散ロック実装ソリューションです。

SET コマンドはいくつかのパラメータをサポートしています:

  • EX 秒 - 有効期限を設定します (秒)
  • NX -- キーが存在しない場合は設定します...

SET コマンド パラメータは SETNX、SETEX、および GETSET を置き換えることができるため、これらのコマンドは将来的に廃止される可能性があります。

上記のコマンドが OK を返す場合 (または再試行される場合)、クライアントはロックを取得します。

ロックを解除するには、DEL コマンドを使用します。タイムアウト期間に達すると、ロックは自動的に解除されます。

ロックを解除するときに、システムをより堅牢にするためにいくつかのデザインを追加します。

3. ロック タグ値として固定の文字列値を使用せず、業界ではトークンと呼ばれる、推測しにくいランダムな値を使用します。

4. ロックを解除するためにDELコマンドを使用せず、キーを削除するスクリプトを送信します。

ポイント 3 と 4 は、「ロックの期限が早く切れ、クライアント A が実行を完了していないため、クライアント B がロックを取得します。この時点で、クライアント A は実行を完了しています。ロックが削除されると、クライアント B のロックも削除されますか?」という問題を解決することを目的としています。 - 4 は 3 つのテクノロジの推奨実装です。

スクリプトは次のとおりです。

  1. redis.call( "get" ,KEYS1] ==ARGV[1])の場合
  2. それから 
  3. redis.call( "DEL" ,KEYS[1])を返す
  4. それ以外 
  5. 0を返す
  6. 終わり 

以下は、StackExchange.Redis を使用した上記の考慮事項に基づいたコード例です。

  1. /// <要約>
  2. /// ロックを取得します。
  3. /// </要約>
  4. /// <param name = "キー" ></param>
  5. /// <param name = "token" >ランダム値</param>
  6. /// <param name = "expireSecond" ></param>
  7. /// <param name = "waitLockSeconds" >非ブロッキング ロック</param>
  8. static bool Lock(文字列キー、文字列トークン、 int expireSecond=10、 double waitLockSeconds = 0)
  9. {
  10. var 待機間隔 = 50;
  11. ブール値はロックです。
  12.              
  13. DateTimeの開始= DateTime.Now;
  14. する
  15. {
  16. isLock = Connection .GetDatabase().StringSet(キー、トークン、TimeSpan.FromSeconds(expireSecond)、 When .NotExists);
  17. if (ロック)
  18. 戻る 真実;
  19. //ロックを待たずに戻る
  20. waitLockSeconds == 0 の場合、中断します。
  21. //待機時間が経過すると、それ以上の待機は行われません
  22. if ((DateTime.Now - begin ).TotalSeconds >= waitLockSeconds) break;
  23. スレッドスリープ(待機間隔Ms);
  24. } while (!isLock);
  25. 戻る 間違い;
  26. }
  27.         
  28. /// <要約>
  29. /// ロックを解除します。
  30. /// </要約>
  31. /// < ロックが解除された場合は><c> true </c>を返し、それ以外の場合は <c> false </c> を返します。</を返します>
  32. /// <param name = "key" >キー。</param>
  33. /// <param name = "value" >値</param>
  34. static bool UnLock(文字列キー、文字列値)
  35. {
  36. 文字列 lua_script = @"
  37. (redis.call( 'GET' , KEYS[1]) == ARGV[1])場合   
  38. redis.call( 'DEL' , キー[1])
  39. 戻る 真実   
  40. それ以外   
  41. 戻る 間違い   
  42. 終わり   
  43. ";
  44. 試す
  45. {
  46. var res =接続.GetDatabase().ScriptEvaluate(lua_script,
  47. 新しいRedisKey[] {キー},
  48. 新しいRedisValue[] { 値 });
  49. (bool)resを返します
  50. }
  51. catch (例外例)
  52. {
  53. Console.WriteLine($ "ReleaseLock ロック失敗...{ex.Message}" );
  54. 戻る 間違い;
  55. }
  56. }
  57.          
  58. プライベート静的Lazy<ConnectionMultiplexer> lazyConnection = 新しい Lazy<ConnectionMultiplexer>(() =>
  59. {
  60. ConfigurationOptions 構成 = 新しい ConfigurationOptions
  61. {
  62. 接続失敗時に中止 = false
  63. 接続タイムアウト = 5000、
  64. };
  65. configuration.EndPoints.Add ( "10.100.219.9" 6379);
  66. ConnectionMultiplexer.Connect ( configuration.ToString () )を返します
  67. });
  68. 公共 静的ConnectionMultiplexer Connection => lazyConnection.Value;

上記のコードでは、5 番目の考慮事項が追加されます。

5. 無制限のロック取得を避けるために、非ブロッキングロックが追加されます。poll_sはロックを待機し、待機されない場合はロックを取得しなくなります。

方向:

3 つのタスクを並行して開始し、同時に在庫を削減してみましょう。

  1. 静的void Main(文字列[] 引数)
  2. {
  3. // 3つのタスクを並列に実行してみる
  4. 平行。 (0, 3, x =>の場合
  5. {
  6. 文字列トークン = $ "loki:{x}" ;
  7. bool isLocked = Lock( "loki" , トークン, 5, 10);
  8.              
  9. (ロックされている場合)
  10. {
  11. Console.WriteLine($ "{token} は {DateTime.Now} に株式の削減 (ロック付き) を開始します。" );
  12. スレッドスリープ(1000);
  13. Console.WriteLine($ "{token}は {DateTime.Now} にロック {UnLock(" loki ", token)} を解除します。" );
  14. }
  15. それ以外 
  16. {
  17. Console.WriteLine($ "{token} は {DateTime.Now} に在庫の削減を開始します。" );
  18. }
  19. });
  20. }

3つの並列タスクが順番にロックを取得/解放しているのがわかります。

出力の概要

この記事では、スレッド セーフティとスレッド同期の基礎から始め、分散ロックがホスト間のリソース スレッド/プロセス同期ソリューションであることを認識します。また、RedisSET コマンドを分散ロックとして使用する場合の設計上の考慮事項についても段階的に説明します。記憶力が良いことは文章力が悪いことほど良くありません。

<<:  IDC:中国の産業用クラウド市場規模は2020年後半に23億ドルに達する見込み

>>:  分散トランザクション - 信頼性の高いメッセージ最終一貫性ソリューション

推薦する

oplink-1g メモリ/20g SSD/xen/onapp/2ip/100m 無制限/月額 9.95 ドル

oplink は、ドメイン名が 2001 年に登録され、会社が 2006 年に登録されました (テキ...

Aruba ESP: 3つのコアバリュー、エッジコンピューティングの追加

[51CTO.com からのオリジナル記事] 低コストと高効率という利点により、クラウド コンピュー...

SEO テクニックを学んだら何ができるでしょうか?

私の一般的な答えは、電子商取引を行うこと、または SEO 関連サービスを提供することです。前者は S...

マイクロソフト、クラウド移行のための企業向けビジネス管理ツールを提供するAIMプログラムを開始

Microsoft は最近、顧客がローカル ERP および CRM アプリケーションをクラウドに移行...

ウェブサイトの機能を理解するためのツール「WalkMe」が1100万ドルを調達

WalkMe は、ユーザーが Web サイトの使用方法を理解するのに役立つツールです。同社は本日、S...

労働力を解放する、初心者向けVPS・サーバー管理パネル - 国内版

初心者でもベテランでも、複雑なサーバーに直面したときは、管理パネルさえあれば、コマンドを 1 行ずつ...

自社SEO技術の評価

時々、過去数年間に学んだことを振り返ります。ざっくり考えてみると、SEO以外には何も見つからない、ほ...

アップル、中国へのソースコード提出に反応

海外メディアの報道によると、アップルは製品のソースコードとキーを中国市場に提出し、監視用の「バックド...

ChinaHR.com の崩壊: 運営上の失敗がモデルのジレンマを招いた

【捜狐ITニュース】ChinaHR.comはまだ売却されていないが、すでに200人の従業員が解雇を要...

高品質な外部リンクを作成するには?高品質な外部リンクとはどのようなものでしょうか?

百度の公式ウェブマスタープラットフォームでLeeが外部リンクの不正行為と無効な外部リンクについての記...

ウェブサイトの内部と外部の問題を解決することで、最適化は無敵になります

私が開設して1年以上経った石家荘証券ネットワークは、すでに運営されています。この1年間で、ウェブサイ...

ブランドプロモーションと SEO: どちらが優れているのでしょうか?

「あなたの靴はとても素敵ですね。ナイキですか、それともアディダスですか?どこで買ったのですか?iPh...

kvmla: 日本のソフトバンク VPS、10M 帯域幅、Windows サポートの簡単なレビュー

kvmlaは国内の古いブランド(2011年~)です。最近、日本のソフトバンク回線でVPSを取得しまし...

インフェルノ: 5 ドル/KVM/512m メモリ/1Gbps 帯域幅/無制限トラフィック | 9 つのデータセンター

今日は、新しい VPS プロバイダー、inferno (inferno Solutions) を紹介...