一.简介:
Redis是一种以键值对格式存储的非关系型数据库
二.特点:
1.读写效率高,10WQPS
2.单线程,线程安全
3.可作为消息中间件,支持消息发布与订阅
4.数据可持久化到本地
5.支持分布式锁
6.支持8种数据类型(List,String,Set,ZSet,Hash,Bitmap 位图,GEO 地理坐标,yperloglog 基数统计)
三.遇到的一些问题
1.数据一致性
分布式环境下,缓存与数据库之间很容易出现一致性问题。
分布式系统中,redis趋向于AP(可用性+分区容错性)
在redis的主从,哨兵还是集群模式中数据都是异步复制,而在master接收到消息到将信息复制到slave这一过程,数据是不一致的。集群模式是指Redis将数据分成多个分片(shards),每个分片可以在多个节点上复制。由于分片之间的数据也是异步复制,所以都无法保证强一致性。
如果项目要求数据强一致性,则不建议使用redis缓存,因为一致性只能通过策略来降低概率,无法保障强一致性,一般的策略包含缓存更新机制,数据库更新后及时更新缓存,并增加失败重试机制
2.击穿,穿透,雪崩
雪崩:大量key同时过期失效(缓存服务器宕机),这时有大量的请求进来,直接访问数据库,造成数据库挂掉
假设: 数据库每秒只能处理2000个请求,这个时候缓存中有大量的key过期失效,导致5000个请求直接落到数据库上,数据库肯定承受不住,也会挂掉。此时若没有采取缓存措施,数据库又急着用,刚重启(有可能没重启完成),请求又来了,数据库就又挂了。
解决方案:
- 事故前: 保证redis高可用,搭建redis集群,部署主从+哨兵,避免全盘崩溃
- 事故中: 降低数据库压力,使用Ehcache本地缓存+限流组件避免超过数据库的承受能力
- 事故后: 做redis持久化,一旦redis重启,可从磁盘中恢复数据
改造后流程:用户发送一个请求,系统先请求Ehcache本地缓存是否有数据,没有再去Redis请求数据,如果还没有就去数据库请求数据,获取到数据后先同步到Redis,再同步到Ehcache
优点:
- 数据库安全:在限流组件可用的情况下,数据库不会挂掉,限流根据确保了每秒多少请求能通过
- 部分请求可以被处理:数据库没挂,就意味着至少2/5的请求可以被处理掉
缺点:
- 高峰时期部分请求无法处理到,需要用户多次点击,因为只有2/5的请求被处理,剩下的请求,用户刷不出来界面,需要多点击几次
- redis设置的缓存失效时间不要设置相同,可根据功能、业务、请求接口灵活设置缓存时间:
- *setRedis(key,value,time+Math.random()10000)
穿透:大量的请求缓存中不存在的key
缓存中都没有对应的数据,但是用户不断发送请求,请求就会直接走数据库,这样的场景会直接是数据库挂掉,多为恶意行为
解决方案:
- 设置白名单:在接口的请求层设置参数校验,比如说用户鉴权,参数校验,不符合要求的直接return
- 对空值进行缓存:对有效的id做认证或者拦截,不符合的id直接过滤掉或者保存到redis,下次不合法的id请求时,直接从缓存中获取数据
- 使用布隆过滤器(Bloom Filter):在请求到达Redis之前,使用布隆过滤器进行初步判断,如果判断为不存在的Key,则直接返回不存在的结 果,避免请求穿透到数据库,存在的话就查询数据库然后刷新同步redis
击穿:redis中某个热点key过期失效
针对某个热点key失效或者过期,此时就会有大量的并发请求穿过缓存直接访问数据库,造成数据库的压力
解决方案:
- 热点数据永不过期:对于重要的热点数据,可以将其设置为永不过期,以避免因过期而导致的击穿问题。当需要更新数据时,可以采用异步方式更新缓存
- 使用互斥锁:在缓存失效时,通过加锁的方式保证只有一个线程能够访问数据库。其他线程等待锁释放后,直接从缓存获取数据。这样可以有效避免大量请求同时穿透Redis访问数据库。
- 实时监控和调整:实时监控Redis的缓存状态和数据访问情况,对于即将过期的热点数据,可以提前进行更新或延长其过期时间
四.Redis的淘汰策略
- volatile为前缀的策略:从已设置过期时间的数据集中淘汰
- allkeys为前缀的策略:所有key中随机淘汰
- LRU:最近最少用
- LFU:最不常用
- 他们的触发条件都 是Redis 内存达到阈值时
注意: 当使用 volatile-lru、volatile-lfu、volatile-random、volatile-ttl 这四种淘汰策略时,如果没有 key 可以淘汰,则和 neoviction 一样返回错误。
五.Redis的持久化
- RDB:快照的形式,定时的将内存中的数据写入的 *dump * 文件中保存到硬盘上
优点:
因为是快照的形式,所以效率高且保存了某个时间点上的数据集,适合备份和大规模数据的恢复
缺点:
1.很难做到秒级持久化,因为bgsave每次执行都要重新执行fork调用一次子线程,频繁执行成本过高
2.一段时间做一次快照,如果redis服务器宕机可能会丢失数据,安全性有所缺失
- AOF:每次执行命令后将命令本身记录下来,每次执行命令都会将命令写入到aof文件中
优点:可以做到秒级持久化
缺点:AOF文件体积比RDB文件体积大,高并发情况下,RDB的性能还是要高于AOF
AOF拓展
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写。重写后的新 AOF 文件包含了重写这一时刻之前数据集所需的最小命令集合
重写的流程是这样:
-
主进程会fork一个子进程出来进行AOF 重写。注意:并不是对原文件进行重新整理!而是直接读取redis服务内存中现有的键值对,然后用一条命令去代替每个键值对,写入到新的AOF文件中
-
在fork子进程这个过程中,服务端仍然可以对外提供服务,在子进程重写的这个时间段里面,主进程的数据更新操作,会缓存到aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof文件
-
当所有的数据全部追加到新的aof文件中后,会把旧的aof文件替换成新的aof文件,此后所有的操作都会被写入新的aof文件
-
如果在rewrite过程中出现故障,不会影响原来aof文件的正常工作,只有当rewrite完成后才会切换文件。因此这个rewrite过程是比较可靠的
六.Redis的主从复制
主从复制就是将一台Redis服务器(master)上的数据同步到另外几台服务器(slave),由于数据的复制是单向的,所以只能从master向slave复制,master写为主,slave读为主
建立连接
- 从节点执行 slaveof [master ip] [master port],保存主节点信息
- 与主节点建立socket连接,从节点发出Ping,主节点响应pong,两者建立通信
- slave 启动成功连接到 master 后会发送sync同步命令
master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步
全量复制: slave服务在接收到数据库文件数据后,将数据存盘并加载到内存中。
增量复制: master 继续将新收集到的修改命令依次传给slave,完成同步,但是只要是重新连接master,全量复制将被自动执行! 主机的数据一定可以在从机中看到。
作用
- 数据冗余:主从复制实现了数据的热备份
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
- 负载均衡:主从复制的基础上配合读写分离,主节点负责写,从节点负责读(即写Redis数据的时候连接主节点,读Redis数据的时候连接从节点),分担服务器负载,使redis的并发量提高(特别是在读多写少的情况下,从多个节点分担读负载)
- 读写分离:可以用于实现读写分离,主库写、从库读。读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础
七.Redis的哨兵模式
我们先说说主从复制会存在问题:
- 一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。
- 主节点的写能力和存储能力受到单机的限制。
- 原生复制的弊端在早期的版本中也会比较突出,比如:Redis 复制中断后,从节点会发起 psync。
此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或秒级的卡顿。
该系统可以执行以下四个任务:
- 监控:不断检查主服务器和从服务器是否正常运行。
- 通知:当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本向管理员或者其他应用程序发出通知。
- 自动故障转移:当主节点不能正常工作时,Sentinel 会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点,这样人工干预就可以免了。
- 配置提供者:在 Redis Sentinel 模式下,客户端应用在初始化时连接的是 Sentinel 节点集合,从中获取主节点的信息。
哨兵原理
Sentinel 使用的算法核心是 Raft算法,主要用途就是用于分布式系统,系统容错,以及Leader选举,每个Sentinel都需要定期的执行以下任务:
- 每个 Sentinel会自动发现其他 Sentinel 和从服务器,它以每秒钟一次的频率向它所知的主服务器、从服务器以及其他Sentinel实例发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds选项所指定的值, 那么这个实例会被Sentinel标记为主观下线。 有效回复可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
- 如果一个主服务器被标记为主观下线, 那么正在监视这个主服务器的所有Sentinel要以每秒一次的频率确认主服务器的确进入了主观下线状态。
- 如果一个主服务器被标记为主观下线, 并且有足够数量的Sentinel(至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断,
那么这个主服务器被标记为客观下线。 - 在一般情况下, 每个Sentinel会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。当一个主服务器Sentinel标记为客观下线时,Sentinel向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10秒一次改为每秒一次。
- 当没有足够数量的Sentinel同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向Sentinel的 PING命令返回有效回复时, 主服务器的主观下线状态就会被移除。