目录
一、缓存基础概念
了解Redis缓存之前,先来了解一下缓存的相关内容
1. 什么是缓存
缓存就是数据交换的缓冲区(称作Cache),是临时存贮数据(使用频繁的数据)的地方。当用户查询数据,首先在缓存中寻找,如果找到了则直接执行;如果找不到则去数据库中查找。
缓存的本质就是用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据,减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度。
2. 缓存的优缺点
优点:加快响应速度,减少对数据库的读操作,降低数据库访问压力
缺点:内存的存储容量小。可能出现缓存数据与数据库数据不一致的情况。内存在断电后数据会丢失。
3. 使用缓存的必要性
远端服务器上,客户端请求量大,尤其是大量的读取操作,都需要访问到数据库,给数据库造成较大的压力,自然响应速度会变慢。那么如果把一些热点数据存到内存中,内存的速度很快,可以达到及时响应的目的,同时缓解数据库压力。
缓存本质就是将数据存储在内存中,当数据没有发生本质变化的时候,应尽量避免直接连接数据库进行查询,因为并发高时很可能会将数据库压塌,而是应去缓存中读取数据,只有缓存中未查找到时再去数据库中查询,这样就大大降低了数据库的读写次数,增加系统的性能和能提供的并发量。
二、Redis知识概述
1. Redis是什么?
Redis 是一个高性能的 Key-Value 开源数据库, 是一个非关系型(NoSQL)的数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案。但它不能替代关系型数据库,只能作为特定环境下的扩充。常用作缓存使用 (cache)
2. Redis的优缺点,为什么用作缓存
优点:
- 性能极高:10w次/s的读写速度。
- 数据持久化:可以将内存中的数据持久化在磁盘中,当宕机或者故障重启时,可以再次加载进如 Redis,从而不会或减少数据的丢失。
- 丰富的数据类型:Redis 不仅支持简单的 Key/Value 类型的数据,Value数据支持多种类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)。
- 支持高可用:Redis 支持 master\slave 主\从机制、sentinel 哨兵模式、cluster 集群模式,保证了 Redis 运行的稳定和高可用性。
缺点:
-
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写。
-
适合的场景主要局限在较小数据量的高性能操作和运算上。
3. Redis 支持的数据类型
- 字符串(string)
- 哈希表(hash)
- 列表(list)
- 集合(set)
- 有序集合(zset)
应用:
1. 排行榜,利用zset可以方便的实现排序功能;
2. 计数器,利用redis中原子性的自增操作,可以统计到阅读量,点赞量等功能;
3. 简单消息队列,list存储结构,满足先进先出的原则,可以使用lpush/rpop或rpush/lpop;
4. session共享,分布式系统中,可以利用redis实现session共享。spring官方提供的分布式解决方案Spring Session就是利用redis 实现的。
4. Redis持久化机制有哪些?
Redis 提供两种持久化机制: RDB 和 AOF
RDB(Redis DataBase):
在指定的时间间隔内将内存中的数据集快照写入磁盘,恢复时是键快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束后,再用这个临时文件替换上次持久化好的文件。
优点:
-
只有一个文件 dump.rdb,恢复操作简单
-
性能较高,fork 子进程进行写操作,主进程继续处理命令
-
大数据集比 AOF 的恢复效率高
缺点:
-
数据安全性低,RDB 是每间隔一段时间进行持久化,若期间 redis 发生故障,可能会发生数据丢失
AOF(Append-only file):
指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储,保存为 aof 文件。
这个方法是每执行一条写操作命令,就将该命令以追加的方式写入到 AOF 文件,然后在恢复时,以逐一执行命令的方式来进行数据恢复。
优点:
-
数据安全,aof 持久化可以配置 appendfsync 属性为 always,记录每个命令操作到 aof 文件中一次;通过 append 模式写文件,即使中途服务器宕机,也可以通过 redis-check-aof 工具解决数据一致性问题
-
AOF 机制的 rewrite 模式,AOF 文件没被 rewrite 之前可以进行处理,如删除文件中的 flushall 命令
缺点:
-
AOF 的持久化文件比 RDB 大,恢复速度慢
如果数据比较重要,使用 AOF 方式备份数据,设置合理的备份频率。
5. 常见问题
1. 缓存穿透
缓存穿透:缓存和数据库中都没有的数据,而用户不断发起请求。 由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方法:
- 空值缓存:一种比较简单的解决办法,在第一次查询完不存在的数据后,将该key与对应的空值也放入缓存中,只不过设定为较短的失效时间,例如几分钟,这样则可以应对短时间的大量的该key攻击
- bloom过滤器(bloom filter):可以用来告诉你 “某样东西一定不存在或者可能存在”。类似于哈希表的一种算法,用所有可能的查询条件生成一个bitarray,在进行数据库查询之前会使用这个bitmap进行过滤,如果不在其中则直接过滤,从而减轻数据库层面的压力。
2. 缓存击穿
缓存击穿: 热点数据缓存失效,流量瞬间直达数据库。缓存击穿实际上是缓存雪崩的一个特例,缓存击穿是指缓存中没有但数据库中有的数据。
解决方法:
- 设置二级缓存,或者设置热点缓存永不过期,需要根据实际情况进行配置。
- 通过对应用层加锁,避免所有流量同时访问数据库。使用互斥锁,在执行过程中,如果缓存过期,那么先获取分布式锁,在执行从数据库中加载数据,如果找到数据就存入缓存,没有就继续该有的动作,在这个过程中能保证只有一个线程操作数据库,避免了对数据库的大量请求。
3. 缓存雪崩
缓存雪崩:大量缓存数据在同一时间段失效,缓存服务挂掉或者热点缓存失效,所有请求都去查数据库。
解决方法:
- 缓存组件设计高可用:缓存高可用是指存储缓存的组件的高可用,能够防止单点故障、机器故障、机房宕机等一系列问题。例如 Redis sentinel 和 Redis Cluster,都实现了高可用。
- 交错失效时间:设置缓存过期时间一定的随机分布,避免集中在同一时间缓存失效。
- 请求限流与服务熔断降级机制,限制服务请求次数,当服务不可用时快速熔断降级。
4. 缓存一致性问题
- 更熟数据库成功 -> 更新缓存失败 -> 数据不一致
- 更新缓存成功 -> 更新数据库失败 -> 数据不一致
- 更新数据库成功 -> 淘汰缓存失败 -> 数据不一致
三、SpringBoot整合Redis
1. 使用 Spring data redis
1. 引入redis依赖
在pom.xml文件中引入Redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 项目启动类添加注解
增加注解@EnableCaching,开启缓存功能
3. 配置Redis数据库
4. 创建配置类
5. 操作Redis
SpringBoot提供了两个bean来操作redis,分别是RedisTemplate 和 StringRedisTemplate,这两者的主要区别如下:
RedisTemplate使用的是JdkSerializationRedisSerializer ,存入数据会将数据先序列化成字节数组然后在存入Redis数据库;
StringRedisTemplate使用的是StringRedisSerializer。
2. 使用 SpringCache 的注解
1. 常用注解
- @Cacheable: 用于对方法返回结果进行缓存,如果已经存在该缓存,则直接从缓存中获取,缓存的key可以从入参中指定,缓存的 value 为方法返回值。
- @CachePut: 无论是否存在该缓存,每次都会重新添加缓存,缓存的key可以从入参中指定,缓存的value为方法返回值,常用作于更新。
- @CacheEvict: 用于清除缓存
2. 注解参数
-
value
:用来指定缓存组件的名字,缓存文件的名称。 -
key
:缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用Spring表达式语言( spEL 表达式)来编写)。 -
cacheManager
:可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。 -
condition
:可以用来指定符合条件的情况下才缓存。 -
unless
:否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。 -
sync
:是否使用异步模式。默认是方法执行完,以同步的方式将方法返回的结果存在缓存中。
3. 自动缓存
@Cacheable
可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。
如果添加了@Cacheable
注解,那么方法被调用后,值会被存入redis,下次再调用的时候会直接从redis中取值返回。
四、Redis的高可用方案
所谓的高可用,也叫HA(High Availability),是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。
如果在实际生产中,如果redis只部署一个节点,当机器故障时,整改服务都不能提供服务了。这就是我们常说的单点故障。如果redis部署了多台,当一台或几台故障时,整个系统依然可以对外提供服务,这样就提高了服务的可用性。
redis高可用有三种模式:主从模式,哨兵模式,集群模式。
1. 主从模式
主要是基于Redis的主从复制特性架构的。通常我们会设置一个主节点,N个从节点;默认情况下,主节点负责处理使用者的IO操作,而从节点则会对主节点的数据进行备份,并且也会对外提供读操作的处理。
特点:
- 主从模式下,当某一节点损坏时,因为其会将数据备份到其它Redis实例上,这样做在很大程度上可以恢复丢失的数据;
- 主从模式下,可以保证负载均衡;
- 主从模式下,主节点和从节点是读写分离的。使用者不仅可以从主节点上读取数据,还可以很方便的从从节点上读取到数据,这在一定程度上缓解了主机的压力;
- 从节点也是能够支持写入数据的,只不过从从节点写入的数据不会同步到主节点以及其它的从节点下。
优点:
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
- 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务依然必须由Master来完成;
- Master和Slave都是以非阻塞的方式提供服务。
缺点:
- 主从模式下的Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复;
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
- Redis的主节点和从节点中的数据是一样的,降低的内存的可用性。
2. 哨兵模式(sentinel)
是基于主从模式做的一定变化,它能够为Redis提供高可用性。
哨兵模式的核心还是主从复制。只不过相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制——从所有的从节点竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个sentinel进程。
sentinel特点:
-
监控:它会监听主服务器和从服务器之间是否在正常工作。
-
通知:它能够通过API告诉系统管理员或者程序,集群中某个实例出了问题。
-
故障转移:它在主节点出了问题的情况下,会在所有的从节点中竞选出一个节点,并将其作为新的主节点。
-
提供主服务器地址:它还能够向使用者提供当前主节点的地址。这在故障转移后,使用者不用做任何修改就可以知道当前主节点地址。
优点:
- 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
- 主从可以自动切换,系统更健壮,可用性更高。
缺点:
- 具有主从模式的缺点,每台机器上的数据是一样的,内存的可用性较低。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
3. 集群模式(cluster)
集群模式,实现了 Redis 的分布式存储,对数据进行分片,也就是说每台主机Redis 节点上存储不同的内容,节点间数据共享,可动态调整数据分布。
Redis Cluster是Redis 3.0以后才正式推出,时间较晚。