Redis之持久化、主从复制、哨兵模式、cluster集群、缓存雪崩、缓存穿透以及数据库双写一致性问题

Redis

Redis主要是提升系统的高性能和高并发

高性能:增加系统响应速度

高并发:增加系统QPS值

  • Redis持久化

    RDB:对redis中的数据做周期性的持久化,比如save 300 10

    AOF:每隔1s把内存中的redis命令作为日志,通过appenf-only的方式写入日志,重启redis的时候通过回放AOF来恢复数据,而且日志文件的命令更加可读,适合做灾难紧急恢复

    优缺点:

  • RDB重启和恢复数比较快,适合做冷备份,而AOF恢复数据较慢,不适合做冷备份。
  • AOF的数据更加完整(AOF文件会比RDB文集更大),最多丢失1s的数据,而RDB根据配置的周期会丢失更多的数据
  • RDB持久化基本不会影响redis对外提供的读写服务,因为它是fork一个子进程去执行磁盘IO,但是如果持久化的时候文数据很大,则比较耗时,会暂停客户端的其他服务,影响redis性能,所以RDB的间隔时间不宜太长
  • AOF文件过大的时候会触发rewrite,对日志中的命令进行合并、重写,不影响客户端的读写。Rewrite的时候实际上是fork一个子进程,先创建一个新的文件,然后基于当前内存的数据构建日志并写入新的临时的aof文件中,当新的文集准备好了,redis会把内存中新的redis数据追加到新的aof文件中,然后替换旧的aof文件
  • 同时使用RDB和AOF来持久化数据。AOF保证数据不丢失,作为恢复数据的第一选择。RDB做数据不同程度的冷备份,当AOF不可用的时候用于恢复数据

 

RDB

AOF

简介

对redis中的数据做周期性的持久话

AOF则是把每条redis命令作为日志,通过append-only的方式写入日志中,重启redis的时候则通过回放AOF文件来恢复数据。

持久化频率

比如每5分钟执行一次,save 300 10,意思每5分钟有10个值的变动就持久化一下

先开启AOF,appendonly yes,appendsync everysec(这里有always、everysec、no)

优点

  1. 可以做冷备份
  2. 对redis对外提供的读写服务影响很小,因为它是fork一个子进程去执行磁盘IO操作来做数据持久化
  3. 重启和恢复数据更加快速
  1. 数据更加完整,最多丢失一秒的数据
  2. 日志写入性能高,文件不易损坏(以append-only的方式写入,没有磁盘开销。)
  3. 文件过大会触发rewrite,对日志中的命令进行合并、重写(基于当时内存中的数据进行merge),不会影响客户端的读写,rewrite的时候实际上是创建一个新的文件,当新文件准备好的时候,就交换新旧文件
  4. 日志文件的命令更加可读,适合做灾难紧急恢复。

缺点

  1. 不适合做第一优先恢复方案。因为redis故障的时候,丢失数据较多
  2. Fork的子进程持久化数据的时候,若数据很大,则比较耗时,会暂停客户端提供的其他服务,影响redis性能。所以RDB的间隔时间不宜太长
  1. 恢复数据比较慢,不适合做冷备份
  2. AOF文件比RDB的文件更大
  3. 开启AOF后,写QPS会低于RDB,但也很高了

如何配置

  1. 配置文件中配置,save 60 1,每1分钟有一个值发生变化就持久化一次。可以配置多个检查点,每到一个检查点都回去check看是否需要持久化。
  2. 可以手动触发持久化,使用save和bgsave
  3. Redis客户端执行shutdown也会触发持久化,实际上就是安全退出的意思
  1. 开启aof,appendonly yes
  2. 持久化间隔,appendsync always、everysce、no
  3. Redis先将数据写入内存,然后os根据不同的策略来写入数据到磁盘

 

AOF重写日志文件原理:

  2.4版本之前是手动触发,之后是自动触发

Rewrite策略:

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

数据增长比例达到了上次的100%并且大小大于64mb的时候就会触发rewrite。

Redis fork一个子进程,基于当前内存中的数据构建日志,往一个新的临时的AOF文件中写入日志,redis主进程接收到新的些操作之后,先写入内存,然后写入就的aof文件中,当子进程完成新的日志文件后,redis主进程会把内存中新的写操作命令追加到新的aof日志文件中,最后用新的日志文件替换旧的日志文件

 

 

综合

  1. RDB和AOF同一时间只会有一个执行,强制执行也会有先后之分
  2. 同时使用RDB和AOF来持久化数据。AOF保证数据不丢失,作为恢复数据的第一选择。RDB做数据不同程度的冷备份,当AOF不可用的时候用于恢复数据
  • 主从复制
  1. 全量复制

slave初始化的时候

流程:从服务器连接主服务器并发送sync命令,主服务器收到sync命令后,开始执行bgsave生成RDB文件,同时在缓冲区继续记录此后接收到的所有写命令。当主服务器执行完bgsave命令后,向所有从服务器发送快照文件并继续在缓冲区记录此后接收到的所有写命令。从服务器接收到快照后,舍弃旧数据,加载新数据。主服务器发送完快照后,发送缓冲区中的写命令到所有从服务器。从服务器载入快照后,开始接受新的命令并执行主服务器发送过来的写命令

  1. 增量复制

slave初始化完成以后,master每执行一个写命令都会向slave发送相同的写命令,slave接受并执行写命令

缓冲区满了以后就需要执行全量复制,因为缓冲区满了以后,无法写入数据到缓冲区中

缺点:master宕机以后,不能进行写操作,需要手动指定master才能继续接受写请求

  • 哨兵模式
  1. 概念

用于监控master、slave是否正常工作,异常时提供通知和自动故障转移的redis架构

自动故障转移的时候会动态修改配置文件并且执行相关的命令,比如slaveof

主观下线:只有一个哨兵认为下线了

客观下线:配置的哨兵都认为下线了

  1. 哨兵的作用

监控:每个哨兵每隔1s向其他的哨兵和master以及slave发送ping命令,用于监控他们是否正常工作

通知:如果被监控的redis出现错误,那么哨兵可以通过API向管理员或者其应用程序发送通知

自动故障转移:当一个master失效的时候,哨兵会自动将该master下面的一个slave升级为master,并且把失效的master下其他的slave改为复制新的master。当客户端连接失效的master的时候,集群也会向客户端返回新的master地址,使得集群可以用新的master替换失效的master

  1. 原理

 

  • Redis cluster
  • Redis过期策略和内存淘汰策略

过期策略定期删除和惰性删除

定期删除:redis默认每隔100ms从设置了过期时间的key中随机抽取,检查是否过期,过期了就删除。

惰性删除:当访问某个key的时候,检查它是否设置了过期时间,如果设置了,判断是否过期,过期了就删除。

总结:定期删除是集中处理,惰性删除是零散处理

 

由于只依靠redis过期策略无法完全精确的删除设置了过期时间的key,所以就要用到内存淘汰策略。

内存淘汰策略:noeviction、allkeys-lru、allkeys-random、volatile-lru、volatile-random、volatile-ttl

noeviction:内存满了,新的写入请求直接报错,不推荐使用

allkeys-lru:针对所有key,优先删除最近使用最少的key

allkeys-random:随机删除key

volatile-lru:对于设置了过期时间的key,优先删除最近最少使用的key

volatile-random:对于设置了过期时间的key,随机删除

volatile-ttl:对于设置了过期时间的key,优先删除剩余时间短的key

  • 缓存常见问题
  1. 缓存雪崩

 

现象:由于缓存宕机导致缓存不可用,大量的请求全部请求到数据库中,从而打死数据库

解决方案:

事前:保证redis是高可用的。Redis主从+哨兵模式,搭建redis集群cluster模式

事中:本地ehcache和hystrix限流降级

事后:redis持久化快速恢复缓存

  1. 缓存穿透

现象:系统受到攻击,接收到大量系统不存在的数据,跳过缓存,每次都会去数据库查询。

解决方案:每次查询的时候,把查询不的结果写入缓存,下次再请求的时候,就会从缓存中返回数据了。

  1. 缓存和数据库双写一致性问题

经典的缓存+数据库读写模式:cache aside pattern

  1. 读取数据的时候,先读缓存,缓存没有,就从数据库中读取,然后写回缓存中
  2. 更新数据的时候,先删除缓存,然后更新数据库
  3. 为什么是删除缓存而不是更新缓存?

对于一个访问不频繁的缓存进行频繁更新,代价是比较高的

复杂场景的缓存更新是非常麻烦的

  1. 最初级的数据库和缓存不一致问题

问题:

先修改数据库,然后删除缓存,如果缓存删除失败,缓存中是旧数据,数据库是新数据,数据库和缓存数据不一致

解决:

先删除缓存,然后更新数据库,就算数据库更新失败,那么数据库和缓存的数据还是一致的

  1. 比较复杂的数据库和缓存不一致问题

问题:

先删除缓存,然后更新数据库,当数据库还未更新完成的时候,此时一个 读请求进来,读取缓存,缓存为空,去数据库查询,得到旧数据,然后将旧数据写入了缓存,导致数据库和缓存数据不一致。(对数据并发读写的时候才会出现这种不一致的问题)

解决:

数据库和缓存的更新和读取操作进行异步串行化

更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到jvm的一个本地队列中,一个队列对应一个工作线程。当删除缓存后准备更新数据的时候,一个读请求进来,发现缓存是空的,此时把这个读请求也加入队列中,并且会在队列中积压,同步等待缓存更新完成。(更新缓存的请求有多个的话,过滤一下放一个请求进入队列即可,其他的请求等待,直到获取到缓存值。这里要注意等待的时间,不宜太长,超时后直接返回数据库的旧值)

 

  1. 鹅鹅鹅
  • 发发发发发

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值