1.1redis
データ型、および各データ型の使用シナリオ
1.1.1String
実際、これについては何も言うことはありません。最も一般的なset/get
操作value
はString
数値です。通常、複雑なカウント関数のキャッシュを実行します。
1.1.2hash
構造化オブジェクトはここvalue
に格納され、フィールドの1つを操作する方が便利です。ブロガーがシングルサインオンを行う場合、ブロガーはこのデータ構造を使用して、キャッシュの有効期限として30分を設定cookieId
する方法としてユーザー情報を保存します。これにより、同様の効果をうまくシミュレートできます。key
session
1.1.3list
データ構造を使用List
すると、簡単なメッセージキュー機能を実行できます。もう1つは、このlrange
コマンドを使用しredis
て、に基づいてページング機能を実行できることです。優れたパフォーマンスと優れたユーザーエクスペリエンスを備えています。
1.1.4set
set
スタックは、繰り返されない値のコレクションの集まりであるためです。したがって、グローバル重複排除の機能を実行できます。重複排除JVM
に独自のものを使用してみませんか?Set
私たちのシステムは一般的にクラスターで展開されているためJVM
、組み込みのシステムを使用するのはSet
面倒ですが、グローバルな重複排除を行うために公共サービスを開始するのは面倒ですか?
さらに、共通部分、和集合、差などの演算を使用して、共通の好み、すべての好み、および独自の好みを計算できます。
1.1.5sorted set
sorted set
追加の重みパラメーターを使用score
すると、コレクション内の要素をscore
順番に配置できます。ランキングアプリをして、TOP N
操作してください。
1.2redis
有効期限ポリシーとメモリ除去メカニズム
分析:この質問は実際には非常に重要です。redis
自宅で使用されているかどうかは、この質問からわかります。たとえば、redis
保存できるのは5G
データだけですが、書き込むと10G
データが削除され5G
ます。どのように削除しましたか、これについて考えましたか?また、データには有効期限が設定されていますが、有効期限が切れてもメモリ使用量は比較的多いので、その理由を考えてみましたか?
回答:Redisは、通常の削除+遅延削除戦略を採用しています。
ポリシーを定期的に削除してみませんか?
回答:定期的に削除され、タイマーを使用して監視key
され、有効期限が切れると自動的に削除されます。メモリは時間内に解放されますが、非常に多くCPU
のリソースを消費します。大規模な同時リクエストではCPU
、削除key
ではなくリクエストの処理に時間を使用する必要があるため、この戦略は採用されていません。
定期的な削除+遅延削除はどのように機能しますか?
定期的に削除します。redis
デフォルト100ms
では、有効期限が切れているかどうかを確認し、有効期限が切れている場合key
はkey
削除します。それらすべてを一度チェックするredis
代わりに、それらはチェックのためにランダムに選択されることに注意する必要があります(それらが1回おきにチェックされる場合、それらはスタックしません)。したがって、定期的な削除戦略のみを採用すると、削除せずに多くの時間が発生します。したがって、遅延削除が役立ちます。つまり、一定の時間を取得したら確認しますが、有効期限が設定されている場合は有効期限が切れていますか?有効期限が切れると削除されます。100ms
key
100ms
key
redis
key
key
redis
key
通常の削除+遅延削除に他の問題はありますか?
いいえ、通常の削除が削除されていない場合key
。次に、すぐkey
にリクエストしないでください。つまり、遅延削除は有効になりません。このようにして、redisのメモリはどんどん高くなります。次に、メモリ除去メカニズムを使用する必要があります。
構成にredis.conf
は、メモリー除去戦略で構成された行があります。
# maxmemory-policy allkeys-lru
1)noeviction
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、新しい書き込み操作はエラーを報告します。使用しないでください。
2)allkeys-lru
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、キースペースで、最も使用頻度の低いを削除しkey
ます。推奨される使用法。
3)allkeys-random
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、キースペースで1つがランダムに削除されkey
ます。誰もそれを使用するべきではありません。最も使用されていないものを削除するのではなく、Key
ランダムに削除するだけです。
4)volatile-lru
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、有効期限が設定されたキースペースで、最も最近使用されていないものを削除しkey
ます。この状況は通常redis
、キャッシュと永続ストレージの両方として使用されます。推奨されません
5)volatile-random
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、有効期限が設定されたキースペースでキーがランダムに削除されます。まだお勧めできません
6)volatile-ttl
:メモリが新しく書き込まれたデータを収容するのに十分でない場合、有効期限が設定されたキースペースで、有効期限が早いキーが最初に削除されます。推奨
されないps:expire
設定されkey
、前提条件(prerequisites
);が満たされていません;そしてvolatile-lru
、ポリシーvolatile-random
の動作、および(削除されていない)は基本的に同じです。volatile-ttl
noeviction
1.3redis
およびデータベースの二重書き込み整合性の問題
分析:一貫性は分散分散における一般的な問題であり、結果整合性と強一貫性にさらに分けることができます。データベースとキャッシュが二重に書き込まれると、必然的に不整合が発生します。この質問に答えるには、まず前提を理解する必要があります。つまり、データに強い整合性要件がある場合、キャッシュを配置することはできません。私たちが行うことはすべて、結果整合性を保証することしかできません。また、私たちが作成した計画は、実際には基本的に言えば、矛盾の可能性を減らすとしか言えず、完全に回避することはできません。したがって、強整合性要件のあるデータはキャッシュできません。
1.3.1データの一貫性redis
に関する問題mysql
ここでは、3つの更新戦略について説明します。
最初にキャッシュを更新し、次にデータベースを更新します
最初にデータベースを更新し、次にキャッシュを更新します最初にキャッシュを削除
し、次にデータベースを更新します
最初にデータベースを更新し、次にキャッシュを削除します
1つ目は、最初にキャッシュを更新してから、データベースを
更新することです。問題:キャッシュの更新は成功しますが、データベースの更新が失敗し、データの不整合が発生します。
2つ目は、最初にデータベースを更新してから、キャッシュを更新すること
です。問題:
1。データベース
2を更新し、Bがデータベース
3を更新し、Bがキャッシュ
4に書き込み、Aがキャッシュに書き込み
データの不整合が発生します。
別の状況を考えてみましょう。次の2つのポイントがあります。
(1)データベースシナリオを作成するためのビジネス要件が多く、データ読み取りシナリオが少ない場合、このソリューションを使用すると、データがまったく読み取られていないという事実につながります。キャッシュ頻繁に更新され、パフォーマンスが低下します。
(2)値をデータベースに書き込む場合、値はキャッシュに直接書き込まれませんが、一連の複雑な計算の後にキャッシュに書き込まれます。次に、データベースに書き込むたびに、キャッシュに書き込まれた値が再度計算されますが、これは間違いなくパフォーマンスの無駄です。明らかに、キャッシュを削除する方が適切です。
3番目の方法は、最初にキャッシュを削除してからデータベースを更新すること
です。問題:
1。Aがキャッシュを削除し
ます。2。Bがデータベースにクエリを実行して古い値を取得します
。3。Bがキャッシュを更新します。4。Aが
データベースを更新します。
データの不整合の問題
二重削除の遅延
public void write(String key,Object data){
redis.delKey(key); // 删除 redis 缓存数据。
db.updateData(data); // 更新数据库数据。
Thread.sleep(1000); // 当前逻辑延时执行。
redis.delKey(key); // 删除 redis 缓存数据。
}
問題1:遅延二重削除は次のように進化しました:最初にデータベースを更新し、次にキャッシュを削除します。
次に例を示し
ます。1。Aはキャッシュを削除し
ます。2。Bはデータベースにクエリを実行して古い値を取得します
。3。Bはキャッシュを更新します。4。Aはデータベースを
更新します
。手順4〜5:最初にデータベースを更新してから、キャッシュを削除します。
したがって、遅延二重削除は次のように進化しました。最初にデータベースを更新し、次にキャッシュを削除しても、問題はまだ解決されていません。
なんで?この時点で、ステップ4が実行される前に、別のクエリCが来て、Cが古い値をクエリするとします。ステップ6:Cは古い値をキャッシュに挿入します。この時点で、キャッシュとデータベースの不整合が発生します。
遅延を解決することはできません。Cをキャッシュに挿入する操作は、Cがネットワークの問題、GCの問題などに遭遇した場合など、ステップ5の後に実行されます。もちろん、これは小さな確率ですが、それが存在しないという意味ではありません。
もちろん、遅延が長ければ長いほど、この問題は回避可能になります。ビジネス要件がそれほど厳しくない場合は、無視できます。
質問2:データベースが更新された後、キャッシュから取得された値が次のクエリのデータベースと同じであることを保証することはできません。
第4に、最初にデータベースを更新してから、キャッシュを削除し
ます。問題:上記のCのクエリは、すでに問題を説明しています。
データの不整合の可能性は比較的小さいです。このオプションを選択するかどうかは、ビジネスニーズによって異なります。
5番目の究極のソリューション
要求のシリアル化
真に信頼できるソリューション:アクセス操作のシリアル化
1.最初にキャッシュを削除し、データベースを更新する操作を順序付けられたキューに入れます
。2.キャッシュから見つからない操作をクエリします。順序付けられたキュー
解決する必要のある問題:
1。読み取り要求のバックログと多数のタイムアウトにより、データベースに圧力がかかります:現在の制限、ヒューズ
2.要求の大量のバックログを回避する方法:キューを水平に分割して並列処理を改善します。
キャッシュが更新されずに削除されるのはなぜですか?
更新の場合、分散トランザクションの問題があり、キャッシュが変更される可能性があり、データベースの変更は失敗します。キャッシュを削除するだけで、データベースの変更が失敗した場合でも、次のクエリはデータベースのデータを直接フェッチし、ダーティデータはありません。
回答:まず、正しい更新戦略を採用し、最初にデータベースを更新してから、キャッシュを削除します。第二に、キャッシュの削除に失敗する可能性があるため、メッセージキューを使用するなどの補償手段を提供するだけで十分です。
1.4キャッシュの侵入とキャッシュのなだれに対処する方法
分析:これらの2つの問題は、正直なところ、中小の従来のソフトウェア会社がこの問題に遭遇することは困難です。大規模な並行プロジェクトがある場合、トラフィックは約数百万になります。これらの2つの問題は真剣に検討する必要があります。
回答:以下に示すように
キャッシュの侵入:つまり、ハッカーは意図的にキャッシュに存在しないデータを要求し、すべての要求をデータベースに送信して、異常なデータベース接続を引き起こします。
解決策:
1。相互排他ロックを使用します。キャッシュに障害が発生した場合は、最初にロックを取得し、次にロックを取得してから、データベースを要求します。ロックが取得されていない場合は、しばらくスリープしてから再試行して
ください。2.非同期更新戦略を採用しkey
、値が取得されているかどうかに関係なく直接戻ります。value
キャッシュの有効期限は値に保持されます。キャッシュの有効期限が切れると、スレッドが非同期的に開始され、データベースを読み取ってキャッシュを更新します。キャッシュのウォームアップ(プロジェクトを開始する前に、最初にキャッシュをロードする)操作を実行する必要があります。
3.リクエストが有効かどうかをすばやく判断できる傍受メカニズムを提供します。たとえば、ブルームフィルターを使用して、一連の合法で有効なものを内部的に維持しますkey
。Key
実行されたリクエストが合法で有効かどうかをすばやく判断します。無効な場合は直接返送します。
キャッシュアバランシェ、つまりキャッシュが広い範囲で同時に失敗する場合、別のリクエストの波が発生し、すべてのリクエストがデータベースに送信され、データベース接続が異常になります。
解決策:
1。一括無効化を回避するために、キャッシュ無効化時間にランダムな値を追加します。
2.相互排他ロックが使用されますが、このスキームのスループットは大幅に低下します。
3、ダブルキャッシュ。キャッシュAとキャッシュBの2つのキャッシュがあります。キャッシュAの無効化時間は20分であり、キャッシュBの無効化時間は設定されていません。キャッシュのウォーミングアップ操作は自分で行ってください。次に、次の点を細分化します
- キャッシュAからデータベースを読み取り、存在する場合は直接返します
- Aにはデータがなく、Bから直接データを読み取り、直接戻り、非同期で更新スレッドを開始します。
- 更新スレッドは、キャッシュAとキャッシュBの両方を同時に更新します。
1.5redis
同時競合のkey
問題を解決する方法
分析:問題は、大まかに言って、同時にset
1つに複数のサブシステムがあることkey
です。現時点で何に注意を払うべきですか?あなたはそれについて考えましたか?ブロガーのBaiduBaiduが事前に説明し、その答えが基本的に推奨さredis
れるトランザクションメカニズムであることを確認する必要があります。ブロガーが推奨しないredis
トランザクションメカニズム。本番環境は基本的にredis
クラスター環境であるため、データシャーディング操作を行っています。トランザクションに複数のkey
操作が含まれている場合、これらの複数の操作はkey
必ずしも同じものに格納されるとは限りませんredis-server
。したがって、redis
トランザクションメカニズムは非常に無味です。
回答:以下に示すように
1.key
この操作で注文が不要な
場合、この場合は分散ロックを用意し、全員がロックを取得し、ロックを取得してからset
操作を行います。これは比較的簡単です。
2.key
この操作に順序が必要な場合
1つあるとするとkey1
、システムをに設定するA
必要があり、システムをに設定する必要があり、システムをに設定する必要があります。期待値はの順序で変化します。このとき、データベースにデータを書き込むときに、タイムスタンプを保存する必要があります。タイムスタンプが次のようになっているとしますkey1
valueA
B
key1
valueB
C
key1
valueC
key1
value
valueA-->valueB-->valueC
システムA key 1 {valueA 3:00}
システムB key 1 {valueB 3:05}
システム次に、これが最初にロックを取得するC key 1 {valueC 3:10}
システムであると仮定すると、に設定されます。次に、システムはロックを取得し、自身のタイムスタンプがキャッシュ内のタイムスタンプよりも前であることを検出したため、何も実行しません。等々。B
key1
{valueB 3:05}
A
valueA
set
キューの使用などの他のアプローチでも、set
メソッドがシリアル化されます。要するに、柔軟であること。
1.6redis
パブリッシュおよびサブスクライブ
Redis
パブリッシュ/サブスクライブ(pub/sub)
はメッセージ通信パターンです。送信者(pub)
はメッセージを送信し、サブスクライバー(sub)
はメッセージを受信します。
Redis
クライアントは、任意の数のチャネルにサブスクライブできます。
次の図はchannel1
、チャネルとチャネルにサブスクライブしている3つのクライアントの関係を示しています- client2
、およびclient5
:コマンドを介して新しいメッセージがチャネルに送信されると、メッセージはチャネルにサブスクライブしている3つのクライアントに送信されます。client1
PUBLISH
channel1
redis 127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
ここで、最初にredis
クライアントを再起動してから、同じチャネルでメッセージを2回redisChat
公開すると、サブスクライバーはメッセージを受信できます。
redis 127.0.0.1:6379> PUBLISH redisChat "hello"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "What 's your name?"
(integer) 1
# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "hello"
1) "message"
2) "redisChat"
3) "What 's your name?"