Redis面试题
1、谈谈Redis的主从复制流程
有几个重点:
主节点负责写,从节点负责读,slave node 主要用来进行横向扩容,做读写分离,扩容的 slave node 可以提高读的吞吐量。
必须开启 master node 的持久化,不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。
当启动一个 slave node 的时候,它会发送一个 PSYNC 命令给 master node。
slave node 初次连接到 master node,那么会触发一次 full resynchronization 全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端 client 新收到的所有写命令缓存在内存中。
断点续传是通过offset机制。
如果 master node 重启或者数据出现了变化,那么 slave node 应该根据不同的 run id 区分。
更详细见:https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-master-slave.md
2、Redis主从复制的断点续传机制是什么?
backlog、offset
3、从节点是根据主节点的IP来定位master吗?
不是,是根据master run id来定位,因为如果更换主节点或者ip更换就不能定位了
4、从节点会过期key吗?
不会,主节点才会,通过LRU淘汰key之后,会模拟一条del命令给从节点。
5、主备切换高可用的Redis哨兵集群的机制,描述一下
https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-sentinel.md
6、哨兵集群下数据丢失问题有什么解决方法
设置
min-slaves-to-write 1
min-slaves-max-lag 10
表示最少有一个从机,数据复制的延迟不能超过10秒。这样就可以减少因为异步复制数据的丢失,还有减少脑裂的数据丢失
7、哨兵集群如何实现自动发现机制
通过pub/sub实现,同时往__sentinel__:hello 这个channel发送消息,同时其他哨兵也消费这个消息,感知其他哨兵的存在。
每隔2秒发送一次,内容有自己的host、ip、runid等。
8、讲讲slave->master选举算法
如果master被判odown了,大部分哨兵允许主备切换,那么需要选举一个slave,考虑如下:
1、跟master断开连接的时长;
2、slave优先级
3、复制offset的位置,复制的越多,越优先
4、如果以上都选不出来,就选runid最小的
9、redis持久化的两种方式是什么?
RDB:对数据执行周期持久化
AOF:每条命令写入日志,append-only模式,重启后通过回放aof日志构建所有数据集
10、redis的RDB持久化优缺点是什么?
rdb是多个数据文件(分时段的处理),非常适合冷备。
对redis提供对外服务的性能影响非常少,因为redis只要fork一个子进程执行磁盘io来持久化
比aof重启更加快速,更快恢复数据
缺点是,没有AOF那么的更少的丢失数据,因为RDB需要5分钟或更久持久化一次,更容易在master故障时丢失数据。
11、如果保证Redis的高并发和高可用?
这个题目比较大。说中几点即可。参考
https://blog.csdn.net/zero__007/article/details/86564520
12、如何保证Redis和数据库的双写一致性?
主要考查点是Cache Aside Pattern中的更新数据,先写数据库,再删缓存
13、redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
过于基础,除了对初级开发,很少会问
https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-data-types.md
14、Redis的数据过期策略是什么?内存淘汰机制都有哪些?
过期策略就是内存淘汰,并不是定时检查是否过期。
内存淘汰机制:
noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。
15、Redis写的并发竞争如何解决
1、用第三方的分布式锁,比如用zookeeper的node实现
2、用redis的自身乐观锁实现(事务),即先watch、multi、set、exec事务实现。
伪代码:
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC
3、红锁实现,具体算法见http://www.redis.cn/topics/distlock.html,用Redisson客户端实现的主要方式:
public class DistributedRedisLock { private static Redisson redisson = RedissonManager.getRedisson(); private static final String LOCK_TITLE = "redisLock_"; public static void acquire(String lockName){ String key = LOCK_TITLE + lockName; RLock mylock = redisson.getLock(key); mylock.lock(2, TimeUnit.MINUTES); //lock提供带timeout参数,timeout结束强制解锁,防止死锁 System.err.println("======lock======"+Thread.currentThread().getName()); } public static void release(String lockName){ String key = LOCK_TITLE + lockName; RLock mylock = redisson.getLock(key); mylock.unlock(); System.err.println("======unlock======"+Thread.currentThread().getName()); } }
16、如何解决缓存雪崩和穿透
https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md
- 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
使用Sentinel来降级。
17、讲讲Redis cluster 的高可用与主备切换原理
redis cluster 的高可用的原理,几乎跟哨兵是类似的。
判断节点宕机
如果一个节点认为另外一个节点宕机,那么就是 pfail,主观宕机。如果多个节点都认为另外一个节点宕机了,那么就是 fail,客观宕机,跟哨兵的原理几乎一样,sdown,odown。
在 cluster-node-timeout 内,某个节点一直没有返回 pong,那么就被认为 pfail。
如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中,ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail。
从节点过滤
对宕机的 master node,从其所有的 slave node 中,选择一个切换成 master node。
检查每个 slave node 与 master node 断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成 master。
从节点选举
每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。
所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。
从节点执行主备切换,从节点切换为主节点。
与哨兵比较
整个流程跟哨兵相比,非常类似,所以说,redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。
18、聊聊Redis分布式寻址算法
首先要明白hash寻址只是负载均衡寻址算法的其中一种,就如同nginx提供4种主要算法,round-robin、ip-hash、least-connected、weighted(权重),redis主要利用hash寻址实现。
只捡重要的说,那就是一致性哈希(包含虚拟节点)和hash slot算法
一致性hash见:
https://my.oschina.net/xianggao/blog/394545?fromerr=Df6BNkP4
hash slot算法:
https://www.cnblogs.com/abc-begin/p/8203613.html
redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。
redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 hash tag 来实现。任何一台机器宕机,另外两个节点,不影响的。因为 key 找的是 hash slot,不是机器。
目前维护的开源产品:https://gitee.com/475660