缓存(Redis)

分布式缓存的原理与应用

缓存指的是将频繁访问的数据放在内存中,加快用户访问速度的技术.缓存分为进程级缓存和分布式缓存,进程级缓存指将数据缓存在服务内部,通过Map,List等结构实现存储,分布式缓存指将缓存数据单独存放在分布式系统中.常见的分布式缓存系统有Ehcache,Redis,和Memcached.

分布式缓存介绍

当我们频繁访问热数据(一些用户信息,系统字典信息等)时,为了加快系统的访问速度,我们把数据缓存在内存中,这样用户再次访问数据时直接从内存中获取,不用频繁查询数据库,这不但缩短了系统的访问时间,还有效降低了数据库的负载,如下图所示,

在用户有写请求数据时,先将数据写入数据库,再写入缓存,用户再次访问数据时先尝试从缓存中获取,如果在缓存中没找到数据,就去数据库中查询并将结果返回给用户,再将查询到的结果写入到缓存中.
在这里插入图片描述
对于传统的单点Web系统一般使用进程内缓存即可,而在微服务架构下往往需要一个分布式缓存来实现跨服务的缓存系统,如下图所示

用户访问的数据库是被部署在多个服务器节点的集群数据库,缓存是被部署在多个服务器节点的分布式缓存,同时缓存之间有数据备份,在一个节点出现问题后,分布式缓存会将用户的请求转发到其他备份节点来保证业务的正常运行.
请添加图片描述

Redis的原理与应用

Redis是一个开源的内存中的数据结构存储系统,可以用做数据库,缓存和消息中间件,支持多种类型的数据结构,String,Hash,List,Set,ZSet(有序集合).Redis内置了复制,Lua脚本,LRU驱动事件,事务和不同级别的磁盘持久化,并通过Redis哨兵(Sentinel)模式和集群模式(Cluster)提供高可用性(High Availability).

Redis的原理

Redis不但支持丰富的数据类型,还支持分布式事务,数据分片,数据持久化等功能,是分布式系统中不可或缺的内存数据库服务.

1.Redis管道

Redis是基于请求/响应协议的TCP服务.在客户端向服务器发送一个查询请求后,需要监听Socket的返回,该监听过程一直阻塞,直到服务器有结果返回.由于Redis集群是部署在多个服务器上的,所以Redis的请求/响应模型在每次请求时都要跨网站在不同的服务器之间传输数据,这样每次查询的都存在一定的网络延迟.由于服务器一般采用多线程处理业务,并且内存操作效率很高,所以如果一次请求延迟20ms,则多次请求的网络延迟会不断累加.也就是说,分布式环境下,Redis性能的瓶颈主要体现在网络延迟上,Redis请求/响应模型的数据请求,响应流程如图所示:请添加图片描述
Redis的管道技术指在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应.管道技术能减少客户端和服务器交互的次数,将客户端批量的请求发送给服务器,服务器针对批量数据分别查询并统一回复,能显著提高Redis的性能.Redis管道模型的请求流程如图所示,请添加图片描述

2.Redis的事务

Redis支持分布式环境下的事务操作,其事务可以一次执行多个命令,事务中的所有命令都会序列化地顺序执行.在事务在执行过程中,不会被其他客户端发送过来的命令请求打断.服务器在执行完事务中的所有命令之后,才会继续处理其他客户端的其他命令.Redis的事务操作分为开启事务,命令入队列,执行任务三个阶段.Redis的事务执行流程如下:

  1. 事务开启:客户端执行Multi命令开启事务
  2. 提交请求:客户端提交命令到事务
  3. 任务入队列:Redis将客户端请求放入事务队列中等待执行
  4. 入队状态反馈:服务器返回QURUD,表示命令已被放入事务队列中
  5. 执行命令:客户端通过Exec执行事务
  6. 事务执行错误:在Redis事务中如果某条命令执行错误,则其他命令会继续执行,不会回滚.可以通过Watch监控事务执行的状态并处理命令执行错误的异常情况.
  7. 执行结果反馈:服务器向客户端返回事务执行的结果

请添加图片描述
Redis事务的相关命令有:

  • Multi:标记一个事务块的开始
  • Exec:标记所有改动事务内的命令
  • Discard:取消事务,放弃执行事务块内的所有命令
  • Watch:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令改动,那么事务将被打断
  • Unwatch:取消Watch命令对所有key的监视

Redis事务基于SprignBoot的使用如下:

public void transcationSet(Map<String,Object> commandList){
	//1.开启事务权限
	redisTemplate.setEnableTranscationSupport(true);
	try{
		//2.开启事务
		redisTemplate.multi();
		//3.执行事务命令
		for(Map.Entry<String,Object> entry:commandList.entrySet()){
            String mapKey = entry.getKey();
            Object mapValue = entry.getValue();
            redisTemplate.opsForValue().set(mapKey,mapValue);
		}
		//4.成功就提交
		redisTemplate.exec();
	}catch(Exception e){
		//5.失败就回滚
		redisTemplate.discard();
	}
}
3.Redis发布,订阅

Redis发布,订阅是一种消息通信模式:发送者(Pub)向频道(channel)发送消息,订阅者(Sub)接受频道上的消息.Redis客户端可以订阅任意数量的频道,发送者也可以向任意频道发送数据.下图展示了1个发送者(pub1),1个频道,和3个订阅者(sub1,sub2,sub3)的关系.由于3个订阅者sub1,sub2,sub3都订阅了频道channel0,在发送者pub1向频道channel0发送一条消息后,这条消息就会被发送给订阅它的三个客户端.
请添加图片描述

4.Redis集群数据复制的原理

Redis提供了复制功能,可以实现在主数据库(Master)中的数据更新后,自动将更新的数据同步到到从数据库(Slave).一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库.

Redis的主从复制原理如下:
请添加图片描述

  1. 一个从数据库启动后,会向主数据库发送SYNC命令
  2. 主数据库在接受到SYNC命令后会开始在后台保存快照(即RDB的持久化过程),并将保存快照期间接受到的命令缓存起来.在该持久化过程中会产生一个.rdb快照文件.
  3. 在主数据库快照执行完成后,Redis会将快照文件和所有缓存的命令以.rdb快照文件的形式发送给数据库.
  4. 从数据库收到主数据库的.rdb快照文件后,载入该快照文件到本地
  5. 从数据库执行载入后的.rdb快照文件,将数据写入内存中.以上过程称为复制初始化
  6. 在复制初始化结束后,主数据库在每次收到写命令时都会将命令同步给从数据库,从而保证主从数据库的数据一致.
6.Redis的集群模式及工作原理

Redis有三种集群模式:主从模式,哨兵模式和集群模式.

(1)主从模式:

所有的写请求都被发送到主数据库上,再由主数据库将数据同步到从数据库上.主数据库主要用于执行写操作和数据同步,从数据库主要用于读操作缓解系统的读压力.Redis的主从模式如下图所示:请添加图片描述

(2)哨兵模式:

在主从模式上添加一个哨兵角色来监控集群的运行状态.哨兵通过发送命令让Redis服务器返回其运行状态,哨兵是一个独立运行的进程,在监测到Master宕机时会自动将Slave切换成Master,然后通过发布与订阅模式通知其他服务器修改配置文件,完成主备热切.Redis的哨兵模式如下图所示:请添加图片描述
(3)集群模式:Redis集群实现了在多个Redis节点之间进行数据分片和数据复制.基于Redis集群的数据自动分片能力,我们能够方便的对Redis集群进行横向扩展,以提高Redis集群的吞吐量.基于Redis集群的数据复制能力,在集群中的一部分节点失效或者无法进行通信时,Redis仍然可以基于副本数据对外提供服务,这提高了集群的可用性.Redis的集群模式如图所示:
请添加图片描述

分布式缓存设计的核心问题

1.缓存预热

缓存预热指在用户请求数据前先将数据加载到缓存系统中,用户查询事先被预热的缓存数据,以提高系统查询效率,缓存预热一般有系统启动加载,定时加载等方式.

2.缓存更新

缓存更新是指在数据发生变化以后将变化的数据更新到缓存中,常见的缓存策略有以下几种:

  • 定时更新:定时将底层数据库内的数据更新到缓存中
  • 过期更新:定时将缓存中过期的数据更新为最新数据并更新缓存的过期时间
  • 写请求更新;在用户有写请求时先写数据库同时更新缓存,这适用于用户对缓存数据和数据库的一致性有很高要求的情况
  • 读请求更新:在用户有读请求时,先判断该请求数据是否存在或过期,没得则进行底层数据库查询并更新到缓存中
3.缓存淘汰策略

在缓存数据过多时需要使用某种淘汰算法决定淘汰哪些数据,常见的淘汰算法有以下几种:

  • FIFO(First in First Out,先进先出):判断被存储的时间,离目前最远的数据优先被淘汰
  • LRU(Least Recently Used,最近最少使用):判断缓存最近被使用的时间,距离当前时间最远的数据优先被淘汰
  • LFU(Least Frequently Used,最不经常使用):在一段时间内,被使用的次数最少的缓存优先被淘汰
缓存穿透

key 对应的数据在数据库中并不存在,每次针对此 key 的请求从缓存获取不
到,请求都会到数据库,从而可能压垮数据库。比如用一个不存在的用户 id 获
取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮
数据库。

解决办法:

1.使用布隆过滤器(BloomFilter)或者压缩 filter 提前拦截.

2.将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。这种情况我们一般会将空对象设置一个较短的过期时间.
3.拉黑该 IP 地址.
4.对参数进行校验,不合法参数进行拦截.

缓存击穿

某个 key 对应的数据库中存在,但在 redis 中的某个时间节点过期了,此时
若有大量并发请求过来,这些请求发现缓存过期,都会从后端 DB 加载数据并回
设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

解决办法:
1.热点数据设置永不过期
2.加上互斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们
可以在第一个查询数据的请求上使用一个互斥锁来锁住它
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将
数据放到 redis 缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.

缓存雪崩

缓存雪崩是指,在高并发情况下,大量的缓存失效,或者缓存层出现故障。于
是所有的请求都会达到数据库,数据库的调用量会暴增,造成数据库也会挂掉的
情况。
解决方法:
1.随机设置 key 失效时间,避免大量 key 集体失效。
setRedis(Key,value,time + Math.random() * 10000);
2.若是集群部署,可将热点数据均匀分布在不同的 Redis 库中也能够避免 key
全部失效问题
3.不设置过期时间
4.跑定时任务,在缓存失效前刷进新的缓存

总结
雪崩是大面积的key缓存失效;

穿透是redis里不存在这个缓存key;

击穿是redis某一个热点 key 突然失效

最终的受害者都是数据库。
对于“Redis 宕机,请求全部走数据库”这种情况,我们可以有以下的思路:
事发前:实现 Redis 的高可用(主从架构+Sentinel(哨兵),尽量避免 Redis
挂掉这种情况发生。
事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量
避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后:redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值