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億ドルに達する見込み

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

推薦する

namecheap-比較的懐かしい価格のXen仮想VPS、高品質のVPS

Namecheap は有名な海外ドメイン名商人です。上記のタイトルは単なるクリックベイトです。商人に...

Yuan Fangさん、SEO会社を選ぶことについてどう思いますか?

ネットワーク技術に精通していない、または不慣れな企業にとって、強力な SEO 会社を選択することは非...

ソフト論文投稿における盗作被害を軽減する方法

宝くじサイトに記事を投稿していたとき、このことを深く理解しました。多くのサイトでは宝くじをセンシティ...

クラウド コンピューティング、SaaS、そして製造業の新たな常識

過去 20 ~ 30 年にわたり、クラウド コンピューティングとサービスとしてのソフトウェア (Sa...

bgpto: 日本のソフトバンクサーバー、月額422元、E3-1230v3/16Gメモリ/480gSSD/20M専用/3IPv4

bgp.to は現在、日本の東京データセンターにソフトバンク回線独立型サーバーを展開しています。国内...

MoonVM: 香港ダイナミック VPS 補充、香港ダイナミック IP (HKT/HGC/WTT ライン)、300M/500M/1G 帯域幅、無制限トラフィック

moonvm は、香港 HKT、香港 HGC、香港 WTT の 3 つのオペレータと連携して、香港ダ...

とてもクール! Kafka は Zookeeper を削除しました。

[[396651]]この記事はWeChat公式アカウント「妹の味」から転載したもので、著者は妹が飼っ...

1ヶ月以内にキーワードをホームページに載せる体験談

実際、私は長い間SEOと連絡を取りました(登録時間2009-3-5)。オンラインで1か月もかかりませ...

量子コンピュータの二重の省エネの可能性

量子コンピュータは、従来のコンピュータよりも少ないエネルギーで、より効率的な技術を開発する方法を提供...

Amazon Web Services の機械学習が北京のダリッジ カレッジの授業に導入

北京ダリッジ・カレッジは、Amazon DeepRacer 自動運転レースカー、Amazon Ale...

毎月 50% 以上の継続的な成長により、ブランド マーケティング プロモーションを 0 から 1 にするにはどうすればよいでしょうか?

ブランド創造のプロセスは、主に発見、認識、記憶、好意、忘れられない、共有の 6 つの段階に分けられま...

タオバオ店主の生活環境が注目を集め、新たなトップ10の高リスク職業にリストアップ

最近、ネットユーザーが投票した「中国の新・トップ10高リスク職業」ランキングで、タオバオのショップオ...

オンラインマーケター必携:Google Chrome の 5 つのトップ拡張機能

起業家および事業主の皆様へ:私はマーケティングの反逆者、Pu Jiang です。ウェブサイトの SE...

キーワードを深く掘り下げるキーワード分析

キーワードはウェブサイトのトラフィックの重要な要素であり、ウェブサイトの露出を拡大する決定的な要素で...

B2Bウェブサイトを成功に導くための重要なポイントについて簡単に説明します。

B2Bウェブサイトも、この百度革命の影響を深刻に受けました。症状は異なりますが、一般的にウェブサイト...