分布式Redis原理+面试题

1 存储5种数据结构

string(字符串,整数,浮点) list hash set sorted-set

2 redis原理

1 expire处理过期key的方法
消极方法:懒惰型,当key访问时发现失效才会删除
积极方法:周期性的从设置了过期时间的key中选择一部分key进行删除
1 随机测试20个带有timeout信息的key
2 如果超过25%key删除,重复执行整个流程

2 发布订阅 pub/sub
producer--------> channel------->consumer
publish channel subscribe channel
不具备协议,持久化,可靠性

3 redis持久化
RDB (默认rdb持久化)当符合条件时,fork(创建)子进程,生成快照文件dump.rdb
Redis会在以下几种情况下对数据进行快照
1)配置规则
save secondes changes
save 900 1
save 300 10 (or)
save 60 10000
当多少seconds时有多少个changes的key变化会存快照文件
2) save(生成快照文件) 会阻塞所有客户端的请求
bgsave 后台异步做快照任务,redis仍能处理客户端的请求
3)flushall 清空内存数据
4)执行复制操作
缺点:两个快照间隔时会有数据丢失
AOF (默认关闭) appendonly yes
每次执行事务操作数据变更时会写入磁盘
当文件到达阀值,重写aof文件,redis正在执行的操作会写入重写缓存,再加到重写aof文件
重写过程类似快照,fork一个子进程,会全量遍历内存中的数据,然后逐个序列到aof文件中,替换上一个aof文件

4 redis内存回收策略
LRU 最少使用的数据淘汰
默认策略 内存达到预值,申请内存时会报错
allkeys-lru 最少使用的数据淘汰
allkeys-random 随机淘汰key
volatile-random/lru/ttl 在已设置过期时间的key中淘汰

5 redis 单进程单线程为什么性能很高
redis接收所有客户端的请求,单线程处理
为什么为单线程?redis瓶颈不在于CPU,而是机器的内存和网络的带宽,所以为单线程
同步阻塞:等待数据返回,造成阻塞
同步非阻塞:不用等待数据,直接返回,数据轮询发送请求告知数据已准备好
1)redis使用IO多路复用=异步阻塞
redis是单线程的,所有操作都是按照顺序线性执行,会造成阻塞,用多路复用不会
数据准备好后通知用户,用户也在监听着
2)基于内存的操作
3)单线程

3 分布式redis

1 redis集群
虽然redis有持久化能保证redis服务器宕机后也能恢复,但所有数据再一台服务器上,如果磁盘出现故障,就算有备份也会有数据丢失。
避免单点故障
主从复制 目的:将redis数据库复制多个副本在不同的服务器上,主节点挂掉后,从节点能顶上请求
如何配置主从:1从节点执行 slaveof 主节点ip 主节点端口号
2 主节点的bind IP 注释掉

1)数据同步
原理 全量复制,增量复制
全量复制: 1新增一个从节点,首先发送SYNC命令连接主服务器
2 主节点bgsave生成rdb快照
3 主节点发送快照到从节点
4 从节点load快照,即部分数据同步了
分布式Redis原理+面试题
增量复制: 执行全量复制后,仍有操作,使用增量复制,主节点发送命令到从节点,保存数据
在全量复制时,此时数据还未一致,网络断开,master无法得知slave同步了多少数据,所以master可配置保证同步多个从节点接受到数据后再接收客户端写操作
min-slave-to-write 3
网络延迟时同步;增量复制 从上次断掉的位置同步

2)选主(哨兵)master选举
作用 1 监控master和slave是否正常运行
2 当master出现故障后,从slave中选举一个新的master
sentinel <—>sentinel
哨兵集群(也有内部选举,raft算法选举)
共同监控master

2 redis-cluster 分片
由于redis集群中每个节点都拥有所有数据,所有集群的总内存受限于最小节点的内存
哨兵和集群是两个独立的功能,当不需要对数据分片使用哨兵就够了
分片模型
redis-client ----set key 123—>计算CRC16(KEY)%16383 的值
—按值分在不同槽—>1 redis-server (Master)<----slave(主从复制)
槽slot:0~5000
2 redis-server (Master)<----slave(主从复制)
槽slot:5001~10000
3 redis-server (Master)<----slave(主从复制)
槽slot:10000~16383

根据key值大小运算,按照计算出的值放入相应的槽,实现数据分区
1)分区后,如何让业务相关的数据放在同一个分区?
例如:user:(user1):id
user:(user1):name
user:(user1):sex
hashtag:让user1实行算法,就会落在同一个分区
2)客户端请求到分区上时数据不存在,则会返回MOVE 192.168.11.153:6379
告知数据在这个分区上,成为重定向
3)分片迁移,如新增一个分区,槽如何分配
从各个分区上取一部分给到新的分区,数据迁移,槽位迁移
槽位迁移过程
masterA------------>masterB
slot 123 123
状态:migranting importing:表示slot正在迁入
表示slot正在迁移
masterA :1 如果客户端访问的key还没有迁移出去,则正常处理key
2 如果key已经迁移或不存在,回复ASK信息跳转到masterB执行
masterB:1 当客户端的访问不是从ASK跳转时,不允许修改,MOVED命令

Redis的应用实战
问题1 哨兵模式下,客户端应该连接到哪个redis-server
答: 哨兵集群的地址,会通过哨兵拿到master地址信息 JedisSentinelPool
2 集群模式下,为什么会有MOVED的error
答:slot不在这台机器下,需要moved其他redis

4 分布式锁

多线程 synchronized lock
保证共享资源的安全
进程A 进程B 进程C
-------中间件(锁)------
共享资源
保证跨进程下原子性操作
是悲观锁
获得锁:jedis.setnx(key,value) 同时也是加锁,设置一个key
释放锁:jedis.multi().del(key)
获得锁成功后才能执行操作

5 面试题

1 如果有大量key需要设置同一过期时间,怎么处理
同一时间过期可能造成redis卡顿,可能出现雪崩,可设置一个随机值,让过期时间分散

2 使用redis做异步队列
使用list结构作为队列,rpush生产消息,lpop消费消息

3 reids持久化
RDB镜像全量持久化,AOF增量持久化
RDB停机时会丢数据,配合AOF

4 redis雪崩
所有请求都打到数据库,重启后又立马被流量打死
处理方式:在批量往reids存数据时,把每个key的失效时间前加个随机值,保证数据不会在同一时间大面积失效。
如果reids时集群部署,将热点数据均匀分布在不同的redis库中,也能避免全部失效问题
或者设置热点数据永不过期,有更新的操作就更新缓存(如首页商品)

5 缓存穿透和击穿
缓存穿透:指缓存和数据库中都没有数据,而用户不断发送请求,这是用户可能是攻击者,导致数据库压力增大,击垮数据库(如查询条件为id=-1)
缓存击穿:缓存雪崩是因为大面积缓存失效,打崩了DB,而缓存击穿是指击穿一个热点的key,对这一个点访问,当这个key失效时,持续的并发穿破缓存
处理方式:
缓存穿透:在接口层增加校验,比如用户鉴权,参数校验,当数据库、缓存都无数据,将key的value写为null,位置错误
bloom filter(布隆过滤器):利用高效的数据结构和算法快速判断key是否在数据库中存在,不存在return,存在更新kv再return
缓存击穿:热点数据永不过期

事前:redis高可用,主从+哨兵 redis cluster
事中:本地ehcache缓存+hystrix限流+降级
事后:reids持久化rdb+aof ,一旦重启,从磁盘中加载数据,快速恢复

6 redis为什么那么快
完全基于内存,数据结构简单,单线程,多路复用io(非阻塞io)

7 集群redis数据交互,持久化
RDB 周期性持久化 冷备
AOF 每条写入命令作为日志 热备
两种机制全开时,redis默认从AOF重构数据,因为完整
RDB 生成多个数据文件,每个代表某一时刻的数据,对redis影响性能小,因为同步时是fork一个子进程进行,缺点:快照文件,定时生成一次,会有数据丢失
AOF 实时写 文件大

8 哨兵集群
集群监控: 监控redis 的master slave是否正常
消息通知: 当redis有故障时,发送消息给管理员
故障转移: 当master node挂了,自动转移到slave node
配置中心: 如果故障转移,通知client新的master地址

9 主从之间数据同步
当第一次slave连到master,全量复制
bgsave生成rdb快照,此时新请求写进内存
slave将rdb快照写入,内存也发给slave

10 内存淘汰
LRU 随机抽取设置了过期时间的key,检查是否过期,删除

11 多个系统同时操作(并发)redis带来的数据问题
1)通过zookeeper获取分布式锁,确保同一时间只有一个系统实例操作
2)mysql+时间戳,写之前判断value的时间戳是否比缓存的value的时间戳新,如果是才能写

12 缓存、数据库的数据一致性
读:先读缓存,再读数据库
写:先写数据库 ,再写缓存