当前位置: 首页 > news >正文

SpringBoot集成Redis、SpringCache

1 Redis介绍

1.1 Redis作为缓存

由于Redis的存取效率非常高,在开发实践中,通常会将一些数据从关系型数据库(例如MySQL)中读取出来,并写入到Redis中,后续当需要访问相关数据时,将优先从Redis中读取所需的数据,以此,可以提高数据的读取效率,并且对一定程度的保护关系型数据库。

一旦使用Redis后,相关的数据就会同时存在于关系型数据和Redis中,即同一个数据有2份或更多(如果你使用了更多的Redis服务或其它数据处理技术),则可能出现数据不同步的问题!

1.2 数据一致性问题

如果最终出现了关系型数据库和Redis中的数据不同的问题,则称之为“数据一致性问题

  • 更新数据库成功 -> 更新缓存失败 -> 数据不一致

  • 更新缓存成功 -> 更新数据库失败 -> 数据不一致

1.3 redis缓存使用场景

  • 高频率访问的数据

    • 例如热门榜单
  • 修改频率非常低的数

    • 例如商品的类别
  • 对于数据的“准确性”要求不高的

    • 例如商品的库存余量(因为不管余量为多少,只有真正付款的时候才会去判断是否买到)

1.4 redis使用缓存的时机

关于在项目中应用Redis,首先考虑何时将MySQL中的数据读取出来并写入到Redis中!常见的策略有:

1 直接尝试从Redis中读取数据,如果Redis中无此数据,则从MySQL中读取并写入到Redis

从运行机制上,类似于单例模式中的懒汉式

2 当项目启动时,就直接从MySQL中读取数据并写入到Redis

从运行机制上,类似于单例模式中的饿汉式

这种做法通常称之为“缓存预热”当使用缓存预热的处理机制时,需要使得某段代码是项目启动时就自动执行的,可以自定义组件类实现AppliacationRunner接口,重写其中的run( )方法,此方法将在项目启动完成之后自动调用。


2 SpringBoot集成Redis

2.1 安装redis

地址Releases · microsoftarchive/redis · GitHub

2.2 在springboot中引入依赖

<!--集成redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.18</version>
</dependency>

2.3 添加redis配置信息

# spring配置
spring:redis:database: 0      # redis数据库索引(默认为0)host: localhostport: 6379timeout: 5000    # mspassword: 123456

2.4 编写redis配置类、工具类

/*** redis配置*/
@Configuration
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport
{@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());template.afterPropertiesSet();return template;}
}

工具类,后面引入即可使用 

/*** spring redis 工具类* **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisService
{@Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timeout 时间* @param timeUnit 时间颗粒度*/public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @return true=设置成功;false=设置失败*/public <T> boolean setCacheObjectIfAbsent(final String key, final T value){return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @param unit 时间单位* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 获取有效时间** @param key Redis键* @return 有效时间*/public long getExpire(final String key){return redisTemplate.getExpire(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key){return redisTemplate.hasKey(key);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** @param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 删除集合对象** @param collection 多个对象* @return*/public boolean deleteObject(final Collection collection){return redisTemplate.delete(collection) > 0;}/*** 缓存List数据** @param key 缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long setCacheList(final String key, final List<T> dataList){Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 获得缓存的list对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public <T> List<T> getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** @param key 缓存键值* @param dataSet 缓存的数据* @return 缓存数据的对象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 缓存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap){if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 缓存基本的对象,Integer、String、实体类等** @param key      缓存的 key* @param hasKey   缓存的 hasKey* @param hasValue 缓存的 hasValue* @return true=设置成功;false=设置失败*/public <T> boolean setCacheMapIfAbsent(final String key,final String hasKey, final T hasValue){return Boolean.TRUE.equals(redisTemplate.opsForHash().putIfAbsent(key,hasKey,hasValue));}/*** 获得缓存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** @param key Redis键* @param hKey Hash键* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** @param key Redis键* @param hKey Hash键* @return Hash中的对象*/public <T> T getCacheMapValue(final String key, final String hKey){HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 获取多个Hash中的数据** @param key Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 删除Hash中的某条数据** @param key Redis键* @param hKey Hash键* @return 是否成功*/public boolean deleteCacheMapValue(final String key, final String hKey){return redisTemplate.opsForHash().delete(key, hKey) > 0;}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}
}

3 SpringCache整合redis

3.1 添加依赖项,添加@EnableCaching

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.18</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><version>2.7.18</version>
</dependency>

3.2 配置yml文件 

重点是 spring.cache.type 这个配置,SpringCache是提供了多种缓存的实现方式,redis只是其中一种。

3.3 Redis配置类编写

在刚才的配置类,添加缓存的配置信息

    @Beanpublic RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {CacheProperties.Redis redisProperties = cacheProperties.getRedis();RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();// 序列化key、valueconfig= config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));if (redisProperties.getTimeToLive() != null) {config = config.entryTtl(redisProperties.getTimeToLive());}if (redisProperties.getKeyPrefix() != null) {config = config.prefixKeysWith(redisProperties.getKeyPrefix());}if (!redisProperties.isCacheNullValues()) {config = config.disableCachingNullValues();}if (!redisProperties.isUseKeyPrefix()) {config = config.disableKeyPrefix();}return config;}

3.4 在方法上面使用缓存

这里代表的是:根据Id查询品牌数据,然后开启缓存;缓存的名称的头部是brands开头,例如:brands:xx:xx。缓存的key使用传入进来的id进行设置;开启条件缓存,只有id是2的倍数,redis才进行缓存

这里第一次走数据库,第二次就没有数据库输出,redis里面正常保存数据

@Cacheable(value = "brands",key = "#id",condition = "#id%2==0")
public Brand getById(Integer id) {Brand brand = brandMapper.selectById(id);return brand;
}

可以看到实现效果是只有2,4,6这种偶数才进行了缓存

3.5 注解的介绍

3.5.1 Cacheable注解

将方法返回值加入缓存。同时在查询时,会先从缓存中取,若不存在才再发起对数据库的访问。在后续调用时(使用相同的参数),返回缓存中的值,而不必实际调用该方法。

注解参数:

1)value:指定缓存的名称,也可以说是缓存的prefix

例如:@Cacheable(value=”mycache”)

2)key:缓存的 key,可以为空,如果指定要按照 SpringEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合

例如:@Cacheable(value=”mycache”,key=”#user.id”);表示方法传入的参数User对象的id作为Key

3)condition:缓存的条件,可以为空,使用 SpringEL 编写,返回 true 或者 false,只有为 true 才进行缓存

例如:@Cacheable(value=”mycache”,condition=”#user.age>18”);表示方法传入的参数User对象,如果user.age>18才缓存

3.5.2 CachePut注解

就是你在更新数据库的时候,要同步更新缓存数据,就用这个注解。

3.5.3 CacheEvict注解

能够根据一定的条件对缓存进行清空,对于从缓存中删除过时或未使用的数据非常有用。与@Cacheable相反,@CacheEvict 定义了执行缓存逐出的方法(即充当从缓存中删除数据的触发器的方法)。

注解参数:跟Cacheable一样,拥有那三属性,同时还有一个allEntries属性,该属性默认为false,当为true时,删除该value所有缓存。

3.5.4 Caching注解

有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id–>user;username—>user;email—>user的缓存;此时就需要@Caching组合多个注解标签了。

@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
public User save(User user) {
}

3.5.5 CacheConfig注解

CacheConfig是一个类级别的注解。

所有的@Cacheable里面都有一个value的属性,这个注解能够指定该类下面所有@Cacheable的value值。

假设我在UserService类上面添加了这个注解,并且指定了value值,那么UserService类下的所有需要缓存的方法(例如:getById( )方法)都不需要添加value值了。

http://www.dtcms.com/a/277317.html

相关文章:

  • C++ 强制类型转换
  • 【操作系统】strace 跟踪系统调用(一)
  • (LeetCode 每日一题) 2410. 运动员和训练师的最大匹配数(排序、双指针)
  • es里为什么node和shard不是一对一的关系
  • Augment AI 0.502.0版本深度解析:Task、Guidelines、Memory三大核心功能实战指南
  • 将 NumPy 数组展平并转换为 Python 列表
  • 1.1.5 模块与包——AI教你学Django
  • OpenLayers 入门指南【二】:坐标系与投影转换
  • 把 DNA 当 PCIe:一条 365 nt 链实现 64 Gbps 片上光互连——基于链式 Förster 共振的分子级波分复用链路
  • 理解 Robots 协议:爬虫该遵守的“游戏规则”
  • MySQL逻辑删除与唯一索引冲突解决
  • M00224-小范围疫情防控元胞自动机模拟matlab
  • 【unitrix】 5.1 第二套类型级二进制数基本结构体(types2.rs)
  • 深入解析Hadoop架构设计:原理、组件与应用
  • OpenLayers使用
  • (2)从零开发 Chrome 插件:实现 API 登录与本地存储功能
  • 音视频学习(三十八):像素与位深
  • 打破并发瓶颈:虚拟线程实现详解与传统线程模型的性能对比
  • QuickUnion优化及Huffman树
  • JS红宝书pdf完整版
  • JAVA生成PDF(itextpdf)
  • 为什么玩游戏用UDP,看网页用TCP?
  • [2025CVPR]GNN-ViTCap:用于病理图像分类与描述模型
  • MyBatis框架进阶指南:深入理解CRUD与参数映射
  • Redis集群方案——哨兵机制
  • 无需付费即可利用AI消除音频噪声和生成字幕
  • 《Linux篇》自动化构建-make/Makefile
  • GraphRAG Docker化部署,接入本地Ollama完整技术指南:从零基础到生产部署的系统性知识体系
  • AI抠图软件,本地运行超快速
  • 水往低处流,人也往低处走