ゴールデン ナインとシルバー 10 が間もなく登場します。この記事では、Redis の典型的な面接の質問 20 個を紹介します。皆さんのお役に立てれば幸いです。
Redis は、正式な英語名は Remote Dictionary Server (リモート辞書サービス) で、ANSI C 言語で書かれたオープンソースで、ネットワークをサポートし、メモリベースにすることができます。永続的なログ タイプ、Key-Value データベースを備え、複数の言語で API を提供します。 [関連する推奨事項: Redis ビデオ チュートリアル ]
MySQL データベースとは異なり、Redis データはメモリに保存されます。読み取りおよび書き込み速度は非常に高速で、1 秒あたり 100,000 回を超える読み取りおよび書き込み操作を処理できます。したがって、Redis はキャッシュに広く使用されているだけでなく、分散ロックにもよく使用されます。さらに、Redis はトランザクション、永続性、LUA スクリプト、LRU 駆動イベント、およびさまざまなクラスター ソリューションをサポートします。
2. Redis の基本的なデータ構造タイプについて話しましょうString (文字列)
#String (string)
はじめに: String は Redis の最も基本的なデータ構造型であり、バイナリです。安全で、画像やシリアル化されたオブジェクトを保存できます。保存される最大値は 512Mget key
など
アプリケーション シナリオ: 共有セッション、分散ロック、カウンター、電流制限。
##C 言語の文字列は SDS (単純な動的文字列) カプセル化を使用します。sds のソース コードは次のとおりです。 :
struct sdshdr{ unsigned int len; // 标记buf的长度 unsigned int free; //标记buf中未使用的元素个数 char buf[]; // 存放元素的坑 }
C 言語ネイティブであるのに、Redis が SDS
構造を選択する理由 char[] いい匂いしませんか? たとえば、SDS では、文字列の長さは O(1) の時間計算量で取得できますが、C 文字列の場合は、文字列全体を走査する必要があり、時間計算量は O です。 (n)
Hash (ハッシュ)
#内部エンコーディング: ziplist (圧縮リスト)
,
アプリケーション シナリオ: ユーザー情報のキャッシュなど。
内部エンコーディング: ziplist (圧縮リスト) )、linkedlist (リンク リスト)
アプリケーション シナリオ: メッセージ キュー、記事リスト、lpush lpop=Stack (スタック)
lpush rpop=Queue (キュー)
- lpsh ltrim=Capped Collection (限定コレクション)
- lpush brpop=Message Queue (メッセージキュー)
- Set (コレクション)
sadd key element [element ... ]
, smembers key
intset (整数セット)
, hashtable (ハッシュ テーブル)
zadd キー スコア メンバー [スコア メンバー ...]
、zrank キー メンバー
ziplist (圧縮リスト)
、skiplist (ジャンプ テーブル)
メモリの読み取りと書き込みがディスクよりもはるかに高速であることは誰もが知っています。メモリ ストレージ。データはディスク上の MySQL データベースに保存されるため、ディスク I/O の消費がなくなります。
効率を向上させるために、Mysql インデックスは B ツリー データ構造を選択することがわかっています。実際、合理的なデータ構造により、アプリケーション/プログラムを高速化できます。まず、Redis のデータ構造と内部エンコード図を見てみましょう。
- #文字列の長さの処理: Redis は文字列の長さを取得します。時間計算量は O(1) ですが、C 言語では最初からたどる必要があり、計算量は O(n);
- スペースの事前割り当て : 文字列が変更される頻度が高くなるほど、メモリの割り当ても頻繁になり、パフォーマンスが消費されます。SDS の変更とスペースの拡張により、追加の未使用スペースが割り当てられ、パフォーマンスの損失が軽減されます。
- Lazy space release: SDS が短縮されると、余分なメモリ領域をリサイクルする代わりに、free に余分な領域が記録されます。その後変更があった場合、free に記録された領域は割り当てを削減するために直接使用されます。
- バイナリ セーフティ: Redis はバイナリ データを保存できます。C 言語では、文字列は '\0' に遭遇すると終了しますが、SDS では、len 属性が文字列の終わりをマークします。
Redis は K-V 型のメモリ データベースであり、すべてのキー値は辞書に格納されます。ディクショナリは HashMap などのハッシュ テーブルであり、キーを通じて対応する値を直接取得できます。ハッシュテーブルの特徴としては、O(1)の計算量で対応する値が得られる。
- スキップ テーブルは Redis の独自のデータ構造であり、リンク リストに基づいて複数のデータを追加します。 -レベルインデックスの改善、検索効率。
- スキップ テーブルは、平均 O(logN) および最悪の場合の O(N) 複雑さによるノード検索をサポートし、順次操作を通じてノードをバッチで処理することもできます。
Redis は複数のデータ型をサポートしており、各基本型は複数のデータ構造を持つことができます。いつ、どのようなデータ構造を使用するか、どのエンコーディングを使用するかは、Redis デザイナーの要約と最適化の結果です。
3.4 合理的なスレッド モデル##I/O 多重化
- 文字列: 数値が格納される場合は、int 型のエンコードが使用されます。数値以外が格納される場合は、39 バイト以下の文字列は embstr です。39 バイトより大きい場合は、embstr になります。は生のエンコーディングです。
- リスト: リスト内の要素の数が 512 未満で、リスト内の各要素の値が 64 バイト (デフォルト) 未満の場合は、ziplist エンコードを使用します。それ以外の場合は、リンクリスト エンコードを使用します。
- Hash: Ha ハッシュ型要素の数が 512 未満で、すべての値が 64 バイト未満の場合は ziplist エンコーディングを使用し、それ以外の場合はハッシュテーブル エンコーディングを使用します。
- Set: セット内の要素がすべて整数で、要素の数が 512 未満の場合は、intset エンコーディングを使用します。それ以外の場合は、ハッシュテーブル エンコーディングを使用します。
#Zset: 順序付きセット内の要素の数が 128 未満で、各要素の値が 64 バイト未満の場合は、ziplist エンコーディングを使用します。それ以外の場合は、skiplist (スキップ リスト) エンコーディングを使用します
##複数の I/O 多重化このテクノロジーにより、単一のスレッドで複数の接続リクエストを効率的に処理できるようになり、Redis は I/O 多重化テクノロジーの実装として epoll を使用します。さらに、Redis 独自のイベント処理モデルは、ネットワーク I/O にあまり時間を費やすことなく、epoll での接続、読み取り、書き込み、およびシャットダウンをイベントに変換します。
I/O: ネットワーク I/OI/O 多重化とは何ですか?
マルチチャネル: 複数のネットワーク接続多重化: 同じスレッドを再利用します。 IO 多重化は実際には同期 IO モデルであり、複数のファイル ハンドルを監視できるスレッドを実装します。ファイル ハンドルの準備が完了すると、対応する読み取りおよび書き込み操作を実行するようにアプリケーションに通知できます。ファイル ハンドルがない場合は、準備ができている場合、アプリケーションはブロックされ、CPU が引き渡されます。
シングル スレッド モデル
Redis はシングル スレッド モデルであり、シングル スレッドにより不必要なコンテキストの切り替えや競合が回避されます。 CPU ロックの消費量。シングルスレッドであるため、特定のコマンド(hgetallコマンドなど)を長時間実行するとブロッキングが発生します。 Redis は、高速実行シナリオのためのデータベースです。 、そのため、smembers、lrange、hgetall などのコマンドは注意して使用する必要があります。
Redis の仮想メモリ メカニズムとは何ですか?
仮想メモリ メカニズムは、アクセス頻度の低いデータ (コールド データ) をメモリからディスクに一時的に交換し、アクセスする必要がある他のデータ (ホット データ) のために貴重なメモリ領域を解放します。 。 データ)。 VM 機能は、ホット データとコールド データの分離を実現できるため、ホット データはメモリ内に残り、コールド データはディスクに保存されます。これにより、メモリ不足によるアクセス速度の低下の問題を回避できます。
4.1 キャッシュの侵入の問題 まず、キャッシュの一般的な使用方法を見てみましょう: 読み取りリクエストが来たとき、最初にキャッシュを確認します。キャッシュに値がヒットした場合、キャッシュは直接返されます。 ; キャッシュが見つからない場合は、データベースをチェックし、データベースの値をキャッシュに更新してから戻ります。4. キャッシュ ブレークダウン、キャッシュ ペネトレーション、キャッシュ アバランチとは何ですか?
キャッシュ侵入
: 存在してはいけないデータのクエリを指します。キャッシュが見つからないため、データベースからクエリを実行する必要があります。データが見つからない場合、キャッシュに書き込まないと、存在しないデータが要求されるたびにデータベース内でクエリされることになり、データベースに負荷がかかります。平たく言えば、読み取りリクエストがアクセスされるとき、キャッシュにもデータベースにも特定の値がないため、この値に対する各クエリ リクエストがデータベースに侵入します。これがキャッシュの侵入です。 . .
不合理なビジネス設計キャッシュの侵入は通常、次の状況によって発生します。
ブルーム フィルターの原理: 初期値 0 のビットマップ配列と N 個のハッシュ関数で構成されます。キーに対して N 個のハッシュ アルゴリズムを実行して N 個の値を取得します。これらの N 個の値をビット配列でハッシュし、1 に設定します。次に、チェック時に、これらの特定の位置がすべて 1 であれば、ブルーム フィルター処理が行われます。サーバーはキーが存在すると判断します。 。
キャッシュ スノー ラン: とは、キャッシュ内の大量のデータ バッチの有効期限とクエリ データの量を指します。は巨大で、すべてのリクエストはデータベースに直接アクセスされるため、データベースに過剰な負荷がかかったり、マシンがダウンしたりすることがあります。
キャッシュ ブレークダウン: とは、ホットスポット キーが特定の時点で期限切れになることを指し、まさにその時点で、このキーには多数の同時リクエストがあるため、大量のリクエストがデータベースにヒットします。
キャッシュのブレークダウンは少し似ています。実際、それらの違いは、キャッシュの降雪は、データベースに過度の圧力がかかっているか、さらにはダウンしていることを意味することです。キャッシュのブレークダウンは、単に DB データベースへの同時リクエストの数が多いだけです。レベル。ブレークダウンはキャッシュ スノーランのサブセットであると考えることができます。一部の記事では、この 2 つの違いは、ブレークダウンが特定のホット キー キャッシュを対象としているのに対し、Xuebeng は多くのキーを対象としている点であると考えられています。
解決策は 2 つあります:
ホット キー とは何ですか? Redisではアクセス頻度の高いキーをホットスポットキーと呼びます。
ホットスポット キーのリクエストがサーバー ホストに送信されると、リクエスト量が特に多いため、ホスト リソースが不足したり、ダウンタイムが発生したりして、通常のサービスに影響を与える可能性があります。
ホットスポット キーはどのように生成されますか?主な理由は 2 つあります。
- ユーザーが消費するデータは、速報セール、話題のニュース、その他のシナリオなど、より多くの読み取りとより少ない書き込みが必要なシナリオなど、生成されるデータよりもはるかに大きいためです。
- リクエストシャーディングが集中し単一のRediサーバーの性能を超え、例えば固定名のキーとハッシュが同一サーバーに落ちてしまうとインスタントアクセス量が膨大になりマシンのボトルネックを超えてしまい、ホットキーの問題を引き起こします。
では、日々の開発においてホットキーを特定するにはどうすればよいでしょうか?
- どのホット キーがエクスペリエンスに基づいているかを決定する;
- クライアント統計をレポートする;
- サービス エージェント層にレポートする
ホットキーの問題を解決するにはどうすればよいですか?
- Redis クラスターの拡張: シャード コピーを追加して読み取りトラフィックのバランスをとる;
- ホット キーをさまざまなサーバーに配布する;
- セカンダリ キャッシュを使用する、つまり、 JVM ローカル キャッシュにより、Redis 読み取りリクエストが削減されます。
現在set key
の場合、expire key 60
のように有効期限を設定できます。このキーが 60 秒後に期限切れになるように指定します。60 秒後に Redis はそれをどのように処理しますか?まず、いくつかの有効期限戦略を紹介します。
# 有効期限のあるキーごとにタイマーを作成する必要があり、有効期限に達するとキーはすぐにクリアされます。 。この戦略は期限切れのデータを即座にクリアでき、メモリに非常に優しいですが、期限切れのデータを処理するために大量の CPU リソースを占有するため、キャッシュの応答時間とスループットに影響します。
キーにアクセスしたときのみ、キーの有効期限が切れているかどうかを判定し、有効期限が切れたらキーをクリアします。この戦略は CPU リソースを最大限に節約できますが、メモリには非常に優しくありません。極端な場合には、期限切れの多数のキーに再度アクセスできなくなり、クリアされずに大量のメモリを占有してしまうことがあります。
一定の時間ごとに、特定の数のデータベースの有効期限切れディクショナリ内の特定の数のキーがスキャンされ、期限切れのキーがクリアされます。 。この戦略は、最初の 2 つの戦略の折衷案です。スケジュールされたスキャンの時間間隔と各スキャンの制限された消費時間を調整することにより、さまざまな状況下で CPU とメモリのリソース間の最適なバランスを実現できます。
expires ディクショナリは、有効期限が設定されたすべてのキーの有効期限データを保存します (key はキー空間内のキーへのポインタ、value はミリ秒精度のキーの UNIX タイムスタンプ)。 。キースペースは、Redis クラスターに保存されているすべてのキーを指します。
Redis は、遅延有効期限と定期有効期限の両方を使用します2 つの有効期限戦略を使用します。
ただし、通常の削除で期限切れのキーが多数失われる場合、遅延削除は実行されません。メモリには期限切れのキーが大量に蓄積され、メモリの爆発の直接の原因となります。あるいは、業務量が増加すると、Redis キーが頻繁に使用され、単純にメモリが不足し、運用保守担当者がメモリの増設を忘れることもあります。 Redis はこのままハングアップしてしまうのでしょうか?しない! Redis は、自身を保護するために 8 つのメモリ削除戦略を使用します~
- volatile-lru: メモリが新しく書き込まれたデータを収容するのに十分でない場合、有効期限が切れたキーには LRU (最も最近使用されていない) アルゴリズムを使用します;
- allkeys-lru: メモリが新しく書き込まれたデータを収容するのに不十分な場合は、すべてのキーから LRU (最も最近使用されていない) アルゴリズムを使用します消去法を実行します。
- volatile-lfu: バージョン 4.0 で新たに追加され、メモリが新しく書き込まれたデータを収容するのに不十分な場合、LFU アルゴリズムを使用して期限切れのキーの中からキーを削除します。
- allkeys-lfu: バージョン 4.0 で新たに追加され、メモリが新しく書き込まれたデータを収容するのに十分でない場合、LFU アルゴリズムを使用してすべてのキーが削除されます;
- volatile-random:メモリが不足している場合 新しく書き込まれたデータを収容する場合、有効期限を設定してキーからデータをランダムに削除します。
- allkeys-random: メモリが新しく書き込まれたデータを収容するのに不十分な場合、データはすべてのキーからランダムに削除されます。
- volatile-ttl: メモリが新しく書き込まれたデータを収容するのに十分ではない場合、有効期限が設定されているキーは有効期限に従って削除され、有効期限の早いキーが最初に削除されます。
- noeviction: デフォルト ポリシー。メモリが新しく書き込まれたデータを収容するのに十分でない場合、新しい書き込み操作はエラーを報告します。
redis というと当然キャッシュが思い浮かびますが、国内外の中大規模の Web サイトはキャッシュと切っても切れない関係にあります。ホットスポット データのキャッシュなど、キャッシュを適切に使用すると、Web サイトのアクセス速度が向上するだけでなく、データベース DB への負荷も軽減されます。さらに、memcached と比較して、Redis は豊富なデータ構造も提供し、RDB や AOF などの最も強力な永続化メカニズムも提供します。
現在のインターネットアプリケーションには、ECサイトの月次売上ランキング、ソーシャルAPPのプレゼントランキング、ミニプログラムの投票ランキングなど、さまざまなランキングが存在します。すぐ。 Redis が提供する zset
データ型は、これらの複雑なランキングを実装できます。
たとえば、ユーザーが毎日ビデオをアップロードする場合、「いいね!」のランキング リストは次のように設計できます。
zadd user:ranking:2021-03-03 Jay 3
zincrby user:ranking:2021-03-03 Jay 1
zrem user:ranking:2021-03-03 John
zrevrangebyrank user:ranking:2021-03-03 0 2
各大网站、APP应用经常需要计数器的功能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。
几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适保存 这种类型的数据,Redis提供的数据结构可以相对比较容易地实现这些功能。
消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多。这里要用到位操作——使用setbit、getbit、bitcount命令。原理是:redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统。
Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。
Redis提供了RDB和AOF两种持久化机制,它持久化文件加载流程如下:
RDB,就是把内存数据以快照的形式保存到磁盘上。
什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。
RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb
文件,Redis 重启的时候,通过加载dump.rdb
文件来恢复数据。RDB触发机制主要有以下几种:
RDB 的优点
RDB缺点
##AOF (追加のみのファイル) ログ形式の永続性各書き込み操作を記録してファイルに追加し、AOF ファイル内のコマンドを再実行して、再起動時にデータを回復します。これは主にデータの永続性に関するリアルタイムの問題を解決します。デフォルトでは有効になっていません。
AOF のワークフローは次のとおりです。AOF の利点
AOF の欠点
マスター/スレーブ モード、センチネル モード、およびクラスター モードという 3 つのデプロイメント モードがあります。
9.1 マスター/スレーブ モードマスター/スレーブ モードでは、Redis は複数のマシンをデプロイします。マスター ノードは読み取りおよび書き込み操作を担当し、スレーブ ノードは読み取り操作のみを担当します。スレーブ ノードのデータはマスター ノードから取得されます。実装原理は次のとおりです。マスター/スレーブ レプリケーション メカニズム
マスター/スレーブ レプリケーションには、フル レプリケーションと増分レプリケーションが含まれます。一般に、スレーブが初めてマスターに接続を開始する場合、または初めての接続とみなされる場合は、フル コピーが使用されます。フル コピー プロセスは次のとおりです:
psync が sync の代わりに使用されています。
スレーブがマスターと完全に同期した後、マスター上のデータが再度更新されると、増分レプリケーションがトリガーされます。
マスター ノードでデータが増加または減少すると、replicationFeedSalves() 関数がトリガーされます。その後マスター ノードで呼び出される各コマンドは、
replicationFeedSlaves()スレーブノードと同期します。この関数を実行する前に、マスターノードはユーザーが実行したコマンドにデータ更新があるかどうかを判断し、データ更新があり、スレーブノードが空でなければ、この関数が実行されます。この関数の機能は次のとおりです。
ユーザーが実行したコマンドをすべてのスレーブ ノードに送信します。そして、スレーブ ノードに実行させます。プロセスは次のとおりです。
Sentinel モード: 1 つ以上の Sentinel インスタンスで構成される Sentinel システム。すべての Redis マスター ノードとスレーブ ノードを監視でき、監視対象のマスター ノードが は、オフライン マスター サーバーの下のスレーブ ノードを新しいマスター ノード に自動的にアップグレードします。ただし、センチネル プロセスが Redis ノードを監視する場合、問題が発生する可能性があります (単一点の問題 )。そのため、複数のセンチネルを使用して Redis ノードを監視することができ、各センチネル間で継続的な通信が行われます。
簡単に言えば、センチネル モードには 3 つの機能があります:フェイルオーバー プロセスとは何ですか?
メイン サーバーがダウンし、Sentinel 1 がこの結果を最初に検出すると仮定します。システムはすぐにフェイルオーバー プロセスを実行しません。これは、Sentinel 1 が主観的にメイン サーバーが利用できないと信じているだけです。この現象は、主観的なオフライン。後続のセンチネルもメイン サーバーが利用できないことを検出し、その数が特定の値に達すると、センチネル間で投票が行われ、投票の結果に従って 1 つのセンチネルがフェイルオーバー操作を実行します。切り替えが成功すると、各センチネルはパブリッシュ/サブスクライブ モードを使用して、監視するスレーブ サーバーをホストに切り替えます。このプロセスは、客観的オフラインと呼ばれます。このようにして、クライアントに対してすべてが透過的になります。
Sentinel の動作モードは次のとおりです:
各 Sentinel は、マスター、スレーブ、および既知の他の Sentinel インスタンスに 1 秒に 1 回程度メッセージを送信します。 PING コマンド。
PING コマンドに対する最後の有効な応答からの時間が、down-after-milliseconds オプションで指定された値を超えた場合、インスタンスは Sentinel によって主観的にオフラインとしてマークされます。
マスターが主観的オフラインとしてマークされている場合、マスターを監視しているすべてのセンチネルは、マスターが実際に主観的オフライン状態に入ったことを 1 秒に 1 回確認する必要があります。
十分な数のセンチネル (設定ファイルで指定された値以上) が、マスターが指定された時間範囲内に実際に主観的オフライン状態に入ったことを確認すると、マスターはオフラインの目標としてマークされます。
通常の状況では、各センチネルは、10 秒に 1 回、知っているすべてのマスターとスレーブに INFO コマンドを送信します。
マスターが Sentinel によって客観的にオフラインとしてマークされると、Sentinel がオフライン マスターのすべてのスレーブに INFO コマンドを送信する頻度が 10 秒に 1 回から 1 秒に 1 回に変更されます
マスターがオフラインであることに同意する十分なセンチネルがいない場合、マスターの客観的なオフライン ステータスは削除されます。マスターがセンチネルの PING コマンドに対して有効な応答を返した場合、マスターの主観的なオフライン ステータスは削除されます。オフラインステータスが削除されます。ステータスが削除されます。
センチネルモードは、マスター/スレーブモードをベースにしており、読み取りと書き込みの分離を実現しており、自動的に切り替えることもでき、システムの可用性を向上させることができます。より高い。ただし、各ノードに保存されるデータは同じであるため、メモリを無駄に消費し、オンラインで拡張するのは簡単ではありません。 そこで、Redis3.0 で追加され、Redis の 分散ストレージ が実装された Cluster クラスターが登場しました。データをセグメント化します。これは、オンライン拡張の問題を解決するために、 各 Redis ノードが異なるコンテンツを格納することを意味します。さらに、レプリケーションおよびフェイルオーバー機能も提供します。
クラスタークラスターノードの通信Redisクラスターは複数のノードで構成されます。各ノードはどのように通信しますか? ゴシッププロトコルを通じて!
Redis Cluster クラスターは Gossip プロトコルを通じて通信します。ノードは継続的に情報を交換します。交換される情報には、ノードの障害、新しいノードの参加、マスター/スレーブ ノードの変更情報、スロット情報などが含まれます。一般的に使用されるゴシップ メッセージは、ping、pong、meet、fail の 4 つのタイプに分類されます。#meet メッセージ: 新しいノードに参加するよう通知します。メッセージ送信者は受信者に現在のクラスタへの参加を通知し、meet メッセージ通信が正常に完了すると、受信ノードはクラスタに参加し、定期的な ping および pong メッセージ交換を実行します。クラスターバス(クラスターバス)特に、各ノードは
- Ping メッセージ: クラスター内で最も頻繁に交換されるメッセージ。クラスター内の各ノードは、他の複数のノードに毎秒 ping メッセージを送信します。これは、ノードがオンラインかどうかを検出し、ステータス情報を相互に交換するために使用されます。 。
- pong メッセージ: ping または meets メッセージを受信すると、メッセージが正常に通信されたことを確認するための応答メッセージとして送信者に返信します。 pong メッセージは、独自のステータス データを内部的にカプセル化します。ノードは、自身の pong メッセージをクラスターにブロードキャストして、クラスター全体にステータスを更新するように通知することもできます。
- 失敗メッセージ: ノードは、クラスター内の別のノードがオフラインであると判断すると、クラスターに失敗メッセージをブロードキャストします。失敗メッセージを受信した後、他のノードは、対応するノードをオフライン状態に更新します。
を介して他のノードと通信します。通信する場合は、外部サービスのポート番号に 10000 を加えた特別なポート番号を使用します。たとえば、ノードのポート番号が 6379 の場合、他のノードとの通信に使用するポート番号は 16379 になります。ノード間の通信には特別なバイナリ プロトコルが使用されます。 ハッシュ スロット スロット アルゴリズム
ですか?いいえ、ただし ハッシュ スロット スロット アルゴリズム 。 スロット アルゴリズムデータベース全体は 16384 個のスロット (スロット) に分割されており、Redis に入力される各キーと値のペアはキーに従ってハッシュされ、16384 個のスロットの 1 つに割り当てられます。使用されるハッシュ マップも比較的単純で、CRC16 アルゴリズムを使用して 16 ビット値を計算し、その後 16384 を法とします。データベース内の各キーはこれら 16384 個のスロットの 1 つに属し、クラスター内の各ノードはこれらの 16384 個のスロットを処理できます。 クラスター内の各ノードは、ハッシュ スロットの一部を担当します。たとえば、現在のクラスターにはノード A、B、および C があり、各ノードのハッシュ スロットの数 = 16384/3、 Redis Cluster クラスターでは、16384 スロットに対応するノードが正常に動作していることを確認する必要があります。場合、担当するスロットも失敗し、クラスター全体が機能しなくなります。 したがって、高可用性を確保するために、クラスター クラスターではマスター/スレーブ レプリケーションが導入され、1 つのマスター ノードが 1 つ以上のスレーブ ノードに対応します。他のマスター ノードがマスター ノード A に ping を実行するときに、マスター ノードの半分以上が A との通信がタイムアウトになると、マスター ノード A はダウンしていると見なされます。マスター ノードがダウンすると、スレーブ ノードが有効になります。 Redis の各ノードには 2 つのものがあり、1 つはスロットで、その値の範囲は 016383 です。もう 1 つはクラスターで、クラスター管理プラグインとして理解できます。アクセスするキーが到着すると、Redis は CRC16 アルゴリズムに基づいて 16 ビット値を取得し、結果のモジュロ 16384 を取得します。 Jiangzi の各キーは、016383 の間の番号が付けられたハッシュ スロットに対応します。この値を使用して、対応するスロットに対応するノードを見つけ、アクセスするために対応するノードに自動的にジャンプします。 データは異なるノードに個別に保存されますが、クライアントにとってはクラスター全体が 1 つの全体として認識されます。クライアントは任意のノードに接続し、Redis の単一インスタンスを操作しているように見えます。クライアントが操作したキーが正しいノードに割り当てられていない場合、Redis はリダイレクト命令を返し、最終的に正しいノードをポイントします。これはブラウザー ページの 302 リダイレクト ジャンプに似ています。 Redis クラスターは高可用性を実現します。クラスター内のノードに障害が発生した場合、フェイルオーバーを使用して、クラスターが確実にサービスを提供できるようにします。通常は外部サービス。 redis クラスターは、ping/pong メッセージを通じて障害の検出を実現します。この環境には、主観的なオフラインと客観的なオフラインが含まれます。 主観的オフライン: ノードは、別のノードが利用できない、つまりオフライン状態であると考えます。この状態は最終的な障害判断ではなく、1 つのノードの意見のみを表すことができます。状況判断を誤る。 目標オフライン: ノードが実際にオフラインであることを示します。クラスター内の複数のノードは、そのノードが利用できないと考えており、コンセンサスに達します。結果。スロットを保持しているマスター ノードに障害が発生した場合、そのノードに対してフェイルオーバーを実行する必要があります。 プロセスは次のとおりです: 障害回復: 障害が発見された後、オフライン ノードがクラスターの高可用性を確保するには、マスター ノードのいずれかを選択して置き換える必要があります。プロセスは次のとおりです。 分散ロックは、分散システム内のさまざまなプロセスを制御して共有リソースに共同でアクセスするロックの実装です。ビジネス シナリオ (フラッシュ販売注文の発行、赤い封筒の取得など) ではすべて、分散ロックの使用が必要です。Redis は、私たちのプロジェクトで分散ロックとしてよく使用されます。 选了Redis分布式锁的几种实现方法,大家来讨论下,看有没有啥问题哈。 如果执行完 笔者看过有开发小伙伴是这么实现分布式锁的,但是这种方案也有这些缺点: 这个方案可能存在这样的问题: 在这里,判断当前线程加的锁和释放锁是不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。 一般也是用lua脚本代替。lua脚本如下:Redis Cluster Cluster
フェイルオーバー
10. Redis 分散ロックを使用したことがありますか?注意すべき点は何ですか?
10.1 命令setnx + expire分开写
if(jedis.setnx(key,lock_value) == 1){ //加锁
expire(key,100); //设置过期时间
try {
do something //业务请求
}catch(){
}
finally {
jedis.del(key); //释放锁
}
}
setnx
加锁,正要执行expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。10.2 setnx + value值是过期时间
long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间
String expiresStr = String.valueOf(expires);
// 如果当前锁不存在,返回加锁成功
if (jedis.setnx(key, expiresStr) == 1) {
return true;
}
// 如果锁已经存在,获取锁的过期时间
String currentValueStr = jedis.get(key);
// 如果获取到的过期时间,小于系统当前时间,表示已经过期
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
// 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈)
String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁
return true;
}
}
//其他情况,均返回加锁失败
return false;
}
jedis.getSet()
,最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。10.3: set的扩展命令(set ex px nx)(注意可能存在的问题)
if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ //加锁
try {
do something //业务处理
}catch(){
}
finally {
jedis.del(key); //释放锁
}
}
10.4 set ex px nx + 校验唯一随机值,再删除
if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ //加锁
try {
do something //业务处理
}catch(){
}
finally {
//判断是不是当前线程加的锁,是才释放
if (uni_request_id.equals(jedis.get(key))) {
jedis.del(key); //释放锁
}
}
}
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end;
这种方式比较不错了,一般情况下,已经可以使用这种实现方式。但是存在锁过期释放了,业务还没执行完的问题(实际上,估算个业务处理的时间,一般没啥问题了)。
分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。
当前开源框架Redisson就解决了这个分布式锁问题。我们一起来看下Redisson底层原理是怎样的吧:
只要线程一加锁成功,就会启动一个watch dog
看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。
Redis一般都是集群部署的,假设数据在主从同步过程,主节点挂了,Redis分布式锁可能会有哪些问题呢?一起来看些这个流程图:
如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。
为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的:
搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。
我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。
RedLock的实现步骤:如下
- 1. 現在時刻をミリ秒単位で取得します。
- 2. 5 つのマスター ノードに順番にロックを要求します。クライアントはネットワーク接続と応答のタイムアウトを設定します。タイムアウトはロックの有効期限よりも短くする必要があります。 (自動ロックの有効期限が 10 秒であると仮定すると、タイムアウト時間は通常 5 ~ 50 ミリ秒の間になります。タイムアウト時間が 50 ミリ秒であると仮定します)。タイムアウトになった場合は、マスター ノードをスキップして、できるだけ早く次のマスター ノードを試してください。
- 3. クライアントは、現在時刻からロックの取得を開始した時刻 (つまり、手順 1 で記録された時刻) を引いた時刻を使用して、ロックの取得に使用された時間を取得します。 Redis マスター ノードの半分以上 (N/2 1、ここでは 5/2 1 = 3 ノード) がロックを取得し、使用時間がロックの有効期限よりも短い場合に限り、ロックは正常に取得されます。 (上の図に示すように、10 秒 > 30 ミリ秒 40 ミリ秒 50 ミリ秒 4 分 0 秒 50 ミリ秒)
- ロックが取得されると、キーの実際の有効時間が変化するため、ロックを取得するのに費やした時間を差し引く必要があります。
- ロックの取得が失敗した場合 (少なくとも N/2 1 マスター インスタンスでロックが取得されなかった場合、またはロックの取得時間が有効時間を超えた場合)、クライアントはすべてのマスター ノードでロックを解除する必要があります (たとえ一部のマスター ノードは正常にロックされていないため、魚がネットをすり抜けるのを防ぐためにロックを解除する必要があります)。
簡略化された手順は次のとおりです:
#最初にキャッシュを削除します
次にデータベースを更新します
しばらく(たとえば 1 秒)スリープして、再度キャッシュを削除します。
これは一体どれくらいの時間眠るのでしょうか?全部1秒ですか?
このソリューションは悪くありません。スリープ期間中 (たとえば、わずか 1 秒) に限り、ダーティ データが存在する可能性がありますが、一般の企業はそれを受け入れます。しかし、2 番目のキャッシュの削除が失敗した場合はどうなるでしょうか
キャッシュとデータベースのデータがまだ不整合である可能性がありますよね?キーに自然な有効期限を設定して、自動的に期限切れになるようにしてはどうでしょうか?企業は有効期限内にデータの不整合を受け入れる必要がありますか?それとも他にもっと良い解決策があるのでしょうか?
14.2 キャッシュ削除の再試行メカニズム遅延二重削除はキャッシュ削除の 2 番目のステップで失敗し、データの不整合が発生する可能性があるためです。このソリューションを使用して最適化できます。削除が失敗した場合は、さらに数回削除して、キャッシュの削除が確実に成功するようにします。したがって、削除キャッシュの再試行メカニズムを導入できます
#データベース更新リクエストの書き込み
削除キャッシュ メカニズムの再試行は問題ありませんが、大量の
ビジネス コード侵入mysql を例に挙げます
Redis によるマルチスレッドの使用は、シングル スレッドを完全に放棄するわけではありません。Redis は依然としてシングル スレッド モデルを使用してクライアント要求を処理します。データの読み取りと書き込みを処理するためにのみマルチ スレッドを使用します。コマンドを実行するには、依然としてシングルスレッドが使用されます。
この目的は、Redis のパフォーマンスのボトルネックが CPU ではなくネットワーク IO にあるためです。マルチスレッドを使用すると、IO の読み取りと書き込みの効率が向上し、それによって Redis の全体的なパフォーマンスが向上します。
Redis は、MULTI、EXEC、WATCH などの一連のコマンドを通じてトランザクション メカニズムを実装します。トランザクションは一度に複数のコマンドの実行をサポートしており、トランザクション内のすべてのコマンドはシリアル化されます。トランザクション実行処理中、キュー内のコマンドは順番に実行され、他のクライアントから送信されたコマンド要求はトランザクション実行コマンドシーケンスに挿入されません。
つまり、Redis トランザクションは、キュー内の一連のコマンドを 順次、1 回限り、排他的に実行するものです。
Redis トランザクション実行のプロセスは次のとおりです。説明 | |
---|---|
トランザクション ブロック内のすべてのコマンドを実行します | |
トランザクションをキャンセルし、トランザクション ブロック内のすべてのコマンドの実行を中止します | |
トランザクション ブロックの開始をマークします | |
WATCH コマンドによるすべてのキーの監視をキャンセルします。 | |
キーを監視します。トランザクションの実行前に他のコマンドによってキーが変更された場合、トランザクションは中断されます。 |
以上がRedis に関する 20 の典型的な面接質問の要約と共有 (回答分析付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。