目次
マーチャントクエリキャッシュを実装するために Redis を使用する理由は何ですか?
- キャッシュは、データ交換のためのバッファーであり、頻繁に使用されるデータを保管するための一時領域であり、高い読み取りおよび書き込み性能が要求されますが、メモリ読み取りおよび書き込みをベースとした Redis の高いパフォーマンス特性は、まさにこの要件を満たしています。
- Redis を使用してキャッシュを実装すると、バックエンドの負荷が軽減され、読み取りと書き込みの効率が向上します。
- キャッシュ機能を増やすと、データ整合性コスト、コード保守コスト、運用保守コストなどのコストが増加するため、ビジネスシナリオに応じて適切なredisの機能や仕組みを選択する必要があります。
Redis を使用して販売者のクエリ キャッシュを実装するという基本的な考え方は?
- キャッシュがない場合、クライアントがデータをクエリするリクエストを頻繁に送信すると、各リクエストはデータベースに直接到達するため、データベースに大きな負荷がかかります。
- Redis キャッシュは、クライアントとデータベースの間にインターセプトを追加することに相当します。クライアントがデータをクエリするリクエストを送信すると、最初に Redis クエリに到達します。ヒットした場合は、クエリ結果が直接返されるため、リクエストは送信されません。データベースに到達し、データベースの負荷を軽減します。
- Redis のリクエストがヒットしない場合、リクエストはデータベースに継続され、データベース クエリの結果がクライアントに返されます。
- クライアントが Redis に存在しないこのデータを繰り返しリクエストしても、データベースを複数回リクエストすることになります。これは私たちが望む結果ではありません。そのため、データベースはクエリ結果をクライアントに返し、クエリ結果を Redis に書き込む必要があります。キャッシング
Redis キャッシュ使用時の問題と解決策?
1. データベース データと Redis キャッシュ データの一貫性を維持するにはどうすればよいですか?
- データの一貫性要件は低く、データ変更の頻度は低く、メモリ削除メカニズムで十分です。
- データの一貫性の要件は高く、データ変更の頻度が高く、アクティブな更新が使用され、タイムアウトの排除が保証として使用されます。
1 メモリ消去メカニズム
- メモリ不足の問題を解決するために、redis にはメモリ不足の場合に一部のデータを自動的に削除する機能が組み込まれており、次回データのクエリ時にキャッシュが更新され、データの一貫性を一定に保つことができます。範囲。
- 利点: メンテナンスコストがほぼ 0、Redis がプロセス全体を自体で管理
- 短所: どのデータがいつ削除されるかわからないため、データの一貫性は比較的劣ります。
2 タイムアウト解消機構
- キャッシュされたデータに TTL 時間を追加し、有効期限が切れると自動的にキャッシュを削除し、次回クエリを実行するときにキャッシュを更新します。
- 利点: メンテナンスコストが低く、元のデータに基づいて有効期限を追加するだけで済みます。
- 欠点: どのデータをいつ削除するかを制御できます。データの一貫性はメモリ削除メカニズムよりも優れていますが、強力でもありません。
3 アクティブな更新メカニズム (win)
- データベース内のデータを変更しながらキャッシュを更新する
- 利点: データの一貫性が向上します。
- 短所: メンテナンスコストが高い、多くのビジネスロジックを自分で記述する必要があり、これが難しい
アクティブな更新メカニズムを実装するにはどうすればよいですか?
- 最初に実装されたソリューション: 独自のコードを記述し、データベースを更新しながらキャッシュを更新します (実際のビジネスで最も一般的に使用されます)。
- 2 番目の解決策は、キャッシュとデータベースをサービスに統合することです。サービスを呼び出すだけで済み、データの一貫性はサービス自体によって内部的に実装されます。しかし、サービスを維持するのは難しく、既製のサービスを見つけるのは困難です。
- 3 番目の実装計画: 呼び出し元はデータベースを気にせず、キャッシュの追加、削除、変更、チェックのみを行い、キャッシュは常に最新のデータを維持します。専用スレッドは、キャッシュされたデータをデータベースに非同期的に更新して、データの最終的な一貫性を保証します。非同期メカニズムにより効率が大幅に向上します。主なシナリオは、キャッシュ内の特定のデータが n 回更新された場合、非同期スレッドはキャッシュ内のデータが変更されたかどうかを確認し、最新のデータをデータベースに書き込みます。は、データベースは 1 回だけ更新され、データベースの読み取りと書き込みはキャッシュの読み取りと書き込みよりも手間がかかるため、データベースを n-1 回更新するために無駄になる時間が節約されます。欠点は、非同期スレッドの維持コスト、Redis がダウンしたときのデータの損失、非同期スレッドがデータベースを更新する前にデータが完全に不整合になることです。
キャッシュとデータベースを操作する場合、キャッシュを更新するか、キャッシュを削除する必要がありますか?
- キャッシュの更新: データベースが更新されるたびにキャッシュを更新する必要があり、キャッシュに対する無効な書き込み操作が多数あります。
- キャッシュの削除: データベース更新時にキャッシュ内のデータを削除します。n 回の更新後に 1 回だけ削除されます。クライアントがデータを使用するときのみ、redis キャッシュにアクセスします。redis にそのようなデータが存在しない場合は、最新のデータが取得されますデータベースから取得され、キャッシュに書き込まれます。
キャッシュとデータベース操作のアトミック性を確保するにはどうすればよいでしょうか?
- つまり、データベースを更新するときは、キャッシュの削除も正常に実行されることを確認する必要があり、両方とも成功するか、両方とも失敗します。
- モノリシック システムの場合: キャッシュとデータベースの操作を 1 つのトランザクションにまとめます。
- 分散システムの場合: TCC などの分散トランザクション ソリューションを利用します。
キャッシュを削除してからデータベースを操作した方が良いでしょうか?
- マルチスレッドのシナリオでは、2 つの実行順序が異なると、異なる結果が生じます。
- 最初にキャッシュを削除します: スレッド 1 が最初にキャッシュを削除し、次にデータベースを更新すると仮定します。スレッド 1 がキャッシュを削除したばかりでデータベースを更新していないときに、スレッド 2 が突然要求します。この時点で、キャッシュ内のデータは削除されています、スレッド 2 はデータベースにクエリを実行しますが、スレッド 1 はデータベースを更新していないため、スレッド 2 は古いデータを見つけて、プロセスに従って古いデータをキャッシュに書き込みます。このとき、スレッド 1 はデータベースを再度更新します。データの不整合の原因となります。
- 最初にデータベースを更新します: スレッド 1 が最初にデータベースを更新し、次にキャッシュを削除すると仮定します。スレッド 1 がデータベースを更新する前に、スレッド 2 がリクエストを作成します。何らかの理由でキャッシュが失敗した場合、スレッド 2 はデータベースにクエリを実行し、古いデータをこのとき、スレッド 2 は正常に更新しますが、データベースはキャッシュを削除し、スレッド 1 はデータベースから読み取ったばかりの古いデータをキャッシュに書き込み続けるため、データの不整合が発生します。
- データベースを先に更新することでデータの不整合が発生する可能性が低く、キャッシュにTTLを追加することでこの問題の発生をある程度回避できるため、まずデータベースを更新してからキャッシュを削除します
2. キャッシュの侵入?
クライアントが要求したデータがキャッシュまたはデータベースにないため、キャッシュを確立できません。存在しないデータが複数回要求されると、それらの要求すべてがデータベースにヒットし、データベースに負荷がかかります。
解決策 1 空のオブジェクトをキャッシュする
- シンプルなブルート フォース。空の値 null を返します。次のリクエストはキャッシュ内で null にヒットし、データベースへのリクエストは続行されません。
- 利点: 実装が簡単で保守が簡単
- 欠点 1: 存在しない要求されたデータが多数ある場合、多くの null がキャッシュに格納され、追加のメモリ消費が発生します。解決策: NULL に TTL を追加して、キャッシュ領域の長期占有を避けることもできます。
- デメリット 2: null 期限が切れる前にリクエストで取得したデータは null であるため、この時点でデータベースがデータを更新すると、短期的な不整合が発生します。解決策: アクティブな更新メカニズムを使用して、データベースの更新中に null を置き換えます。
解決策 2 ブルームフィルター
- クライアントと Redis の間にブルーム フィルターを追加します。クライアントがリクエストを送信すると、ブルーム フィルターはまずデータが存在するかどうかを判断します。存在する場合は解放されます。リクエストは通常のプロセスの実行を継続します。存在しない場合は、データが存在するかどうかを判断します。キャッシュとデータベースへの頻繁なリクエストを無効にする
- ブルーム フィルターはビット配列であり、各ビットは 0 または 1 のいずれかであり、要素が存在するかどうかを迅速に判断できます。
- データを保存する: k 個の異なるハッシュ関数に従ってデータをビット配列の k 位置にマッピングし、k 位置を 1 に設定します。
- データを決定する: k 個の異なるハッシュ関数に基づいてデータをビット配列の k 位置にマッピングします。k 位置に 0 がある場合、データは間違いなく存在しません。切片。k 位置がすべて 1 の場合、データは存在する可能性があります(ハッシュが競合する可能性があるため、誤った判断がある可能性があります)、通過を許可します
- 利点: 高速、省スペース
- 短所: 実装が複雑、判断ミスが存在する
3. キャッシュ雪崩?
Redis キャッシュ内の多数のキーが同時に無効または期限切れになったり、Redis サーバーがダウンしたりして、大量のリクエストがデータベースに直接到達し、データベースに大きな負荷がかかります。
解決策 1 ランダムな TTL 値
多数のキーに同じ TTL を設定すると、多数のキーが同時に期限切れになるため、異なるキーに均等に分散されたランダムな TTL を追加できます。
解決策 2 redis クラスター
1 つの Redis がダウンしても、他の Redis が存在します
解決策 3 サービスの中断とダウングレード
- Redis がダウンすると、雪崩を防ぐために Redis キャッシュ サービスが一時停止 (サーキット ブレーカー) されます。
- Redis が直接爆発した場合、サービスはダウングレードすることしかできず、リクエストは無視され、エラーが直接返されます。
解決策 4 マルチレベル キャッシュ
複数のレベル (ブラウザ、データベースなど) でキャッシュを確立します。一次レベル キャッシュと二次レベル キャッシュの有効期限は異なります。一次レベル キャッシュが失敗した場合は、二次レベル キャッシュを使用します。
4. キャッシュの故障? ホットスポットキーの問題?
一部のホット キー (同時にアクセスされ、複雑なキャッシュ再構築操作が必要なキー) は、キャッシュ内で無効であるか期限切れです。最初のリクエストが失敗すると、データベースがクエリされてキャッシュに書き込まれます。ただし、ホット キーの数が多い場合は、この期間中に同じキーのリクエストが殺到すると、クエリのために大量のリクエストがデータベースに殺到し、データベースに大きな負荷がかかります。
解決策 1 ミューテックス ロック
- スレッド 1 がクエリ キャッシュを見逃すと、ロックの取得が試行され、取得が成功した後にのみデータベースにクエリを実行し、キャッシュへの書き込み後にロックを解放できます。この期間中に、同じクエリを持つ他のリクエストがキャッシュをミスすると、ロックの取得も試行しますが、取得に失敗した場合は、一定時間待機して、再度キャッシュにクエリを実行してロックを取得しようとします。
- 利点: 追加のメモリ消費がない、データの一貫性が向上、実装が簡単
- 短所: ロックを取得したスレッドがキャッシュに書き込むかロックを解放するまで、他のスレッドは待機する必要があり、デッドロックのリスクがあります。
解決策 2 論理的な有効期限
- TTL を設定する代わりに、論理有効期限を設定します。TTL はカウントダウンに相当し、論理有効期限は特定の時間に相当します。
- スレッド 1 がキャッシュにクエリを実行すると、キャッシュの論理時間が期限切れになったことがわかるため、ミューテックス ロックの取得を試み、新しいスレッド 2 を開始してデータベースにクエリを実行し、キャッシュを再構築します。 2 を実行して実行を終了し、期限切れのデータを直接返します。
- スレッド 3 もスレッド 1 の直後にキャッシュにクエリを実行し、論理時間が経過したことが判明した場合、スレッド 3 もミューテックス ロックの取得を試みますが、この時点ではロックはスレッド 2 によって占有されており、取得は失敗し、直接諦めます。そして期限切れのデータを返します。
- ロックが解放された後に来るスレッド 4 のみが、キャッシュ内の期限切れになっていないデータにヒットできます。
- 利点: スレッドを待つ必要がなく、パフォーマンスが向上します。
- 短所: データの一貫性が低い、メモリ消費量が増える、実装が複雑