Redis脑图
2021-08-19 14:47:33 28 举报
AI智能生成
Redis缓存策略
作者其他创作
大纲/内容
青铜
为什么使用redis不用map
Java实现的Map是本地缓存,如果有多台实例(机器)的话,每个实例都需要各自保存一份缓存,缓存不具有一致性
Redis实现的是分布式缓存,如果有多台实例(机器)的话,每个实例都共享一份缓存,缓存具有一致性。
Java实现的Map不是专业做缓存的,JVM内存太大容易挂掉的。一般用做于容器来存储临时数据,缓存的数据随着JVM销毁而结束。Map所存储的数据结构,缓存过期机制等等是需要程序员自己手写的。
Redis是专业做缓存的,可以用几十个G内存来做缓存。Redis一般用作于缓存,可以将缓存数据保存在硬盘中,Redis重启了后可以将其恢复。原生提供丰富的数据结构、缓存过期机制等等简单好用的功能。
Redis实现的是分布式缓存,如果有多台实例(机器)的话,每个实例都共享一份缓存,缓存具有一致性。
Java实现的Map不是专业做缓存的,JVM内存太大容易挂掉的。一般用做于容器来存储临时数据,缓存的数据随着JVM销毁而结束。Map所存储的数据结构,缓存过期机制等等是需要程序员自己手写的。
Redis是专业做缓存的,可以用几十个G内存来做缓存。Redis一般用作于缓存,可以将缓存数据保存在硬盘中,Redis重启了后可以将其恢复。原生提供丰富的数据结构、缓存过期机制等等简单好用的功能。
为什么使用缓存
Redis数据结构
String string-->简单的key-value
list-->有序列表(底层是双向链表)-->可做简单队列
set-->无序列表(去重)-->提供一系列的交集、并集、差集的命令
hash-->哈希表-->存储结构化数据
sortset-->有序集合映射(member-score)-->排行榜
白金
Redis服务器的数据库
如果不指定具体的数量,默认会有16个数据库。
当切换到15号数据库,存进15号库的数据,再切换到0号数据库时,是获取不到的!
这说明,数据库与数据库之间的数据是隔离的。
这说明,数据库与数据库之间的数据是隔离的。
Redis对过期键的处理
键的过期(生存)时间了。
设置键的生存时间可以通过EXPIRE或者PEXPIRE命令。
设置键的过期时间可以通过EXPIREAT或者PEXPIREAT命令。
移除过期时间,查看剩余生存时间的命令了:
PERSIST(移除过期时间)
TTL(Time To Live)返回剩余生存时间,以秒为单位
PTTL以毫秒为单位返回键的剩余生存时间
删除策略
定时删除(对内存友好,对CPU不友好)
到时间点上就把所有过期的键删除了。
到时间点上就把所有过期的键删除了。
惰性删除(对CPU极度友好,对内存极度不友好)
每次从键空间取键的时候,判断一下该键是否过期了,如果过期了就删除。
每次从键空间取键的时候,判断一下该键是否过期了,如果过期了就删除。
定期删除(折中)
每隔一段时间去删除过期键,限制删除的执行时长和频率。
Redis采用的是惰性删除+定期删除两种策略,所以说,在Redis里边如果过期键到了过期的时间了,未必被立马删除的!
每隔一段时间去删除过期键,限制删除的执行时长和频率。
Redis采用的是惰性删除+定期删除两种策略,所以说,在Redis里边如果过期键到了过期的时间了,未必被立马删除的!
淘汰策略
从过期时间的数据中挑选最近最少使用的数据淘汰
从过期时间的数据中挑选要过期的数据淘汰
从过期时间的数据中挑选任意的数据淘汰
从所有的数据中挑选最近最少使用的数据淘汰(默认)
从所有的数据中挑选任意的数据淘汰
禁止淘汰
Redis持久化策略(RDB和AOF)
Redis是基于内存的,如果不想办法将数据保存在硬盘上,一旦Redis重启(退出/故障),内存的数据将会全部丢失。
RDB(基于快照),将某一时刻的所有数据保存到一个RDB文件中
持久化功能:生成RDB文件
同步:SAVE会阻塞Redis服务器进程,服务器不能接收任何请求,直到RDB文件创建完毕为止。
异步:BGSAVE创建出一个子进程,由子进程来负责创建RDB文件,服务器进程可以继续接收请求。
恢复
Redis服务器在启动的时候,如果发现有RDB文件,就会自动载入RDB文件(不需要人工干预)
过期键的策略:
执行SAVE或者BGSAVE命令创建出的RDB文件,程序会对数据库中的过期键检查,已过期的键不会保存在RDB文件中。
载入RDB文件时,程序同样会对RDB文件中的键进行检查,过期的键会被忽略。
AOF(append-only-file)优先恢复,当Redis服务器执行写命令的时候,将执行的写命令保存到AOF文件中。
持久化功能的实现可以分为3个步骤:
命令追加:命令写入aof_buf缓冲区
文件写入:调用flushAppendOnlyFile函数,考虑是否要将aof_buf缓冲区写入AOF文件中
文件同步:考虑是否将内存缓冲区的数据真正写入到硬盘
AOP重写
AOF重写由Redis自行触发(参数配置),也可以用BGREWRITEAOF命令手动触发重写操作。
要值得说明的是:AOF重写不需要对现有的AOF文件进行任何的读取、分析。AOF重写是通过读取服务器当前数据库的数据来实现的!
要值得说明的是:AOF重写不需要对现有的AOF文件进行任何的读取、分析。AOF重写是通过读取服务器当前数据库的数据来实现的!
AOP后台重写
Redis将AOF重写程序放到子进程里执行(BGREWRITEAOF命令),像BGSAVE命令一样fork出一个子进程来完成重写AOF的操作,从而不会影响到主进程。
创建子线程后,执行后的写操作会写入AOF缓冲区
子线程完成AOP重写后,让父线程将AOP重写缓冲区的数据放到新的AOP中
RDB和AOF用哪个?
RDB和AOF并不互斥,它俩可以同时使用。
RDB的优点:载入时恢复数据快、文件体积小。
RDB的缺点:会一定程度上丢失数据(因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)
AOF的优点:丢失数据少(默认配置只丢失一秒的数据)。
AOF的缺点:恢复数据相对较慢,文件体积大
RDB的优点:载入时恢复数据快、文件体积小。
RDB的缺点:会一定程度上丢失数据(因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)
AOF的优点:丢失数据少(默认配置只丢失一秒的数据)。
AOF的缺点:恢复数据相对较慢,文件体积大
Redis单线程为什么快
纯内存操作
核心是基于非阻塞的IO多路复用机制
单线程避免了多线程的频繁上下文切换问题
优化方案
不要和其他高硬盘负载的服务放在一台机器上,例如 MQ,存储。
AOF 重写时会消耗大量硬盘 IO,可以开启配置 no-appendfsync-on-rewrite,默认关闭。表示在 AOF 重写期间不做 fsync 操作。
当开启 AOF 的 Redis 在高并发场景下,如果使用普通机械硬盘,每秒的写速率是 100MB左右,这时,Redis 的性能瓶颈在硬盘上,建议使用 SSD。
对于单机配置多个 Redis 实例的情况,可以配置不同实例分盘存储 AOF 文件,分摊硬盘压力。
铂金
主从架构
http://t.cn/AiRS1aQp
http://t.cn/AiRS1aQp
原因
Redis的内存是有限的,可能放不下那么多的数据
单台Redis支持的并发量也是有限的。
万一这台Redis挂了,所有的请求全走关系数据库了,那就更炸了
特点
主服务器负责接收写请求
从服务器负责接收读请求
从服务器的数据由主服务器复制过去。主从服务器的数据是一致的
复制功能
同步
将从服务器的数据库状态更新至主服务器的数据库状态
初次同步
从服务器没有复制过任何的主服务器,或者从服务器要复制的主服务器跟上次复制的主服务器不一样。
完整重同步
从服务器向主服务器建立Socket并发送PSYNC命令
收到PSYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件。并用一个缓冲区来记录从现在开始执行的所有写命令。
当主服务器的BGSAVE命令执行完后,将生成的RDB文件发送给从服务器,从服务器接收和载入RBD文件。将自己的数据库状态更新至与主服务器执行BGSAVE命令时的状态。
主服务器将所有缓冲区的写命令发送给从服务器,从服务器执行这些写命令,达到数据最终一致性。
断线后同步
处于命令传播阶段的主从服务器因为网络原因中断了复制,从服务器通过自动重连重新连接主服务器,并继续复制主服务器
部分重同步
功能
主从服务器的复制偏移量
主服务器的复制积压缓冲区
服务器运行的ID(run ID)
主服务器的复制积压缓冲区
服务器运行的ID(run ID)
流程
断线重连以后,从服务器向主服务器发送PSYNC命令,报告现在的偏移量是36,那么主服务器该对从服务器执行完整重同步还是部分重同步呢??这就交由复制积压缓冲区来决定。
当主服务器进行命令传播时,不仅仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面(这个大小可以调的)。如果复制积压缓冲区存在丢失的偏移量的数据,那就执行部分重同步,否则执行完整重同步。
服务器运行的ID(run ID)实际上就是用来比对ID是否相同。如果不相同,则说明从服务器断线之前复制的主服务器和当前连接的主服务器是两台服务器,这就会进行完整重同步。
当主服务器进行命令传播时,不仅仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面(这个大小可以调的)。如果复制积压缓冲区存在丢失的偏移量的数据,那就执行部分重同步,否则执行完整重同步。
服务器运行的ID(run ID)实际上就是用来比对ID是否相同。如果不相同,则说明从服务器断线之前复制的主服务器和当前连接的主服务器是两台服务器,这就会进行完整重同步。
命令传播
主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致,让主从服务器的数据库状态重新回到一致状态。
好处
读写分离(主服务器负责写,从服务器负责读)
高可用(某一台从服务器挂了,其他从服务器还能继续接收请求,不影响服务)
处理更多的并发量(每台从服务器都可以接收读请求,读QPS就上去了)
铂金二
主服务器挂了
哨兵机制
哨兵Sentinel可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能,Replication则是负责让一个Redis服务器可以配备多个备份的服务器。
通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决。每个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否活着,如果发现对方在指定时间内未回应,则暂时认为对方宕机。若哨兵群中的多数sentinel都报告某一master没响应,系统才认为该master真正宕机,通过Raft投票算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
哨兵至少需要3个实例,来保证自己的健壮性。
缓存雪崩
原因
对缓存数据设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库
解决方法:在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。
Redis挂掉了,请求全部走数据库
事发前:实现Redis的高可用(主从架构+Sentinel 或者Redis Cluster),尽量避免Redis挂掉这种情况发生。
事发中:万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流(hystrix),尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
缓存穿透
缓存穿透是指查询一个一定不存在的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。
解决方案
由于请求的参数是不合法的(每次都请求不存在的参数),于是我们可以使用布隆过滤器(BloomFilter)或者压缩filter提前拦截,不合法就不让这个请求到数据库层!
当我们从数据库找不到的时候,我们也将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。
这种情况我们一般会将空对象设置一个较短的过期时间。
这种情况我们一般会将空对象设置一个较短的过期时间。
缓存与数据库双写一致问题
解决方案
键的过期时间
只要我们设置了键的过期时间,我们就能保证缓存和数据库的数据最终是一致的。因为只要缓存数据过期了,就会被删除。随后读的时候,因为缓存里没有,就可以查数据库的数据,然后将数据库查出来的数据写入到缓存中。
更新操作
先操作数据库,再操作缓存
如果原子性被破坏了:
第一步成功(操作数据库),第二步失败(删除缓存),会导致数据库里是新数据,而缓存里是旧数据。
如果第一步(操作数据库)就失败了,我们可以直接返回错误(Exception),不会出现数据不一致。
如果第一步(操作数据库)就失败了,我们可以直接返回错误(Exception),不会出现数据不一致。
高并发的场景下
现数据库与缓存数据不一致的概率特别低,也不是没有:
缓存刚好失效
线程A查询数据库,得一个旧值
线程B将新值写入数据库
线程B删除缓存
线程A将查到的旧值写入缓存
缓存刚好失效
线程A查询数据库,得一个旧值
线程B将新值写入数据库
线程B删除缓存
线程A将查到的旧值写入缓存
删除缓存失败的解决思路:
将需要删除的key发送到消息队列中
自己消费消息,获得需要删除的key
不断重试删除操作,直到成功
自己消费消息,获得需要删除的key
不断重试删除操作,直到成功
先操作缓存,再操作数据库
如果原子性被破坏了:
第一步成功(删除缓存),第二步失败(更新数据库),数据库和缓存的数据还是一致的。
如果第一步(删除缓存)就失败了,我们可以直接返回错误(Exception),数据库和缓存的数据还是一致的。
如果第一步(删除缓存)就失败了,我们可以直接返回错误(Exception),数据库和缓存的数据还是一致的。
在并发场景下
线程A删除了缓存
线程B查询,发现缓存已不存在
线程B去数据库查询得到旧值
线程B将旧值写入缓存
线程A将新值写入数据库
线程B查询,发现缓存已不存在
线程B去数据库查询得到旧值
线程B将旧值写入缓存
线程A将新值写入数据库
并发下解决数据库与缓存不一致的思路:
将删除缓存、修改数据库、读取缓存等的操作积压到队列里边,实现串行化
对比
先更新数据库,再删除缓存
在高并发下表现优异,在原子性被破坏时表现不如意
先删除缓存,再更新数据库
在高并发下表现不如意,在原子性被破坏时表现优异
0 条评论
下一页
为你推荐
查看更多