Redis概念

1.为什么快

  • 纯内存操作
  • 单线程操作,避免了频繁的上下文切换
  • 采用了非阻塞I/O多路复用机制

2.集群

1.twemproxy,大概概念是,它类似于一个代理方式,使用方法和普通Redis无任何区别,设置好它下属的多个Redis实例后,使用时在本需要连接Redis的地方改为连接twemproxy,它会以一个代理的身份接收请求并使用一致性hash算法,将请求转接到具体Redis,将结果再返回twemproxy.使用方式简便(相对Redis只需修改连接端口),对旧项目扩展的首选。
问题: twemproxy自身单端口实例的压力,使用一致性hash后,对Redis节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。
2. codis, 目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在节点数量改变情况下,旧节点数据可恢复到新hash节点。
3. Redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。
4.在业务代码层实现,起几个毫无关联的Redis实例,在代码层,对key进行hash计算,然后去对应的Redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。

3.持久化

3.1 RDB

Redis DataBase,RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb.通过配置文件中的save参数来定义快照的周期。
优点:

  • 只有一个文件dump.rdb,方便持久化。
  • 容灾性好,一个文件可以保存到安全的磁盘。
  • 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是10最大化。使用单独子进程来进行持久化,主进程不会进行任何10操作,保证了redis的高性能
  • 相对于数据集大时,比AOF的启动效率更高。
    缺点:
    1.数据安全性低。RDB 是间隔段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

3.2 AOF


Append only fle,特久化方式:是指所有的命令行记录以redis命令请求协议的格式完全持久化存储保存为aof文件。将Redis执行的每次写命令记录到单独的日志文件中。当重启Redis会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
优点:

  • 数据安全,aof持久化可以配置appendfsyno属性,有always,每进行一次命令操作就记录到aof文件中一次。
  • 通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。
  • AOF机制的rewrite模式。AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)
    缺点:
  • AOF文件比RDB文件大,且恢复速度慢。
  • 数据集大的时候,比rdb启动效率低。
    俩种持久化的优缺点是什么
  • AOF文件比RDB更新频率高,优先使用AOF还原数据。AOF比RDB更安全也更大
  • RDB性能比AOF好
  • 如果两个都配了优先加载AOF

3.3 选择

  • 一般来说,如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF
    文件保存的数据集要比RDB文件保存的数据集要完整。
  • 如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用RDB持久化。
  • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份,并且RDB恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使
    用RDB还可以避免AOF程序的bug.
  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

3.4 扩容

  • 如果Redis被当做缓存使用, 使用一致性哈希实现动态扩容缩容。
  • 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系, 节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据
    再平衡的一套系统,而当前只有Redis集群可以做到这样。

4.redis的过期策略以及内存淘汰机制

Redis可以对存储在Redis中的缓存数据设置过期时间。
比如我们获取的短信验证码一般十分钟过期,我们这时候就需要在验证码存进Redis时添加一个key的过期时间
但是这里有一个需要格外注意的问题就是:并非key过期时间到了就一定会被Redis给删除。

4.1 定时删除

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;
但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

4.2 定期删除

Redis默认是每隔100ms就随机抽取一些设置了过期时间的Key,检查其是否过期,如果过期就删除。
为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。

4.3 惰性删除

定期删除由于是随机抽取可能会导致很多过期Key到了过期时间并没有被删除。
所以用户在从缓存获取数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。这时候就会在查询的时候将过期key从缓存中清除。

4.4.内存淘汰机制

仅仅使用定期删除+惰性删除机制还是会留下一个严重的隐患:
如果定期删除留下了很多已经过期的key,而且用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里
最终造成Redis内存块被消耗殆尽。
那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰机制提供了6种数据淘汰策略:
仅仅使用定期删除+惰性删除机制还是会留下一个严重的隐患:如果定期删除留下了很多已经过期的key,而且
用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key- -直堆积在内存里,最终
造成Redis内存块被消耗殆尽。那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰
机制提供了6种数据淘汰策略:

  • volatile-lru: 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-tt1: 从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • allkeys-lru: 当内存不足以容纳新写入数据时移除最近最少使用的key。
  • allkeys-random:从数据集中任意选择数据淘汰。
  • noenviction (默认) :当内存不足以容纳新写入数据时,新写入操作会报错

4.5 过期时间和永久有效分别设置

expire和persist命令。

4.6 过期的数据怎么处理

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

  • 定时去清理过期的缓存;
  • 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

5.线程模型

Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器(file eventhandler)
它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。
因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
文件事件处理器使用IO多路复用(multiplexing) 程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
被监听的套接字准备好执行连接应答(accept) 、读取(read) 、写入(write) 、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
虽然文件事件处理器以单线程方式运行,但通过使用IO多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与redis服务器中其他同样以单线程方式运行的模块进行对接,这保持了Redis 内部单线程设计的简单性。

6.事务

1.不支持回滚 失败继续执行剩下的
2.一个命令错误 所有命令都不执行
3.运行错误,正确命令执行

  • MULTI 开启事务,返回ok,命令被放入队列
  • EXEC 执行所有事务块的命令,返回所有命令的返回值
  • DISCARD 清空事务队列,放弃执行事务
  • WATCH 为Redis事务提供check-and-set行为,监控多个键,一个键被修改删除,之后事务不会执行

7.缓存

7.1 缓存击穿

首先我们来看下请求是如何取到数据的:
当接收到用户请求,首先先尝试从Redis缓存中获取到数据,如果缓存中能取到数据则直接返回结果,
当缓存中不存在数据时从DB获取数据,如果数据库成功取到数据,则更新Redis,然后返回数据

  • 定义:高并发的情况下,某个热门key突然过期,导致大量请求在Redis未找到缓存数据进而全部去访问DB请求数据,引起DB压力瞬间增大。

7.1.1 解决方案:缓存击穿的情况下一般不容易造成DB的宕机,只是会造成对DB的周期性压力。对缓存击穿的解决方案一般可以这样:

  • Redis中的数据不设置过期时间,然后在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,
    校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程 主动更新缓存中的数据。
    但是这种方案可能会导致有些请求会拿到过期的值,就得看业务能否可以接受,
  • 如果要求数据必须是新数据,则最好的方案则为热点数据设置为永不过期,然后加一个互斥锁保证缓存的单线程写。

7.2 缓存穿透

  • 定义:缓存穿透是指查询缓存和DB中都不存在的数据。
    比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中
    则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。

7.2.1 解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
  • 如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。

7.3 缓存雪崩

  • 定义:缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。

7.3.1 解决方案:

  • 给缓存的失效时间,加上一个随机值,避免集体失效。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题
  • 使用互斥锁,但是该方案吞吐量明显下降了。
  • 设置热点数据永远不过期。
  • 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。
    自己做缓存预热操作。然后细分以下几个小点
    1.从缓存A读数据库,有则直接返回
    2.A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
    3.更新线程同时更新缓存A和缓存B。

7.4 缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
3、定时刷新缓存;

7.5 缓存更新

缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!

7.6 缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,
并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的
最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

posted @ 2023-08-06 17:29  lwx_R  阅读(4)  评论(0编辑  收藏  举报