この記事では、知ってマスターする必要がある Redis に関する 20 の問題を紹介します。お役に立てれば幸いです。ぜひ集めてください。
Redis (Remote Dictionary Server
) は、C 言語で書かれた高性能の非リレーショナル キー/値データベースです。従来のデータベースとは異なり、Redis データはメモリに保存されるため、読み取りおよび書き込み速度が非常に速く、キャッシュに広く使用されています。 Redis はデータをディスクに書き込むことができ、データのセキュリティを確保して損失を防ぎ、Redis の操作はアトミックです。 [関連する推奨事項: Redis ビデオ チュートリアル ]
メモリ操作に基づいて、メモリの読み取りおよび書き込み速度が高速です。
Redis は シングルスレッド であるため、スレッド切り替えのオーバーヘッドやマルチスレッドの競合の問題が回避されます。シングル スレッドとは、ネットワーク リクエストが 1 つのスレッドで処理されることを意味します。つまり、1 つのスレッドがすべてのネットワーク リクエストを処理します。Redis には実行時に複数のスレッドがあります。たとえば、データ永続化のプロセスは新しいスレッドを開始します。
文字列、ハッシュ、リスト、セット、ZSet などを含む複数のデータ型をサポートします。
永続性のサポート。 Redis は、RDB と AOF という 2 つの永続化メカニズムをサポートしており、永続化機能によりデータ損失の問題を効果的に回避できます。
サポート トランザクション。 Redis のすべての操作はアトミックであり、Redis は複数の操作をマージした後のアトミック実行もサポートしています。
マスター/スレーブ レプリケーションをサポート。マスター ノードはデータをスレーブ ノードに自動的に同期し、読み取りと書き込みを分離できます。
ホットスポット データをキャッシュしてデータベースへの負担を軽減します。
の機能を実現できます。 、など。
Redis 独自のパブリッシュ/サブスクライブ モードまたはリストを使用して、単純なメッセージ キューを実装し、非同期操作を実装できます。
は、ユーザーが特定のインターフェイスにアクセスする頻度を制限するために使用できます。たとえば、フラッシュ セール シナリオは、ユーザーがアクセスするのを防ぐために使用されます。素早いクリックによる不必要な圧力。
は、共通の友人や共通の趣味などの機能を実現するために、共通の友人や共通の趣味などの機能を実現するために、交差、和集合、差集合などのいくつかのコレクション コマンドを使用します。
のみを使用しますが、Memcached は複数のコアを使用できます。
。
。
を提供します。 Memcached はネイティブ クラスター モードを提供しないため、クラスター内のシャードにデータを書き込むにはクライアントに依存する必要があります。
を使用し、Memcached はマルチスレッドのノンブロッキング IO モデルを使用します。
基本データ型:
1、String: 最も一般的に使用されるデータ型、文字列型の値は文字列または数値またはバイナリですが、最大値は 512MB を超えることはできません。
2、ハッシュ: ハッシュはキーと値のペアのコレクションです。
3、Set: 重複排除を備えた順序なしセット。 Set は交差や結合などのメソッドを提供しており、共通の友人や共通の注意などの機能を実現するのに特に便利です。
4、List: 順序付けされた反復可能なコレクション。最下層は二重リンク リストに依存して実装されます。
5, SortedSet(ZSet): 順序付きセット。 score
パラメータは実装のために内部的に維持されます。ランキングや重み付けされたメッセージ キューなどのシナリオに適しています。
特別なデータ型:
1、Bitmap: ビットマップ、ビット単位の配列とみなすことができます。配列 各ユニットは 0 または 1 のみを格納できます。配列の添え字は、ビットマップではオフセットと呼ばれます。ビットマップの長さはコレクション内の要素の数とは関係ありませんが、カーディナリティの上限に関係します。
2、ハイパーログログ。 HyperLogLog はカーディナリティ統計に使用されるアルゴリズムで、その利点は、入力要素の数またはボリュームが非常に大きい場合でも、カーディナリティの計算に必要なスペースが常に固定され、非常に小さいことです。一般的な使用シナリオは、ユニーク訪問者の数をカウントすることです。
3、地理空間: 主に地理的位置情報を保存し、保存された情報を操作するために使用されます。適用可能なシナリオには、測位、近くの人々などがあります。
トランザクションの原理は、トランザクション スコープ内の複数のコマンドを Redis に送信し、Redis にこれらのコマンドを順番に実行させることです。
トランザクション ライフ サイクル:
MULTI
を使用してトランザクションを開きます。
はトランザクションを開きます。トランザクション では、各操作のコマンドがキューに挿入され、コマンドは実際には実行されません。
EXEC
コマンドがトランザクションをコミットします。
トランザクション スコープ内のコマンドのエラーは、他のコマンドの実行には影響せず、アトミック性は保証されません:
first:0>MULTI "OK" first:0>set a 1 "QUEUED" first:0>set b 2 3 4 "QUEUED" first:0>set c 6 "QUEUED" first:0>EXEC 1) "OK" 2) "OK" 3) "OK" 4) "ERR syntax error" 5) "OK" 6) "OK" 7) "OK"
WATCH コマンド
WATCH
コマンドは 1 つ以上のキーを監視できます。キーの 1 つが変更されると、後続のトランザクションは実行されなくなります (オプティミスティック ロックと同様) )。 EXEC
コマンド実行後、監視は自動的に解除されます。
first:0>watch name "OK" first:0>set name 1 "OK" first:0>MULTI "OK" first:0>set name 2 "QUEUED" first:0>set gender 1 "QUEUED" first:0>EXEC (nil) first:0>get gender (nil)
たとえば、上記のコードでは:
watch name
は name
key## の監視をオンにします。
name
の値を変更しますa
#の値。
コマンド
を使用して、WATCH
コマンドによる key
の監視をキャンセルし、すべての監視ロックが解除されます。 永続化メカニズム
に書き込むことです。 Redis は 2 つの永続化メソッドをサポートしています。1 つは
RDB メソッド、もう 1 つは AOF
メソッドです。 前者は、指定されたルールに従って定期的にデータをハードディスクのメモリに保存します。
一方、後者は、コマンドを実行するたびにコマンドを記録します。一般に、この 2 つを組み合わせて使用します。 RDB メソッド
dump.rdb ファイルが生成されます。 Redis を再起動すると、
dump.rdb ファイルがロードされてデータが復元されます。
bgsave
Execution
BGSAVERedis 親プロセスは、現在実行中の子プロセス
があるかどうかを判断します。存在する場合、fork
操作を実行します
親プロセスfork完了後、子プロセスはデータを一時ファイル
;子プロセスがすべてのデータの書き込みを完了すると、古い RDB ファイルをこの一時ファイルに置き換えます。
手动触发:用户执行SAVE
或BGSAVE
命令。SAVE
命令执行快照的过程会阻塞所有客户端的请求,应避免在生产环境使用此命令。BGSAVE
命令可以在后台异步进行快照操作,快照的同时服务器还可以继续响应客户端的请求,因此需要手动执行快照时推荐使用BGSAVE
命令。
被动触发:
SAVE 100 10
,100秒内至少有10个键被修改则进行快照。BGSAVE
生成 RDB 文件并发送给从节点。shutdown
命令时,如果没有开启 AOF 持久化功能则自动执行·BGSAVE·。优点:
Redis 加载 RDB 恢复数据远远快于 AOF 的方式。
使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能。
缺点:
RDB方式数据无法做到实时持久化。因为BGSAVE
每次运行都要执行fork
操作创建子进程,属于重量级操作,频繁执行成本比较高。
RDB 文件使用特定二进制格式保存,Redis 版本升级过程中有多个格式的 RDB 版本,存在老版本 Redis 无法兼容新版 RDB 格式的问题。
AOF(append only file)持久化:以独立日志的方式记录每次写命令,Redis重启时会重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,AOF 是Redis持久化的主流方式。
默认情况下Redis没有开启AOF方式的持久化,可以通过appendonly
参数启用:appendonly yes
。开启AOF方式持久化后每执行一条写命令,Redis就会将该命令写进aof_buf
缓冲区,AOF缓冲区根据对应的策略向硬盘做同步操作。
默认情况下系统每30秒会执行一次同步操作。为了防止缓冲区数据丢失,可以在Redis写入AOF文件后主动要求系统将缓冲区数据同步到硬盘上。可以通过appendfsync
参数设置同步的时机。
appendfsync always //每次写入aof文件都会执行同步,最安全最慢,不建议配置 appendfsync everysec //既保证性能也保证安全,建议配置 appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
所有的写入命令会追加到 AOP 缓冲区中。
AOF 缓冲区根据对应的策略向硬盘同步。
随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩文件体积的目的。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。
优点:
AOF可以更好的保护数据不丢失,可以配置 AOF 每秒执行一次fsync
操作,如果Redis进程挂掉,最多丢失1秒的数据。
AOF以append-only
的模式写入,所以没有磁盘寻址的开销,写入性能非常高。
缺点:
对于同一份文件AOF文件比RDB数据快照要大。
数据恢复比较慢。
Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作,当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的,它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
//启动Redis实例作为主数据库 redis-server //启动另一个实例作为从数据库 redis-server --port 6380 --slaveof 127.0.0.1 6379 slaveof 127.0.0.1 6379 //停止接收其他数据库的同步并转化为主数据库 SLAVEOF NO ONE
主从复制的原理?
当启动一个从节点时,它会发送一个 PSYNC
命令给主节点;
如果是从节点初次连接到主节点,那么会触发一次全量复制。此时主节点会启动一个后台线程,开始生成一份 RDB
快照文件;
同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB
文件生成完毕后, 主节点会将RDB
文件发送给从节点,从节点会先将RDB
文件写入本地磁盘,然后再从本地磁盘加载到内存中;
その後、マスター ノードはメモリにキャッシュされた書き込みコマンドをスレーブ ノードに送信し、スレーブ ノードはデータを同期します。スレーブ ノードは、ネットワークに障害が発生して接続が切断された場合、自動的に再接続します。接続後、マスター ノードは欠落しているデータの一部のみをスレーブ ノードに同期します。
Sentinel
動作原理
各 Sentinel
は 1 秒に 1 回メッセージを送信します 既知Slave
、およびその他の Sentinel
インスタンスは、PING
コマンドを送信します。
インスタンスの PING
コマンドに対する最後の有効な応答からの時間が指定された値を超えた場合、インスタンスは
Master
が主観的オフラインとしてマークされている場合、この Sentinel
は 1 秒に 1 回の頻度で確認する必要があります Master
本当に主観的なオフライン状態に入ったかどうか。
十分な数の Sentinel
(構成ファイルで指定された値以上) により、Master
は客観的にオフラインとしてマークされます。 Master
がオフラインであることに同意するのに十分な Sentinel
がいない場合、Master
の客観的なオフライン ステータスは解除されます。 Master
が Sentinel
の PING
コマンドに対して再度有効な応答を返した場合、Master
の主観的なオフライン ステータスは削除されます。
センチネル ノードは、フェイルオーバーを担当するセンチネル リーダーを選出します。
センチネル リーダーは、パフォーマンスの良いスレーブ ノードを新しいマスター ノードとして選出し、他のスレーブ ノードにマスター ノード情報を更新するように通知します。 Redis クラスターは
仮想スロット パーティショニングを使用します。すべてのキーはハッシュ関数に従って 0 ~ 16383 の整数スロットにマッピングされます。各ノードはスロットの一部とキー値を維持する責任があります。スロットデータによってマッピングされます。
#ハッシュ スロットは Redis インスタンスにどのようにマッピングされますか?
crc16
の結果を計算します。 結果の残りを 16384 にすると、取得された値は
key
に基づいて対応するインスタンスを見つけます。スロット情報
は 容量の動的な拡張をサポートします。
データは高可用性
。一部のノードが使用不可になった場合でも、クラスターは引き続き使用可能です。クラスター モードでは、自動フェイルオーバー (フェイルオーバー) を実現し、gossip プロトコルを通じてノード間でステータス情報を交換し、投票メカニズムを使用して
欠点:
key
が異なるノードに分散されている場合、同じノード上の複数の key
トランザクション操作のみをサポートします。この期間中はトランザクション機能はご利用いただけません。 key
データ分割の最小粒度として、hash
、list
などの大きなキーと値のオブジェクトをマッピングすることはできません。別のノードに。 1、受動的削除 (遅延)。キーにアクセスしたときに、キーの有効期限が切れていることが判明した場合、キーは削除されます。
2、積極的に (定期的に) 削除します。定期的にキーをクリーンアップします。各クリーンアップは、すべての DB を順番に走査し、データベースから 20 個のキーをランダムに取り出し、期限切れになった場合は削除します。5 つのキーの期限が切れた場合は、この DB のクリーンアップを続行し、それ以外の場合は次の DB のクリーンアップを開始します。
3.メモリ不足時のクリーンアップ。 Redis には最大メモリ制限があります。最大メモリは maxmemory パラメータで設定できます。使用されているメモリが設定された最大メモリを超えると、メモリを解放する必要があります。メモリが解放されると、構成された削除戦略に従ってメモリがクリーンアップされます。
メモリ除去戦略とは何ですか? Redis のメモリが最大許容メモリを超えると、Redis サーバーの正常な動作を確保するために、Redis はメモリ削除戦略をトリガーし、使用頻度の低いデータを削除します。Redisv4.0 より前では、6 つのデータ削除戦略が提供されていました :
)、最近使用しました。 LRU アルゴリズムを使用して、有効期限が設定されたキーを削除します
Redisv4.0 以降、次の 2 つの が追加されます:
メモリ削除ポリシーは、構成ファイルを通じて 変更できます。対応する構成項目は maxmemory-policy で、デフォルト構成は
です。ノエビクション 。
1. 最初にキャッシュを削除してからデータベースを更新します。
更新操作を実行する場合は、最初にキャッシュを削除してからデータベースを更新します。後続のリクエストが読み取られるとき再度、データベースからデータが読み取られ、読み取られた後、新しいデータがキャッシュに更新されます。 既存の問題: キャッシュされたデータを削除した後、データベースを更新する前に、この期間中に新しい読み取りリクエストがあると、古いデータがデータベースから読み取られてキャッシュに再書き込みされ、再び不整合が発生します。そして、それ以降の読み取りはすべて古いデータになります。2. 最初にデータベースを更新してからキャッシュを削除します。
更新操作を実行するときは、最初に MySQL を更新します。成功したら、キャッシュを削除してから、新しいデータを後続の読み取りリクエストに送信するライトバック キャッシュ。 既存の問題: MySQL の更新からキャッシュの削除までの期間、読み取りリクエストはキャッシュされた古いデータのままですが、データベースの更新が完了すると一貫性があり、影響は比較的小さくなります。小さい。3. 非同期更新キャッシュ
データベース更新操作完了後、キャッシュは直接操作されず、操作コマンドがメッセージにカプセル化されて投げ込まれます。メッセージ キュー、そして Redis 自体がデータを消費して更新します。メッセージ キューはデータ操作シーケンスの一貫性を確保し、キャッシュ システム内のデータが正常であることを保証します。キャッシュ ペネトレーション、キャッシュ アバランシェ、キャッシュ ブレークダウン [詳細説明]キャッシュ ペネトレーションキャッシュペネトレーションとは、
存在しないデータをクエリすることを指します。ミスがあった場合、キャッシュは受動的に書き込まれるため、データがDBから見つからない場合、データはDBに書き込まれません。これにより、この存在しないデータが要求されるたびに DB 内でクエリが行われることになり、キャッシュの意味が失われます。トラフィックが多い場合、DB がハングする可能性があります。
キャッシュ空の値、データベースはチェックされません。
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap
中,查询不存在的数据会被这个bitmap
拦截掉,从而避免了对DB
的查询压力。
布隆过滤器的原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。查询时,将元素通过散列函数映射之后会得到k个点,如果这些点有任何一个0,则被检元素一定不在,直接返回;如果都是1,则查询元素很可能存在,就会去查询Redis和数据库。
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重挂掉。
解决方法:在原有的失效时间基础上增加一个随机值,使得过期时间分散一些。
缓存击穿:大量的请求同时查询一个 key 时,此时这个 key 正好失效了,就会导致大量的请求都落到数据库。缓存击穿是查询缓存中失效的 key,而缓存穿透是查询不存在的 key。
解决方法:加分布式锁,第一个请求的线程可以拿到锁,拿到锁的线程查询到了数据之后设置缓存,其他的线程获取锁失败会等待50ms然后重新到缓存取数据,这样便可以避免大量的请求落到数据库。
public String get(String key) { String value = redis.get(key); if (value == null) { //缓存值过期 String unique_key = systemId + ":" + key; //设置30s的超时 if (redis.set(unique_key, 1, 'NX', 'PX', 30000) == 1) { //设置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(unique_key); } else { //其他线程已经到数据库取值并回写到缓存了,可以重试获取缓存值 sleep(50); get(key); //重试 } } else { return value; } }
redis客户端执行一条命令分4个过程:发送命令、命令排队、命令执行、返回结果。使用pipeline
可以批量请求,批量返回结果,执行速度比逐条执行要快。
使用pipeline
组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline
命令完成。
原生批命令(mset和mget)与pipeline
对比:
原生批命令是原子性,pipeline
是非原子性。pipeline命令中途异常退出,之前执行成功的命令不会回滚。
原生批命令只有一个命令,但pipeline
支持多命令。
Redis 通过 LUA 脚本创建具有原子性的命令:当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。
在Redis中执行Lua脚本有两种方法:eval
和evalsha
。eval
命令使用内置的 Lua 解释器,对 Lua 脚本进行求值。
//第一个参数是lua脚本,第二个参数是键名参数个数,剩下的是键名参数和附加参数 > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second"
lua脚本作用
1、Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
2、Lua脚本可以将多条命令一次性打包,有效地减少网络开销。
应用场景
举例:限制接口访问频率。
在Redis维护一个接口访问次数的键值对,key
是接口名称,value
是访问次数。每次访问接口时,会执行以下操作:
aop
拦截接口的请求,对接口请求进行计数,每次进来一个请求,相应的接口访问次数count
加1,存入redis。count=1
,并设置过期时间。因为这里set()
和expire()
组合操作不是原子操作,所以引入lua
脚本,实现原子操作,避免并发访问问题。private String buildLuaScript() { return "local c" + "\nc = redis.call('get',KEYS[1])" + "\nif c and tonumber(c) > tonumber(ARGV[1]) then" + "\nreturn c;" + "\nend" + "\nc = redis.call('incr',KEYS[1])" + "\nif tonumber(c) == 1 then" + "\nredis.call('expire',KEYS[1],ARGV[2])" + "\nend" + "\nreturn c;"; } String luaScript = buildLuaScript(); RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class); Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
PS:这种接口限流的实现方式比较简单,问题也比较多,一般不会使用,接口限流用的比较多的是令牌桶算法和漏桶算法。
更多编程相关知识,请访问:编程入门!!
以上がRedis でマスターしなければならない 20 の質問、ぜひ集めてください。 !の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。