文章目录
- Redis 基础面试
- 说说你对Redis的了解?
- 说说Redis中的数据类型?
- 说说Redis数据类型对应的数据结构?
- 说说Redis对应的Java客户端有哪些?
- 说说Redis 中持久化发生了什么?
- 说说Redis中持久化以及方式?
- 如何理解Redis中RDB方式的持久化?
- RDB方式持久化常用配置参数有哪些?
- 什么情况下会RDB方式的持久化?
- RDB方式持久化有哪些优势?
- RDB方式持久化有哪些缺点?
- 如何理解Redis中AOF方式的持久化?
- AOF持久化方式有什么优势?
- AOF持久化方式有什么劣势?
- 如何理解Redis的混合持久化?
- Save和Bgsave有什么不同?
- Redis为什么要AOF重写?
- 描述一下AOF重写的过程?
- AOF 后台重写存在的问题?
- Redis高级部分
- AOF 重写存在的数据不一致问题?
- 如何理解Redis缓存穿透?
- 如何理解Redis缓存击穿?
- 如何理解Redis缓存雪崩?
- 如何理解Redis缓存预热?
- 如何理解Redis缓存更新?
- Redis的数据淘汰策略有哪些?
- Redis为什么是单线程的?
- 说说Redis中的事务操作?
- Redis是单线程还是多线程?
- 说说Redis的主从复制架构设计?
- 说说Redis中哨兵(Sentinel)?
- 说说Redis的集群架构设计?
- 如何从Redis 上亿个 key中找出10 个包含Java的 key?
Redis 基础面试
说说你对Redis的了解?
Redis(Remote Dictionary Server)是一个基于C语言编写的内存数据库,基于Key/Value结构存储数据,读写速度很快,一般会用来做缓存、消息队列,分布式锁,同时还支持事务 、持久化、集群等。
说说Redis中的数据类型?
常见的有五种基本数据类型和一些特殊数据类型,基本数据结构:String、 list、set、zset和hash,特殊数据类型如位图(bitmaps) 、计数器(hyperloglogs)和地理空间(geospatial indexes)。
string:以字符串形式存储数据,经常用于记录用户的访问次数、文章访问量等。 hash:以对象形式存储数据,比较方便的就是操作其中的某个字段,例如存储登陆用户登陆状态,实现购物车。 list:以列表形式存储数据,可记录添加顺序,允许元素重复,通常应用于发布与订阅或者说消息队列、慢查询。 set:以集合形式存储数据,不记录添加顺序,元素不能重复,也不能保证存储顺序,通常可以做全局去重、投票系统。 zset:排序集合,可对数据基于某个权重进行排序。可做排行榜,取TOP N操作。直播系统中的在线用户列表,礼物排行榜,弹幕消息等。
说说Redis数据类型对应的数据结构?
Redis 为了 平衡空间和时间效率,针对 value 的具体类型在底层会采用不同的数据结构来实现,如图所示:
说说Redis对应的Java客户端有哪些?
Redis 支持的 Java 客户端都有jedis、lettuce、Redisson等。
说说Redis 中持久化发生了什么?
Redis数据持久化从客户端发起请求开始,到服务器真实地写入磁盘,需要发生如下几件事情:
详细版 的文字描述大概就是下面这样:
- 客户端向数据库 发送写命令 (数据在客户端的内存中)
- 数据库 接收 到客户端的 写请求 (数据在服务器的内存中)
- 数据库 调用系统 API 将数据写入磁盘 (数据在内核缓冲区中)
- 操作系统将 写缓冲区 传输到 磁盘控控制器 (数据在磁盘缓存中)
- 操作系统的磁盘控制器将数据 写入实际的物理媒介 中 (数据在磁盘中)
说说Redis中持久化以及方式?
Redis持久化是把内存中的数据同步到硬盘文件中,当Redis重启后再将硬盘文件内容重新加载到内存以实现数据恢复的目的。具体持久化方式,分别为RDB和AOF方式。
如何理解Redis中RDB方式的持久化?
RDB方式的持久化是Redis数据库默认的持久化机制,是保证redis中数据可靠性的方式之一,这种方式可以按照一定的时间周期策略把内存中的数据以快照(二进制数据)的形式保存到磁盘文件中,即快照存储。对应产生的数据文件为dump.rdb。
RDB方式持久化常用配置参数有哪些?
# 这里表示每隔60s,如果有超过1000个key发生了变更,就执行一次数据持久化。
# 这个操作也被称之为snapshotting(快照)。
save 60 1000
# 持久化 rdb文件遇到问题时,主进程是否接受写入,yes 表示停止写入,
# 如果是no 表示redis继续提供服务。
stop-writes-on-bgsave-error yes
# 在进行快照镜像时,是否进行压缩。yes:压缩,
# 但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间。
rdbcompression yes
# 一个CRC64的校验就被放在了文件末尾,当存储或者加载rbd文件的时候
# 会有一个10%左右的性能下降,为了达到性能的最大化,你可以关掉这个配置项。
rdbchecksum yes
# 快照的文件名
dbfilename dump.rdb
# 存放快照的目录
dir /var/lib/redis
什么情况下会RDB方式的持久化?
- 基于配置文件中的save规则周期性的执行持久化。
- 手动执行了shutdown操作会自动执行rdb方式的持久化。
- 手动调用了save或bgsave指令执行数据持久化。
- 主从复制(Master/Slave)架构下Slave连接到Master时,Master会对数据持久化,然后全量同步到Slave。
RDB方式持久化有哪些优势?
1)RDB 文件是经过压缩的二进制文件,占用空间很小,它保存了 Redis 某个时间点的数据集,很适合做备份。
2)RDB 非常适用于灾难恢复,它只有一个文件,并且内容都非常紧凑,可以将它传送到别的数据中心。
3)RDB 方式持久化性能较好,执行持久化时可以fork 一个子进程,由子进程处理保存工作,父进程无须执行任何磁盘 I/O 操作。
RDB方式持久化有哪些缺点?
-
RDB方式在服务器故障时容易造成数据的丢失。实际项目中,我们可通过配置来控制持久化的频率。但是,如果频率太频繁,可能会对 Redis 性能产生影响。所以通常可能设置至少5分钟才保存一次快照,这时如果 Redis 出现宕机等情况,则意味着最多可能丢失5分钟数据。
-
RDB 方式使用 fork 子进程进行数据的持久化,子进程的内存是在fork操作时父进程中数据快照的大小,如果数据快照比较大的话,fork 时开辟内存会比较耗时,同时这个fork是同步操作,所以,这个过程会导致父进程无法对外提供服务。
-
RDB持久化过程中的fork操作,可能会导致内存占用加倍,Linux系统fork 子进程采用的是 copy-on-write 的方式(写时复制,修改前先复制),在 Redis 执行 RDB 持久化期间,如果 client 写入数据很频繁,那么将增加 Redis 占用的内存,最坏情况下,内存的占用将达到原先的2倍。
如何理解Redis中AOF方式的持久化?
Redis中AOF方式的持久化是将Redis收到的每一个写命令都追加到磁盘文件的最后,类似于MySQL的binlog。当Redis重启时,会重新执行文件中保存的写命令,然后在内存中重建整个数据库的内容。
AOF 持久化默认是关闭的,可以通过配置appendonly yes 开启。当 AOF 持久化功能打开后,服务器在执行完一个写命令之后,会将被执行的写命令追加到服务器端 aof 缓冲区(aof_buf)的末尾,然后再将 aof_buf 中的内容写到磁盘。
Linux 操作系统中为了提升性能,使用了页缓存(page cache)。当我们将 aof_buf 的内容写到磁盘上时,此时数据并没有真正的落盘,而是存在在 page cache 中,为了将 page cache 中的数据真正落盘,需要执行 fsync / fdatasync 命令来强制刷盘。这边的文件同步做的就是刷盘操作,或者叫文件刷盘可能更容易理解一些。
AOF持久化方式有什么优势?
AOF 比 RDB更加可靠。你可以设置不同的 fsync 策略(no、everysec 和 always)。默认是 everysec,在这种配置下,redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。
AOF文件是一个基于纯追加模式的日志文件。即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机等等), 我们也可以使用 redis-check-aof 工具也可以轻易地修复这种问题。
当 AOF文件太大时,Redis 会自动在后台进行重写。重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写是绝对安全,因为重写是在一个新的文件上进行,同时 Redis 会继续往旧的文件追加数据。当新文件重写完毕,Redis 会把新旧文件进行切换,然后开始把数据写到新文件上。
AOF 文件有序地保存了对数据库执行的所有写入操作,以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析也很轻松。如果你不小心执行了 FLUSHALL 命令把所有数据刷掉了,但只要 AOF 文件没有被重写,那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF持久化方式有什么劣势?
对于相同的数据集,AOF 文件的大小一般会比 RDB 文件大。根据所使用的 fsync 策略,AOF 的速度可能会比 RDB 慢。通常 fsync 设置为每秒一次就能获得比较高的性能,而关闭 fsync 可以让 AOF 的速度和 RDB 一样快。AOF 可能会因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。虽然这种 bug 在 AOF 文件中并不常见, 但是相较而言, RDB 几乎是不可能出现这种 bug 的。
如何理解Redis的混合持久化?
混合持久化并不是一种全新的持久化方式,而是对已有方式的优化。混合持久化只发生于 AOF 重写过程。使用了混合持久化,重写后的新 AOF 文件前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。
开启:混合持久化的配置参数为 aof-use-rdb-preamble,配置为 yes 时开启混合持久化,在 redis 4 刚引入时,默认是关闭混合持久化的,但是在 redis 5 中默认已经打开了。
关闭:使用 aof-use-rdb-preamble no 配置即可关闭混合持久化。混合持久化本质是通过 AOF 后台重写(bgrewriteaof 命令)完成的,不同的是当开启混合持久化时,fork 出的子进程先将当前全量数据以 RDB 方式写入到新的 AOF 文件,然后再将 AOF 重写缓冲区(aof_rewrite_buf_blocks)的增量命令以 AOF 方式写入到文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
优点:结合 RDB 和 AOF 的优点, 更快的重写和恢复。
缺点:AOF 文件里面的 RDB 部分不再是 AOF 格式,可读性差。
Save和Bgsave有什么不同?
SAVE生成 RDB 快照文件,但是会阻塞主进程,服务器将无法处理客户端发来的命令请求,所以通常不会直接使用该命令。 BGSAVEfork 子进程来生成 RDB 快照文件,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求
Redis为什么要AOF重写?
AOF持久化是通过保存被执行的写命令来记录数据库状态的,随着写入命令的不断增加,AOF文件中的内容会越来越多,文件的体积也会越来越大。如果不加以控制,体积过大的 AOF 文件可能会对 Redis 服务器、甚至整个宿主机造成影响,并且 AOF 文件的体积越大,使用 AOF 文件来进行数据还原所需的时间就越多。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。为了处理这种情况, Redis 引入了 AOF 重写:可以在不打断服务端处理请求的情况下, 对 AOF 文件进行重建(rebuild)。
描述一下AOF重写的过程?
描述:Redis 生成新的 AOF 文件来代替旧 AOF 文件,这个新的 AOF 文件包含重建当前数据集所需的最少命令。具体过程是遍历所有数据库的所有键,从数据库读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令。
命令:有两个 Redis 命令可以用于触发 AOF 重写,一个是 BGREWRITEAOF 、另一个是 REWRITEAOF 命令;开启:AOF 重写由两个参数共同控制,auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size,同时满足这两个条件,则触发 AOF 后台重写 BGREWRITEAOF。
// 当前AOF文件比上次重写后的AOF文件大小的增长比例超过100
auto-aof-rewrite-percentage 100
// 当前AOF文件的文件大小大于64MB
auto-aof-rewrite-min-size 64mb
关闭:auto-aof-rewrite-percentage 0,指定0的百分比,以禁用自动AOF重写功能。
REWRITEAOF:进行 AOF 重写,但是会阻塞主进程,服务器将无法处理客户端发来的命令请求,通常不会直接使用该命令。
BGREWRITEAOF:fork 子进程来进行 AOF 重写,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求。REWRITEAOF 和 BGREWRITEAOF 的关系与 SAVE 和 BGSAVE 的关系类似。
AOF 后台重写存在的问题?
AOF 后台重写使用子进程进行重写写,解决了主进程阻塞的问题,但是仍然存在另一个问题:子进程在进行 AOF 重写期间,服务器主进程还需要继续处理命令请求,新的命令可能会对现有的数据库状态进行修改,从而使得当前的数据库状态和重写后的 AOF 文件保存的数据库状态不一致。
Redis高级部分
AOF 重写存在的数据不一致问题?
Redis 引入了 AOF 重写缓冲区(aof_rewrite_buf_blocks),这个缓冲区在服务器创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,它会同时将这个写命令追加到 AOF 缓冲区和 AOF 重写缓冲区。这样一来可以保证:1、现有 AOF 文件的处理工作会如常进行。这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。2、从创建子进程开始,也就是 AOF 重写开始,服务器执行的所有写命令会被记录到 AOF 重写缓冲区里面。这样,当子进程完成 AOF 重写工作后,父进程会在 serverCron 中检测到子进程已经重写结束,则会执行以下工作:1、将 AOF 重写缓冲区中的所有内容写入到新 AOF 文件中,这时新 AOF 文件所保存的数据库状态将和服务器当前的数据库状态一致。2、对新的 AOF 文件进行改名,原子的覆盖现有的 AOF 文件,完成新旧两个 AOF 文件的替换。之后,父进程就可以继续像往常一样接受命令请求了。
如何理解Redis缓存穿透?
当访问一个缓存和数据库都不存在的 key时,请求会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。这时缓存就好像被“穿透”了一样,起不到任何作用。假如一些恶意的请求,故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力,甚至数据库挂掉,这就叫做缓存穿透。例如:
如何避免?
方案1:接口校验。在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验,用户鉴权、数据合法性校验等,例如商品查询中,商品的ID是正整数,则可以直接对非正整数直接过滤等等。
方案2:缓存空值。当访问缓存和DB都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。
方案3:布隆过滤器。使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库。可把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。
如何理解Redis缓存击穿?
描述:
某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。
如何避免?
方案1:热点数据不设置过期时间,后由定时任务去异步加载数据,更新缓存。这种方式适用于比较极端的场景,例如流量特别特别大的场景,使用时需要考虑业务能接受数据不一致的时间,还有就是异常情况的处理,不要到时候缓存刷新不上,一直是脏数据,那就凉了。
方案2:应用互斥锁。在并发的多个请求中,保证只有一个请求线程能拿到锁,并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。
关于互斥锁的选择,网上看到的大部分文章都是选择 Redis 分布式锁,因为这个可以保证只有一个请求会走到数据库,这是一种思路。但是其实仔细想想的话,这边其实没有必要保证只有一个请求走到数据库,只要保证走到数据库的请求能大大降低即可,所以还有另一个思路是 JVM 锁。JVM 锁保证了在单台服务器上只有一个请求走到数据库,通常来说已经足够保证数据库的压力大大降低,同时在性能上比分布式锁更好。需要注意的是,无论是使用“分布式锁”,还是“JVM 锁”,加锁时要按 key 维度去加锁。我看网上很多文章都是使用一个“固定的 key”加锁,这样会导致不同的 key 之间也会互相阻塞,造成性能严重损耗。
public Object loadData(String key) throws InterruptedException {
Object value = redis.get(key);
// 缓存值过期
if (value == null) {
// lockRedis:专门用于加锁的redis;
// "empty":加锁的值随便设置都可以
if (lockRedis.set(key, "empty", "PX", lockExpire, "NX")) {
try {
// 查询数据库,并写到缓存,让其他线程可以直接走缓存
value = getDataFromDb(key);
redis.set(key, value, "PX", expire);
} catch (Exception e) {
// 异常处理
} finally {
// 释放锁
lockRedis.delete(key);
}
} else {
// sleep50ms后,进行重试
Thread.sleep(50);
return getData(key);
}
}
return value;
}
如何理解Redis缓存雪崩?
描述:
缓存雪崩是当缓存服务器重启或者大量缓存集中在某一个时间段失效,造成瞬时数据库请求量大,压力骤增,导致系统崩溃。缓存雪崩其实有点像“升级版的缓存击穿”,缓存击穿是一个热点 key,缓存雪崩是一组热点 key。
如何避免?
方案1:打散过期时间。不同的key,设置不同的过期时间(例如使用一个随机值),让缓存失效的时间点尽量均匀。
方案2:做二级缓存。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
方案3:加互斥锁。缓存失效后,通过加锁或者队列来控制写缓存的线程数量。比如对某个key只允许一个线程操作缓存,其他线程等待。
方案4:热点数据不过期。该方式和缓存击穿一样,要着重考虑刷新的时间间隔和数据异常如何处理的情况。
如何理解Redis缓存预热?
缓存预热就是系统上线后,将相关的数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,接下来,用户直接查询事先被预热的缓存数据。 解决思路:直接写个缓存刷新页面,上线时手工操作下;数据量不大,可以在项目启动的时候自动进行加载;定时刷新缓存;
如何理解Redis缓存更新?
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:第一是定时去清理过期的缓存。第二是当有用户请求过来时,再判断这个请求所用到的缓存是否过期。过期的话就去底层系统得到新数据并更新缓存。两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂。具体用哪种方案,大家可以根据自己的应用场景来权衡。
Redis的数据淘汰策略有哪些?
Redis 中默认提供 6 种数据淘汰策略,分别为: volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 allkeys-lru:当内存不足以容纳新写入数据时,移除最近最少使用的 key(这个是最常用的) allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。 no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
Redis为什么是单线程的?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!),Redis利用队列技术将并发访问变为串行访问。
1)绝大部分请求是纯粹的内存操作(非常快速)。
2)采用单线程,避免了不必要的上下文切换和竞争条件。
3)非阻塞IO操作。
说说Redis中的事务操作?
Redis的一个事务从开始到结束通常会经历以下3个阶段:
1)事务开始:multi 命令将执行该命令的客户端从非事务状态切换至事务状态,底层通过 flags 属性标识。
2)命令入队:当客户端处于事务状态时,服务器会根据客户端发来的命令执行不同的操作,例如exec、discard、watch、multi 命令会被立即执行,其他命令不会立即执行,而是将命令放入到一个事务队列,然后向客户端返回 QUEUED 回复。
3)事务执行:当一个处于事务状态的客户端向服务器发送 exec 命令时,服务器会遍历事务队列,执行队列中的所有命令,最后将结果全部返回给客户端。
说明: redis 的事务并不推荐在实际中使用,如果要使用事务,推荐使用 Lua 脚本,redis 会保证一个 Lua 脚本里的所有命令的原子性。
Redis是单线程还是多线程?
Redis的线程模型,要看具体的版本:
Redis6.0 前的请求解析、键值数据读写、结果返回都是由⼀个线程完成,所以称 Redis 为单线程模型。Redis 单线程容易阻塞,为了避免阻塞,Redis 设计了⼦进程和异步线程的⽅式来完成某些耗时操作,例如使用⼦进程实现 RDB ⽣成,AOF 重写等;
Redis 6.0 开始,使用多个线程来完成请求解析和结果返回,提升对⽹络请求的处理,提升系统整体的吞吐量,不过读写命令的执行还是单线程的。
2.19 Redis是否可以保证事务的原子性?
这个问题要从三个层面进行说明,分别是:
1)命令都正常执⾏,此时原⼦性可以保证;
2)命令⼊队时出错,EXEC 时会拒绝执⾏所有命令,原⼦性可以保证;
3)命令实际执⾏时出错,Redis 会执⾏剩余命令,原⼦性得不到保证;
说说Redis的主从复制架构设计?
主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为 主节点(master),后者称为 从节点(slave)。且数据的复制是 单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步 和 从从同步 两种,后者是 Redis 后续版本新增的功能,以减轻主节点的同步负担。如图所示:
主从复制原理解析,如图所示:
说说Redis中哨兵(Sentinel)?
哨兵(Sentinel) 是 Redis 的高可用性解决方案,由一个或多个 Sentinel 实例组成的 Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器。如图所示:
Sentinel 可以在被监视的主服务器进入下线状态时,自动将下线主服务器的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
1)哨兵故障检测
检查主观下线状态: 在默认情况下,Sentinel 会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他 Sentinel 在内)发送 PING 命令,并通过实例返回的 PING 命令回复来判断实例是否在线。如果一个实例在 down-after-miliseconds 毫秒内,连续向 Sentinel 返回无效回复,那么 Sentinel 会修改这个实例所对应的实例结构,在结构的 flags 属性中设置 SRI_S_DOWN 标识,以此来表示这个实例已经进入主观下线状态。
检查客观下线状态: 当 Sentinel 将一个主服务器判断为主观下线之后,为了确定这个主服务器是否真的下线了,它会向同样监视这一服务器的其他 Sentinel 进行询问,看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线)。当 Sentinel 从其他 Sentinel 那里接收到足够数量(quorum,可配置)的已下线判断之后,Sentinel 就会将服务器置为客观下线,在 flags 上打上 SRI_O_DOWN 标识,并对主服务器执行故障转移操作。
2)哨兵故障转移流程 当哨兵监测到某个主节点客观下线之后,就会开始故障转移流程。核心流程如下:发起一次选举,选举出领头 Sentinel领头 Sentinel 在已下线主服务器的所有从服务器里面,挑选出一个从服务器,并将其升级为新的主服务器。领头 Sentinel 将剩余的所有从服务器改为复制新的主服务器。领头 Sentinel 更新相关配置信息,当这个旧的主服务器重新上线时,将其设置为新的主服务器的从服务器。
说说Redis的集群架构设计?
Redis集群(Cluster)将数据分散到多个节点,一方面 突破了 Redis 单机内存大小的限制,存储容量大大增加;另一方面 每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。集群中的每一个 Redis 节点都 互相两两相连,客户端任意 直连到集群中的 任意一台,就可以对其他 Redis 节点进行 读写的操作。同时集群支持主从复制和主节点的 自动故障转移 (与哨兵类似),当任一节点发生故障时,集群仍然可以对外提供服务。如图所示:
Redis 集群中内置了 16384 个哈希槽。当客户端连接到 Redis 集群之后,会同时得到一份关于这个 集群的配置信息,当客户端具体对某一个 key 值进行操作时,会计算出它的一个 Hash 值,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,Redis 会根据节点数量 大致均等的将哈希槽映射到不同的节点。再结合集群的配置信息就能够知道这个 key 值应该存储在哪一个具体的 Redis 节点中,如果不属于自己管,那么就会使用一个特殊的 MOVED 命令来进行一个跳转,告诉客户端去连接这个节点以获取数据。
如何从Redis 上亿个 key中找出10 个包含Java的 key?
1)keys java 命令,该命令性能很好,但是在数据量特别大的时候会有性能问题。
2)scan 0 MATCH java 命令,基于游标的迭代器,更好的选择SCAN 命令是一个基于游标的迭代器(cursor based iterator): SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。