redis
2020-11-20 17:45:55 34 举报
AI智能生成
redis
作者其他创作
大纲/内容
持久化
RDB
默认情况下:redis将内存数据库快照
保存在名字为dump.rdb的二进制文件中
保存在名字为dump.rdb的二进制文件中
可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”
这一条件被满足时, 自动保存一次数据集。
这一条件被满足时, 自动保存一次数据集。
写时复制:COW(Copy-On-Write)
优点
体积小
恢复速度快
缺点
启动优先级低
易丢失数据数据
AOF
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件appendonly.aof中(先写入os cache,每隔一段时间fsync到磁盘)
比如执行命令“set zhuge 666”,aof文件里会记录如下数据
比如执行命令“set zhuge 666”,aof文件里会记录如下数据
开启功能:# appendonly yes
配置
appendfsync always:每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
appendfsync everysec:每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync no:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
配置重写频率
# auto-aof-rewrite-min-size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
# auto-aof-rewrite-percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写
# auto-aof-rewrite-percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写
优点
启动优先级高
根据策略而定丢失数据少
缺点
恢复慢
体积大
混合持久化(Redis4.0)
开启
# aof-use-rdb-preamble yes
优点
如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。
出现背景
我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间
备份策略
1写crontab定时调度脚本,每小时都copy一份rdb或aof的备份到一个目录中去,仅仅保留最近48小时的备份
2每天都保留一份当日的数据备份到一个目录中去,可以保留最近1个月的备份
3每次copy备份的时候,都把太旧的备份给删了
4每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏
数据类型
String
List
Hash
Set
zSet
集群
特性
一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性
Redis集群不需要sentinel哨兵·也能完成节点移除和故障转移的功能
需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,
据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)
据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)
redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单
原理
1.redis cluster将所有数据划分为16384个槽位(slots),每个节点负责一部分槽位,
槽位的信息存储在每个节点中
槽位的信息存储在每个节点中
2.当redis cluster的客户端,来连接集群是,他也会得到一份槽位的配置信息,并缓存在客户端
3.当客户请求某个Key时,可以直接定位到某个目标节点
4.同时因为槽位的信息会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位的调整
槽位跳转重定位
1.客户端根据自身缓存的槽位配置信息,去访问某个节点
2.访问的节点中没有该Key,则会向客户端发送携带目标操作的节点的地址
(也就是说将key正真所在的节点信息发送给客户端,告诉客户端到真正的节点去访问)
(也就是说将key正真所在的节点信息发送给客户端,告诉客户端到真正的节点去访问)
3.客户端接收到命令后,除了去正确节点操作,还会同步更新本地混村的槽位配置信息,
后续所有key将使用新的操作信息
后续所有key将使用新的操作信息
算法
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) mod 16384
HASH_SLOT = CRC16(key) mod 16384
通信机制
集中式
优点在于元数据的更新和读取,时效性非常好,
一旦元数据出现变更立即就会更新到集中式的存储中,
其他节点读取的时候立即就可以立即感知到;
不足在于所有的元数据的更新压力全部集中在一个地方,
可能导致元数据的存储压力。 很多中间件都会借助zookeeper集中式存储元数据。
一旦元数据出现变更立即就会更新到集中式的存储中,
其他节点读取的时候立即就可以立即感知到;
不足在于所有的元数据的更新压力全部集中在一个地方,
可能导致元数据的存储压力。 很多中间件都会借助zookeeper集中式存储元数据。
gossip
优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,
打到所有节点上去更新,有一定的延时,降低了压力;
缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后。
打到所有节点上去更新,有一定的延时,降低了压力;
缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后。
网络抖动
Redis Cluster 提供了一种选项cluster-node-timeout,
表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,
需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。
表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,
需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。
选举原理
1.1.slave发现自己的master变为FAIL
2.将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST 信息
3.其他节点收到该信息,只有master响应,
判断请求者的合法性,并发送FAILOVER_AUTH_ACK
,对每一个epoch只发送一次ack
判断请求者的合法性,并发送FAILOVER_AUTH_ACK
,对每一个epoch只发送一次ack
4.尝试failover的slave收集master返回的FAILOVER_AUTH_ACK
5.slave收到超过半数master的ack后变成新Master(
这里解释了集群为什么至少需要三个主节点,如果只有两个
,当其中一个挂了,只剩一个主节点是不能选举成功的)
这里解释了集群为什么至少需要三个主节点,如果只有两个
,当其中一个挂了,只剩一个主节点是不能选举成功的)
6.slave广播Pong消息通知其他集群节点。
从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,
而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,
slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票
而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,
slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票
对批量操作的支持
对于类似mset,mget这样的多个key的原生批量操作命令,redis集群只支持所有key落在同一slot的情况,如果有多个key一定要用mset命令在redis集群上操作,则可以在key的前面加上{XX},这样参数数据分片hash计算的只会是大括号里的值,这样能确保不同的key能落到同一slot里去
缓存击穿
概念
由于大批量缓存在同一时间失效可能导致大量请求同时穿透缓存直达数据库,
可能会造成数据库瞬间压力过大甚至挂掉
可能会造成数据库瞬间压力过大甚至挂掉
解决方案
对于这种情况我们在批量增加缓存时最好将这一批数据的缓存过期时间设置为一个时间段内的不同时间。
热点key
当前key是一个热点key(例如一个热门的娱乐新闻),并发量非常大。
重建缓存不能在短时间完成, 可能是一个复杂计算, 例如复杂的SQL、 多次IO、 多个依赖等。
在缓存失效的瞬间, 有大量线程来重建缓存, 造成后端负载加大, 甚至可能会让应用崩溃。
我们可以利用互斥锁来解决,此方法只允许一个线程重建缓存, 其他线程等待重建缓存的线程执行完, 重新从缓存获取数据即可。
性能
redis单线程?
redis单线程主要指的是网络IO,
和键值对读写是单线程,这也是 Redis 对外提供键值存储服务的主要流程。
但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
和键值对读写是单线程,这也是 Redis 对外提供键值存储服务的主要流程。
但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
redis为什么这么快?
因为,redis主要是内存级别的存储,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换性能损耗问题,
正因为是单线程,在使用redis命令是需要格外谨慎,以免造成卡顿
正因为是单线程,在使用redis命令是需要格外谨慎,以免造成卡顿
Redis 单线程如何处理那么多的并发客户端连接?
redis利用epoll实现多路复用,将连接信息
放到队列中,依次放到文件事件分发器中,
事件分派器将事件分发给事件处理器。
放到队列中,依次放到文件事件分发器中,
事件分派器将事件分发给事件处理器。
主从
工作原理
为master配置slave
1.发送PSYNC命令,请求同步数据,发送命令之前,
建立socket长连接
建立socket长连接
2
2.1:master收到PSYNC命令后,会在后台进行数据持久化通过bgsave生成最新的rdb快照文件
2.2:持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中
3:当持久化进行完毕以后,master会把这份rdb文件数据集发送给slave
4:slave会把接收到的数据进行持久化生成rdb,然后再加载到内存中
(清空老数据,并加载master同步过来的数据)
(清空老数据,并加载master同步过来的数据)
5:然后,master再将之前缓存在内存中的命令发送给slave。(sendBuffer)
6.执行buffer里的写命令到内存中
7.master会通过sockert的长连接,不断的把写数据的命令发送到从节点,保证数据的一致性
redis2.8以后支持数据部分复制
原理
子主题
redis改用可以支持部分数据复制的命令PSYNC去master同步数据,
slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。
slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。
master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制
哨兵
特点
sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。
原理
哨兵架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)
缺点
哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般
在主从切换的瞬间存在访问瞬断的情况
只有一个主节点对外提供服务,没法支持很高的并发
且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率
缓存穿透
概念
缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中,
通常出于容错的考虑, 如果从存储层查不到数据则不写入缓存层。
通常出于容错的考虑, 如果从存储层查不到数据则不写入缓存层。
原因
第一, 自身业务代码或者数据出现问题。
第二, 一些恶意攻击、 爬虫等造成大量空命中。
第二, 一些恶意攻击、 爬虫等造成大量空命中。
解决方案
1、缓存空对象
2.对于恶意攻击,向服务器请求大量不存在的数据造成的缓存穿透,
还可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一般都能够过滤掉,
不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
还可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一般都能够过滤掉,
不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
缓存雪崩
概念
缓存雪崩指的是缓存层支撑不住或宕掉后,流量会像奔逃的野牛一样, 打向后端存储层。
由于缓存层承载着大量请求, 有效地保护了存储层,
但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支撑不住,或者由于缓存设计不好,
类似大量请求访问bigkey,导致缓存能支撑的并发急剧下降), 于是大量请求都会打到存储层, 存储层的调用量会暴增, 造成存储层也会级联宕机的情况。
由于缓存层承载着大量请求, 有效地保护了存储层,
但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支撑不住,或者由于缓存设计不好,
类似大量请求访问bigkey,导致缓存能支撑的并发急剧下降), 于是大量请求都会打到存储层, 存储层的调用量会暴增, 造成存储层也会级联宕机的情况。
解决方案
1.1) 保证缓存层服务高可用性,比如使用Redis Sentinel或Redis Cluster。
2 依赖隔离组件为后端限流熔断并降级。比如使用Sentinel或Hystrix限流降级组件。
比如服务降级,我们可以针对不同的数据采取不同的处理方式。当业务应用访问的是非核心数据(例如电商商品属性,用户信息等)时,暂时停止从缓存中查询这些数据,而是直接返回预定义的默认降级信息、空值或是错误提示信息;当业务应用访问的是核心数据(例如电商商品库存)时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。
比如服务降级,我们可以针对不同的数据采取不同的处理方式。当业务应用访问的是非核心数据(例如电商商品属性,用户信息等)时,暂时停止从缓存中查询这些数据,而是直接返回预定义的默认降级信息、空值或是错误提示信息;当业务应用访问的是核心数据(例如电商商品库存)时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。
3提前演练。 在项目上线前, 演练缓存层宕掉后, 应用以及后端的负载情况以及可能出现的问题, 在此基础上做一些预案设定。
过期策略
定期删除
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在
大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略
大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是
每个100ms将所有的key检查一次,而是随机抽取进行检查;
因此,如果只采用定期删除策略,会导致很多key到时间没有删除
每个100ms将所有的key检查一次,而是随机抽取进行检查;
因此,如果只采用定期删除策略,会导致很多key到时间没有删除
惰性删除
只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。
极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存
极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存
采用定期删除+惰性删除
存在问题:,如果定期删除没删除key。然后你也没即时去请求key,
也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制
也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制
内存淘汰机制
配置项:maxmemory-policy volatile-lru
1)noeviction:当内存不足以容纳新写入数据
时,新写入操作会报错
时,新写入操作会报错
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最
近最少使用的key
近最少使用的key
3)allkeys-random:当内存不足以容纳新写入数据时,在键
空间中,随机移除某个key
空间中,随机移除某个key
4)volatile-lru:当内存不足以容
纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
这种情况一般是把redis既当缓存,又
做持久化存储的时候才用
纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
这种情况一般是把redis既当缓存,又
做持久化存储的时候才用
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键
空间中,随机移除某个key
空间中,随机移除某个key
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键
空间中,有更早过期时间的key优先移除
空间中,有更早过期时间的key优先移除
0 条评论
下一页