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) |
优点 |
|
|
缺点 |
|
|
如何配置 |
|
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日志文件中,最后用新的日志文件替换旧的日志文件
|
综合 |
|
- 主从复制
- 全量复制
slave初始化的时候
流程:从服务器连接主服务器并发送sync命令,主服务器收到sync命令后,开始执行bgsave生成RDB文件,同时在缓冲区继续记录此后接收到的所有写命令。当主服务器执行完bgsave命令后,向所有从服务器发送快照文件并继续在缓冲区记录此后接收到的所有写命令。从服务器接收到快照后,舍弃旧数据,加载新数据。主服务器发送完快照后,发送缓冲区中的写命令到所有从服务器。从服务器载入快照后,开始接受新的命令并执行主服务器发送过来的写命令
- 增量复制
slave初始化完成以后,master每执行一个写命令都会向slave发送相同的写命令,slave接受并执行写命令
缓冲区满了以后就需要执行全量复制,因为缓冲区满了以后,无法写入数据到缓冲区中
缺点:master宕机以后,不能进行写操作,需要手动指定master才能继续接受写请求
- 哨兵模式
- 概念
用于监控master、slave是否正常工作,异常时提供通知和自动故障转移的redis架构
自动故障转移的时候会动态修改配置文件并且执行相关的命令,比如slaveof
主观下线:只有一个哨兵认为下线了
客观下线:配置的哨兵都认为下线了
- 哨兵的作用
监控:每个哨兵每隔1s向其他的哨兵和master以及slave发送ping命令,用于监控他们是否正常工作
通知:如果被监控的redis出现错误,那么哨兵可以通过API向管理员或者其应用程序发送通知
自动故障转移:当一个master失效的时候,哨兵会自动将该master下面的一个slave升级为master,并且把失效的master下其他的slave改为复制新的master。当客户端连接失效的master的时候,集群也会向客户端返回新的master地址,使得集群可以用新的master替换失效的master
- 原理
- 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
- 缓存常见问题
- 缓存雪崩
现象:由于缓存宕机导致缓存不可用,大量的请求全部请求到数据库中,从而打死数据库
解决方案:
事前:保证redis是高可用的。Redis主从+哨兵模式,搭建redis集群cluster模式
事中:本地ehcache和hystrix限流降级
事后:redis持久化快速恢复缓存
- 缓存穿透
现象:系统受到攻击,接收到大量系统不存在的数据,跳过缓存,每次都会去数据库查询。
解决方案:每次查询的时候,把查询不的结果写入缓存,下次再请求的时候,就会从缓存中返回数据了。
- 缓存和数据库双写一致性问题
经典的缓存+数据库读写模式:cache aside pattern
- 读取数据的时候,先读缓存,缓存没有,就从数据库中读取,然后写回缓存中
- 更新数据的时候,先删除缓存,然后更新数据库
- 为什么是删除缓存而不是更新缓存?
对于一个访问不频繁的缓存进行频繁更新,代价是比较高的
复杂场景的缓存更新是非常麻烦的
- 最初级的数据库和缓存不一致问题
问题:
先修改数据库,然后删除缓存,如果缓存删除失败,缓存中是旧数据,数据库是新数据,数据库和缓存数据不一致
解决:
先删除缓存,然后更新数据库,就算数据库更新失败,那么数据库和缓存的数据还是一致的
- 比较复杂的数据库和缓存不一致问题
问题:
先删除缓存,然后更新数据库,当数据库还未更新完成的时候,此时一个 读请求进来,读取缓存,缓存为空,去数据库查询,得到旧数据,然后将旧数据写入了缓存,导致数据库和缓存数据不一致。(对数据并发读写的时候才会出现这种不一致的问题)
解决:
数据库和缓存的更新和读取操作进行异步串行化
更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到jvm的一个本地队列中,一个队列对应一个工作线程。当删除缓存后准备更新数据的时候,一个读请求进来,发现缓存是空的,此时把这个读请求也加入队列中,并且会在队列中积压,同步等待缓存更新完成。(更新缓存的请求有多个的话,过滤一下放一个请求进入队列即可,其他的请求等待,直到获取到缓存值。这里要注意等待的时间,不宜太长,超时后直接返回数据库的旧值)
- 鹅鹅鹅
- 发发发发发