Redis を使用して高い同時実行性を解決する方法
NoSQL
Not Only SQLの略。 NoSQL は、従来の RDBMS が特定の問題に対処できないことを解決するために提案されました。
つまり、非リレーショナル データベースです。リレーショナル データの ACID 特性は保証されません。通常、データ間に相関関係はありません。拡張の点で実装が非常に簡単で、パフォーマンスが高いです。
Redis
Redis は nosql の代表的な存在であり、現在のインターネット企業にとって必須の技術でもあります。
Redis は主にハッシュ テーブルを使用して、キーと値のペアのストレージを実装します。ほとんどの場合、リクエストがディスクに直接アクセスしないようにキャッシュの形で直接使用されるため、効率が非常に良く、中小企業のニーズを十分に満たすことができます。
#一般的なデータ型
- 文字列
- ハッシュ ハッシュ
- ##sets セット
#Ordered set sort set
# 文字列とそれぞれの操作コマンドは追加、削除、変更、確認だけですが、具体的なコマンドについては後ほど整理します。
問題点
Web アプリケーションで多くのリクエストが同時に発生すると、データの読み取りと保存でエラーが発生する可能性があります。つまり、ダーティ リードやダーティ データ生成が発生します。
分散プロジェクトでは、さらに多くの問題が発生します。
感想
同時実行性に関して言えば、本質的には、複数のリクエストが同時に受信され、正しく処理できないということです。
すべてのリクエストをキューに入れて、ビジネス ロジックを実行するためにリクエストを 1 つずつ受け取ることができます。現時点では、メッセージ キューを使用することが実現可能な解決策です。同時実行性の高いメッセージ キューの対処方法については、次回まとめます。
もう 1 つの方法は、並列処理を直列化に直接変換することです。Java は同期を提供します。それは同期ですが、これは、厳しい効率要件がある場所や分散プロジェクトにはまだ適切なソリューションではないため、同時実行の問題を解決するために Redis を使用して分散ロックを実装することになります。
分散ロック
分散プロジェクトでは、ロックとロック解除を表すために、一意で汎用的かつ効率的な識別子が使用されます。
Redis は実装が非常に簡単です。つまり、キーが存在するかどうかによって、キーがロックされているかロック解除されているかがわかります。
文字列型を例として挙げます:
Integer stock = goodsMapper.getStock(); if (stock > 0) { stock =- 1; goodsMapper.updateStock(stock); }
上記は、インスタント キルのための最も単純な疑似コードです。分散ロックを実装するために Redis を使用してみます。
// 这里是错误代码,只是一个思考过程,请耐心看完哦 String key = "REDIS_DISTRIBUTION_LOCKER"; // 分布式锁名称 String value = jedisUtils.get(key); if (value != null) { // 未上锁 // wingzingliu jedisUtils.set(key, 1); // 上锁 Integer stock = goodsMapper.getStock(); if (stock > 0) { stock =- 1; goodsMapper.updateStock(stock); jedisUtils.del(key); // 释放锁 } }
上記のコードには問題がある可能性があります。つまり、複数のリクエストが同時に受信され、特定の時点で複数のリクエストがすべて空の値を取得した場合、スレッド A は if および// wingzingliu に移動します。ロックすると、他のリクエストも入ってくるため、ダーティなデータが表示されます。
ここでのコードの問題は、原子性の問題が考慮されていないことです。
そこで、redis の setNx コマンドを使用する必要があります。本質は値を設定することですが、これはアトミックな操作です。実行後、設定が成功したかどうかが返されます。
redis> SETNX job "programmer" # job 设置成功 (integer) 1 redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败 (integer) 0 redis> GET job # 没有被覆盖 "programmer"
値がある場合に注目すると、失敗して 0 が返されます。したがって、コードは次のように変換されます。
// 这里是错误代码,只是一个思考过程,请耐心看完哦 String key = "REDIS_DISTRIBUTION_LOCKER"; // 分布式锁名称 Long result = jedisUtils.setNx(key, 1); if (result > 0) { // 上锁成功,进入逻辑 // wingzingliu1 Integer stock = goodsMapper.getStock(); if (stock > 0) { stock =- 1; goodsMapper.updateStock(stock); System.out.println("购买成功!"); } else { System.out.println("没有库存了!"); } // wingzingliu2 jedisUtils.del(key); // 释放锁 }
上記により、アトミック性を確保し、順番に正しく処理することができます。
しかし、別の隠れた問題があります。つまり、スレッドがロックを正常に実行した後、プログラムは wingzingliu1 と wingzingliu2 の間に例外をスローします。その後、プログラムは終了し、ロックは解放できません。他のスレッドは解放できません。入ることすらできない。
解決策は、try catchfinallyブロックを追加し、finallyのロックを解放することです。
しかし、ダウンしている場合はどうなるでしょうか?ロックがロックされた後、マシンはクラッシュします。finally の内容は依然として実行されません。ロックは解放されません。手動で処理しないと、今後すべてのスレッドがアクセスできなくなります。
そこで、redis の有効期限が導入され、一定の時間になると自動的にロックが解除されるようになります。
// 这里是不够完善的代码,请耐心看完哦 try { String key = "REDIS_DISTRIBUTION_LOCKER"; // 分布式锁名称 Long result = jedisUtils.setNx(key, 1, 30); // 假设处理逻辑需要20s左右,设置了30秒自动过期 if (result > 0) { // 上锁成功,进入逻辑 Integer stock = goodsMapper.getStock(); if (stock > 0) { stock =- 1; goodsMapper.updateStock(stock); System.out.println("购买成功!"); } else { System.out.println("没有库存了!"); } } } catch (Exception e) { } finally { jedisUtils.del(key); // 释放锁 }
上記は比較的完全な分散ロックですが、まだ小さな欠陥があります。特定のリクエスト A の処理が非常に遅いと仮定します。20 秒かかると予想されますが、35 秒かかります。 30 秒に達すると、ロックの有効期限が切れます。他のリクエストも自然に発生しました。
これにより、同時実行が発生するだけでなく、リクエスト A が処理された後もロック解放操作が実行され続けるため、実際にロックが次のスレッドに渡されます。類推すると、同時実行制御全体が台無しになります。
理論的には、キーの有効期限をより長く設定できますが、それは最善の解決策ではありません。ここで、ライフをロックするという概念が登場します。
ロック寿命延長
名前が示すように、ロック寿命を延長します。この実装では、ロックの有効期限が近づくとロック時間を延長します。 30 秒のロックが使用され、ロックがまだ存在するかどうかを 10 秒ごとにチェックすると仮定します。ロックがまだ存在する場合は、30 秒間ロックを維持します。これにより、上記の考えられる問題が回避されます。
ここではスケジュールされたタスクが使用されており、定期的に呼び出すことができます。
Extension
キーに設定した値は 1 です。実際、リクエスト ID を使用して保存できるため、どのリクエストからのロックであるかを知ることができ、他のスレッドのロックはロック解除されます。フロントエンドによって渡されるか、特定のルールに基づいてサーバーによって生成されます。
以上がRedis を使用して高い同時実行性を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Redisクラスターモードは、シャードを介してRedisインスタンスを複数のサーバーに展開し、スケーラビリティと可用性を向上させます。構造の手順は次のとおりです。異なるポートで奇妙なRedisインスタンスを作成します。 3つのセンチネルインスタンスを作成し、Redisインスタンスを監視し、フェールオーバーを監視します。 Sentinel構成ファイルを構成し、Redisインスタンス情報とフェールオーバー設定の監視を追加します。 Redisインスタンス構成ファイルを構成し、クラスターモードを有効にし、クラスター情報ファイルパスを指定します。各Redisインスタンスの情報を含むnodes.confファイルを作成します。クラスターを起動し、CREATEコマンドを実行してクラスターを作成し、レプリカの数を指定します。クラスターにログインしてクラスター情報コマンドを実行して、クラスターステータスを確認します。作る

Redis指令を使用するには、次の手順が必要です。Redisクライアントを開きます。コマンド(動詞キー値)を入力します。必要なパラメーターを提供します(指示ごとに異なります)。 Enterを押してコマンドを実行します。 Redisは、操作の結果を示す応答を返します(通常はOKまたは-ERR)。

Redisデータをクリアする方法:Flushallコマンドを使用して、すべての重要な値をクリアします。 FlushDBコマンドを使用して、現在選択されているデータベースのキー値をクリアします。 [選択]を使用してデータベースを切り替え、FlushDBを使用して複数のデータベースをクリアします。 DELコマンドを使用して、特定のキーを削除します。 Redis-CLIツールを使用してデータをクリアします。

Redisは、単一のスレッドアーキテクチャを使用して、高性能、シンプルさ、一貫性を提供します。 I/Oマルチプレックス、イベントループ、ノンブロッキングI/O、共有メモリを使用して同時性を向上させますが、並行性の制限、単一の障害、および書き込み集約型のワークロードには適していません。

Redisソースコードを理解する最良の方法は、段階的に進むことです。Redisの基本に精通してください。開始点として特定のモジュールまたは機能を選択します。モジュールまたは機能のエントリポイントから始めて、行ごとにコードを表示します。関数コールチェーンを介してコードを表示します。 Redisが使用する基礎となるデータ構造に精通してください。 Redisが使用するアルゴリズムを特定します。

Redisのすべてのキーを表示するには、3つの方法があります。キーコマンドを使用して、指定されたパターンに一致するすべてのキーを返します。スキャンコマンドを使用してキーを繰り返し、キーのセットを返します。情報コマンドを使用して、キーの総数を取得します。

Redisはハッシュテーブルを使用してデータを保存し、文字列、リスト、ハッシュテーブル、コレクション、注文コレクションなどのデータ構造をサポートします。 Redisは、スナップショット(RDB)を介してデータを維持し、書き込み専用(AOF)メカニズムを追加します。 Redisは、マスタースレーブレプリケーションを使用して、データの可用性を向上させます。 Redisは、シングルスレッドイベントループを使用して接続とコマンドを処理して、データの原子性と一貫性を確保します。 Redisは、キーの有効期限を設定し、怠zyな削除メカニズムを使用して有効期限キーを削除します。

Redisのキューを読むには、キュー名を取得し、LPOPコマンドを使用して要素を読み、空のキューを処理する必要があります。特定の手順は次のとおりです。キュー名を取得します:「キュー:キュー」などの「キュー:」のプレフィックスで名前を付けます。 LPOPコマンドを使用します。キューのヘッドから要素を排出し、LPOP Queue:My-Queueなどの値を返します。空のキューの処理:キューが空の場合、LPOPはnilを返し、要素を読む前にキューが存在するかどうかを確認できます。
