Spring---缓存数据-Redis

本文介绍了缓存的基本原理,包括缓存的收益与成本、常见的缓存更新策略等,并详细阐述了如何在Spring框架中使用SpringCache及Redis进行缓存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

0. 简介

0.1 缓存收益和场景

0.2 缓存更新策略

1. 概述

2. Spring Cache

2.1 Spring与缓存实现进行集成

2.1.1 启用对缓存的支持

2.1.2 缓存管理器

2.1.3 为方法添加注解以支持缓存

3. 使用Redis缓存

3.1 添加依赖

3.2 配置连接池

3.3 Redis连接工厂 

3.4 配置RedisTemplate       

3.5 key 和 value 的序列化器

3.6 启用对缓存的支持

3.7 配置缓存管理器

3.8 RedisTemplate API 


0. 简介

0.1 缓存收益和场景

缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要。

下图左侧为客户端直接调用存储层的架构,右侧为比较典型的缓存层+存储层架构

 缓存比较常用的选型,缓存层选用Redis,存储层选用MySQL。

缓存加入后带来的收益和成本。

       收益

  1. 加速读写:因为缓存通常都是全内存的,而存储层通常读写性能不够强悍(例如MySQL),通过缓存的使用可以有效地加速读写,优化用户体验。
  2. 降低后端负载:帮助后端减少访问量和复杂计算(例如很复杂的SQL语句),在很大程度降低了后端的负载

       成本

  1. 数据不一致性:缓存层和存储层的数据存在着一定时间窗口的不一致性,时间窗口跟更新策略有关。
  2. 代码维护成本:加入缓存后,需要同时处理缓存层和存储层的逻辑,增大了开发者维护代码的成本。
  3. 运维成本:以Redis Cluster为例,加入后无形中增加了运维成本。

缓存的使用场景基本包含如下两种:

  1. 开销大的复杂计算:以MySQL为例子,一些复杂的操作或者计算(例如大量联表操作、一些分组计算),如果不加缓存,不但无法满足高并发量,同时也会给MySQL带来巨大的负担。
  2. 加速请求响应:即使查询单条后端数据足够快(例如select*from tablewhere id=),那么依然可以使用缓存,以Redis为例子,每秒可以完成数万次读写,并且提供的批量操作可以优化整个IO链的响应时间。

0.2 缓存更新策略

缓存中的数据会和数据源中的真实数据有一段时间窗口的不一致,需要利用某些策略进行更新,下面会介绍几种主要的缓存更新策略。

  • LRU/LFU/FIFO算法剔除

剔除算法通常用于缓存使用量超过了预设的最大值时候,如何对现有的数据进行剔除。例如Redis使用maxmemory-policy这个配置作为内存最大值后对于数据的剔除策略。

  • 超时剔除

通过给缓存数据设置过期时间,在过期时间后自动删除,例如Redis提供的expire命令。如果业务可以容忍一段时间内,缓存层数据和存储层数据不一致,那么可以为其设置过期时间。在数据过期后,再从真实数据源获取数据,重新放到缓存并设置过期时间。例如一个视频的描述信息,可以容忍几分钟内数据不一致,但是涉及交易方面的业务,后果可想而知。

  • 主动更新

应用方对于数据的一致性要求高,需要在真实数据更新后,立即更新缓存数据。例如可以利用消息系统或者其他方式通知缓存更新。

建议:

  1. 低一致性业务建议配置最大内存和淘汰策略的方式使用。
  2. 高一致性业务可以结合使用超时剔除和主动更新,这样即使主动更新出了问题,也能保证数据过期时间后删除脏数据;

1. 概述

     缓存(Caching)可以存储经常会用到的信息,这样每次需要的时候,这些信息都是立即可用的。

     常用的缓存数据库:

  • Redis   使用内存存储(in-memory)的非关系数据库,字符串、列表、集合、散列表、有序集合,每种数据类型都有自己的专属命令。另外还有批量操作(bulk operation)和不完全(partial)的事务支持 、发布与订阅、主从复制(master/slave replication)、持久化、脚本(存储过程,stored procedure)。 效率比ehcache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。
  • memcached   使用内存存储的键值缓存,键值之间的映射、创建命令、读取命令、更新命令、删除命令以及其他几个命令。为提升性能而设的多线程服务器。memcache在客户端中实现分布式缓存,通过分布式算法指定目标数据的节点。
  • ehcache    纯java实现,缓存在内存中,可持久化到硬盘,效率高于memcache;但是缓存共享麻烦,集群分布式应用不方便。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适。

2. Spring Cache

    Spring 缓存的实现是通过创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)。根据所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值

2.1 Spring与缓存实现进行集成

Spring 能都与多个流行的缓存实现进行集成,实现步骤如下:

2.1.1 启用对缓存的支持

  • Java 配置 注解驱动缓存

使用注解:@EnableCaching

  • XML 声明的缓存

----<cache:annotation-driven>

2.1.2 缓存管理器

缓存管理器是Spring缓存抽象的核心,它能够与多个流行的缓存实现进行集成。Spring3.1配置了五个缓存管理器实现:

  • SimpleCacheManager
  • NoOpCacheManager
  • ConcurrentMapCacheManager(它的缓存存储是基于内存的,所以它的生命周期是与应用关联的,对于生产级别的大型企业级应用程序,这可能并不是理想的选择)
  • CompositeCacheManager(系统同时使用多个缓存管理器集成)
  • EhCacheCacheManager

除了核心的Spring框架,Spring Data又提供了两个缓存管理器:

  • RedisCacheManager--Spring Data Redis提供
  • GemfireCacheManager

2.1.3 为方法添加注解以支持缓存

Spring 提供的四个注解来声明缓存规则:

  1. @Cacheable会条件性地触发对方法的调用,这取决于缓存中是不是已经有了所需要的值。
  2. @CachePut采用了一种更为直接的流程。带有@CachePut注解的方法始终都会被调用,而且它的返回值也会放到缓存中。

@Cacheable 和 @CachePut 一些共有的属性:

  • value:value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。主要作用是给 cache 取个名称!个人觉得,主要是为了在Spring 容器中可以更好的管理缓存对象。
  • condition
    • 调用方法前:如果SPEL表达式的值为false的话,将不会去走缓存。
    • 调用方法后:如果SPEL表达式的值为false的话,将不会将返回值放在缓存中。。
  • unless
    • 调用方法前:不进行判断。
    • 调用方法后:如果SPEL表达式的值是true的话,将不会将返回值放在缓存中。
  • key:指定的key的值就是我们要保存到缓存数据库中的key的值,但是这个key的指定有自己的一套方式,如下:

另外,需要说明的是 #result 不能用在@Cacheable 上是因为 #result 表示的是 方法调用的返回值。但是@Cacheable 可能不走方法,可能不会有返回值。所以就不太适用了。但是!但是!#result 可以用在@Cacheable的 unless 属性上,因为 unless 是在方法返回的时候再判断的,所以一定会有返回值!

3. @CacheEvict 的一些属性,指定了哪些缓存数目应该被移除: 

@CacheEvict 主要用在要移除一个数据条目的时候,同时移除这个数据条目在缓存中的 key 和 value。

4. @Cacheing

@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关注解,其中拥有属性:cacheable、put、evict。

@Caching(cacheable=@Cacheable("users"),evict={@CacheEvict("cache2"),@CacheEvict(value="cache3",allEntries=true)})

3. 使用Redis缓存

 Spring Data Redis包含了多个模板实现,用来完成Redis数据库的数据存取功能。

 <!-- 配置连接池 -->
       <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
           <property name="maxIdle" value="50"/>
           <property name="maxTotal" value="100"/>
           <property name="maxWaitMillis" value="20000"/>
       </bean>
                  
       <!-- 配置连接工厂 -->
       <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
           <property name="hostName" value="localhost"/>
           <property name="port" value="6379"/>
           <property name="poolConfig" ref="poolConfig"/>
       </bean>
    
    <!-- 键值序列化器 -->
       <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
       <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
  
    <!-- 配置redisTemplate -->
       <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
           <property name="connectionFactory" ref="connectionFactory"/>
           <!-- 设置默认的序列化器为字符串序列化 -->
           <property name="defaultSerializer" ref="stringRedisSerializer"/>
           <property name="keySerializer" ref="stringRedisSerializer"/>
           <property name="valueSerializer" ref="jdkSerializationRedisSerializer"/>
           <property name="hashKeySerializer" ref="stringRedisSerializer"/>
           <property name="hashValueSerializer" ref="jdkSerializationRedisSerializer"/>
       </bean>
       
       <!-- 使用注解驱动,其中属性cache-manager默认值为cacheManager -->
       <cache:annotation-driven cache-manager="redisCacheManager"/>
       
       <!-- 定义缓存管理器 -->
       <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
           <!-- 通过构造方法注入redisTemplate -->
           <constructor-arg index="0" ref="redisTemplate"/>
           <!-- 定义默认超时时间 -->
           <property name="defaultExpiration" value="600"/>
           <!-- 缓存管理器名称 -->
           <property name="cacheNames">
               <list>
                   <value>redisCacheManager</value>
               </list>
           </property>
       </bean>

3.1 添加依赖

在Spring中使用Redis需要jedis.jar和spring-data-redis.jar

    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>1.8.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.9.0</version>
    </dependency>

 

3.2 配置连接池

配置了Redis数据库连接池的最大闲置连接数、最大连接数和最长等待时间;

3.3 Redis连接工厂 

redis连接工厂会生成到Redis数据库服务器的连接。配置了Redis数据库的主机和端口号,同时引入了之前配置的连接池;

有四种Redis连接工厂:

  1. JedisConnetionFactory
  2. JredisConnectionFactory
  3. LettuceConnectionFactory
  4. SrpConnectionFactory

3.4 配置RedisTemplate       

redisTemplate封装了对redis的操作,要操作redis,则肯定要引入上面配置好的连接工厂和序列化器;

  • Spring Data Redis提供了两个模版
  1. RedisTemplate
  2. StringRedisTemplate

RedisTemplate 支持 Object类型 的key 和 Object类型 的value。而StringRedisTemplate则采用更简单粗暴的方式,只支持 String类型 的key 和String类型 的value。 

  • 需要注入Redis连接工厂bean;
  • 需要注入序列化器bean

3.5 key 和 value 的序列化器

什么是序列化器呢?当某个条目保存到Redis key-value存储的时候,key和value都会使用Redis的序列化器进行序列化。由于Redis只能提供基于字符串型的操作,而在java中是以类和对象为主,序列化器的作用是将java对象和Redis字符串相互转换,使得可以将java对象序列化为字符串存入Redis,同时也可以取出Redis序列化过的字符串转换成java对象

Spring Data Redis提供了多个这样的序列化器,包括:

  • GenericToStringSerializer:使用Spring转换服务进行序列化;
  • JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
  • Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
  • JdkSerializationRedisSerializer:使用Java序列化;
  • OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化用于XML序列化;
  • StringRedisSerializer:序列化String类型的key和value。

RedisTemplate会使用JdkSerializationRedisSerializer,这意味着key和value都会通过Java进行序列化。StringRedisTemplate默认会使用StringRedisSerializer。

RedisTemplate 的 key 和 value 经过序列化存储可能会让你觉得奇怪,类似如下:

这是正常的,因为使用的是 java 的序列化,如果想要看起来比较舒服的,可以用 StringRedisSerializer 序列成字符串的样子。

3.6 启用对缓存的支持

注解驱动的缓存:<cache:annotation-driven>会启动注解驱动的缓存,会创建一个切面(aspect),并触发Spring缓存注解的切点(pointcut)。根据所使用的注解以及缓存的状态,切面会从缓存中获取数据,将数据添加到缓存中,或者从缓存中移除某个值。

3.7 配置缓存管理器

有了对redis的操作,接下来就是要将spring缓存机制和redis进行结合(即配置缓存管理器),缓存管理器中我们注入了redisTemplate,同时设置了超时时间和管理器的名称。

3.8 RedisTemplate API 

Spring Data Redis 提供了一套API 友好的操作Redis,如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值