springboot中redis的使用

这篇具有很好参考价值的文章主要介绍了springboot中redis的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

springboot中redis的使用

一、springboot整合redis
1.1 基本使用

1、导入依赖

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

2、添加redis配置

spring.redis.password=123456

此时已经可以在代码中使用redisTemplate对象了。例如:

@Service
public class DepartmentServiceImpl implements DepartmentService {
    @Resource
    private DepartmentDAO departmentDAO;
    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public List<Department> findAll() {
        // json处理的对象
        ObjectMapper mapper = new ObjectMapper();
        // 1、查询缓存
        // 定义一个key(唯一、尽量不会被推导)
        // 使用一个唯一的名字 + 参数值 + md5
        String key = MD5Utils.md5("findAllDepartments");
        // 得到redisTemplate中的操作对象
        BoundValueOperations<String, String> boundValueOps = redisTemplate.boundValueOps(key);
        // 得到缓存中的值
        String value = boundValueOps.get();
        System.out.println("value===" + value);
        // 2、判断缓存是否有该数据
        if (value != null){
            // 3、如果有则返回该数据
            System.out.println("查询缓存");
            List<Department> list = null;
            try {
                list = mapper.readValue(value, new TypeReference<List<Department>>(){});
            }catch (Exception e){
                e.printStackTrace();
            }
            return list;
        }else{
            // 4、如果没有,则去数据库中查询,并保存到缓存中
            System.out.println("查询数据库");
            List<Department> departments = departmentDAO.findAll();
            if (departments != null && departments.size() > 0){
                // 转换成json格式
                try {
                    String string = mapper.writeValueAsString(departments);
                    // 保存到缓存中
                    boundValueOps.set(string);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            return departments;
        }
    }
}

但是,查看redis中存储的数据发现,key和value前面都有一段看似乱码的数据,而且中文也进行编码。为了解决该问题,需要参考前面ssm中整合redis的方案,对redis的key和value进行配置。

3、添加redis的设置

@Configuration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

再使用前面的service代码进行测试,发现上述问题已解决。

1.2 使用缓存注解

直接编写redis的逻辑,相对代码比较复杂,而且逻辑基本固定,可以使用缓存注解来简化该流程。

spring框架提供一套缓存注解:

@Cacheable:表示当前查询结果会使用缓存。(如果有缓存,直接使用,否则进行数据库查询并放入缓存)
@CachePut:表示当前操作的数据的结果会更新到缓存中。(如果修改的数据在缓存存在,会同时修改缓存中的数据)
@CacheEvict:表示如果删除的数据在缓存中,也会对应的删除。

1、添加缓存注解的配置

@Configuration // 配置文件
@EnableCaching // 允许缓存使用,开启缓存的配置,需要继承CachingConfigurerSupport类
public class MyRedisConfig extends CachingConfigurerSupport {
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory){
        RedisSerializer<String> keyRedisSerializer = new StringRedisSerializer(); // redis的key序列化方式
        Jackson2JsonRedisSerializer valueRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // redis的value的序列化

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        valueRedisSerializer.setObjectMapper(om);

        //配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ZERO) // 默认生存时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keyRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueRedisSerializer))
                .disableCachingNullValues();

        //缓存配置map
        Map<String,RedisCacheConfiguration> cacheConfigurationMap=new HashMap<>();
        //自定义缓存名,后面使用的@Cacheable的CacheName
        cacheConfigurationMap.put("myRedis",config);
//        cacheConfigurationMap.put("default",config);

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(cacheConfigurationMap)
                .build();

        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4、使用

@Service
public class EmployeeServiceImpl implements EmployeeService{
    @Resource
    private EmployeeDAO employeeDAO;

    @Override
    public PageInfo findAll(Integer page) {
        PageHelper.startPage(page, 10);
        List<Employee> list = employeeDAO.findAll();
        PageInfo info = new PageInfo(list);
        return info;
    }

    @Override
    // @Cacheable将查询结果进行缓存
    // key的定义:
    // T()里面写一个类型,表示当作一个类处理,后面调用该类方法
    //  如果是字符串,需要使用单引号
    // 如果要使用方法中的参数,需要在变量名前面加上#
    @Cacheable(cacheNames = "myRedis", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #id)")
    public Employee findById(Integer id) {
        System.out.println("进入方法,去数据库查询");
        return employeeDAO.findById(id);
    }

    @Override
    public void save(Employee employee) {
          employeeDAO.save(employee);
    }

    @Override
    // @CachePut将返回值作为数据替换掉原来的缓存数据
    @CachePut(cacheNames = "myRedis", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #employee.id)")
    public Employee update(Employee employee) {
        employee.setUpdatetime(new Date());
        employeeDAO.update(employee);
        return employee;
    }

    @Override
    // @CacheEvict删除缓存
    @CacheEvict(cacheNames = "myRedis", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #id)")
    public void delete(Integer id) {
          employeeDAO.delete(id);
    }
}
二、redis做缓存过程中的难点以及解决方案
1、问题

为了让缓存使用方式通用,选择了使用注解的方案,即@Cacheable, @CachePut等。

思考一下缓存中需要处理的问题,在使用注解过程中是否得以解决。

问题如下:缓存击穿,缓存穿透,大量的key过期导致的雪崩问题。

2、问题的思考

1、缓存击穿问题的原因是同一时间大量的请求访问,导致访问到数据库,可以使用缓存预热来解决。或者对请求使用削峰。【已解决】

2、穿透问题的原因是无法缓存null值,导致每次都访问数据库。解决问题的方案需要缓存空值。而且需要设置一个较短的过期时间,相较于非空数据的时间要短很多。【未解决】

3、大量key过期导致的缓存雪崩问题。原因主要是由于缓存时间设置一致。解决方案是设置不同的时间。【未解决】

3、思考的过程以及解决方案

1、在@Cache注解中使用unless属性。

@Cacheable(value=“XXX”,key=“#info”,unless = “#result==null”)

上面的办法能够解决空值出现的异常问题,但是又出现了新的问题------缓存穿透,每一次都会访问数据库。

2、使用自定义的AOP方案,切入到缓存存储的时机,自定义过程。

【方式比较麻烦,而且中间还会产生aop失效问题https://my.oschina.net/guangshan/blog/1807721】

3、自己使用redisTemplet的opsForValue().set()方法。

可能需要写很多次代码,也比较麻烦,而且没有充分利用到@Cacheable注解。

4、我还是希望站在巨人的肩膀上

找到了一个分布式落地方案:layering-cache

https://my.oschina.net/xiaolyuh/blog/2245782

5、在springboot中的使用

1、导入启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 启动器-->
<dependency>
    <groupId>com.github.xiaolyuh</groupId>
    <artifactId>layering-cache-starter</artifactId>
    <version>3.1.8</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>
<!--默认的键值序列化方式-->
<dependency>
    <groupId>com.esotericsoftware.kryo</groupId>
    <artifactId>kryo</artifactId>
    <version>2.21</version>
</dependency>
<!--默认使用的json解析-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

2、在application.properties中添加配置

#layering-cache 配置
layering-cache.stats=true
# 缓存命名空间,如果不配置取 "spring.application.name"
layering-cache.namespace=layering-cache-web

# redis单机
layering-cache.redis.database=0
layering-cache.redis.host=127.0.0.1
layering-cache.redis.port=6379
layering-cache.redis.password=
# redis集群
#layering-cache.redis.password=
#layering-cache.redis.cluster=127.0.0.1:6379,127.0.0.1:6378

3、配置类中添加注解@EnableLayeringCache启用layering-cache

@SpringBootApplication
@EnableLayeringCache // 启用多级缓存框架
public class LayeringCacheStartDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(LayeringCacheStartDemoApplication.class, args);
    }
}

4、使用注解配置

参考:https://my.oschina.net/xiaolyuh/blog/2245782

不需要使用MyRedisConfig配置类。文章来源地址https://www.toymoban.com/news/detail-583054.html

import com.github.xiaolyuh.annotation.*;

@Service
public class EmployeeServiceImpl implements EmployeeService {
    @Resource
    private EmployeeDAO employeeDAO;

    @Override
    public List<Employee> findAll() {
        return employeeDAO.findAll();
    }

    @Override
    @Cacheable(value = "employee:info", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #id)"
            , depict = "用户信息缓存", enableFirstCache = true,
            firstCache = @FirstCache(expireTime = 400, timeUnit = TimeUnit.SECONDS),
            secondaryCache = @SecondaryCache(expireTime = 1000, preloadTime = 200,
                    forceRefresh = true, timeUnit = TimeUnit.SECONDS, isAllowNullValue = true, magnification = 100))
    public Employee findById(Integer id) {
        System.out.println("查询数据库");
        return employeeDAO.findById(id);
    }

    @Override
    @CachePut(value = "employee:info", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #id)"
            , depict = "用户信息缓存", enableFirstCache = true,
            firstCache = @FirstCache(expireTime = 400, timeUnit = TimeUnit.SECONDS),
            secondaryCache = @SecondaryCache(expireTime = 1000, preloadTime = 200,
                    forceRefresh = true, timeUnit = TimeUnit.SECONDS, isAllowNullValue = true, magnification = 100))
    public Employee update(Employee employee) {
        employee.setUpdatetime(new Date());
        employeeDAO.update(employee);
        return findById(employee.getId());
    }

    @CacheEvict(value = "employee:info", key = "T(com.qf.sbems.utils.MD5Utils).md5('EmployeeService_findById' + #id)")
    public void delete(Integer id) {
//        employeeDAO.delete(id);
    }
}

到了这里,关于springboot中redis的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 0001Java程序设计-springboot基于微信小程序批发零售业商品管理系统

    文章目录 **摘 要** **目录** 系统实现 开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅🐧@裙:772162324 摘 要 本毕业设计的内容是设计并且实现一个基于微信小程序批发零售业商品管理系统。它是在Windows下,以MYSQL为数据库开发平台,java技术和Tomcat网络信息服务

    2024年02月04日
    浏览(54)
  • 基于Java+spring boot的旅游景区小程序的设计与实现

    👇其他专栏推荐👇: 计算机毕业设计100套 微信小程序项目实战 java项目实战 🥰需要源码可以滴滴我🥰 目录 摘要 1 绪论 1.1 国内外研究现状 1.2 研究意义 2 开发技术介绍 2.1 Java语言 2.2 spring boot框架 2.3 微信小程序 3 系统分析 3.1 可行性分析 3.1.1 经济可行性 3.1.2 技术可行性

    2024年02月04日
    浏览(41)
  • 0003Java安卓程序设计-springboot基于Android的学习生活交流APP

    编程技术交流、源码分享、模板分享、网课教程 🐧裙:776871563 网络的广泛应用给生活带来了十分的便利。所以把学习生活交流管理与现在网络相结合,利用java技术建设学习生活交流APP,实现学习生活交流的信息化。则对于进一步提高学习生活交流管理发展,丰富学习生活交

    2024年02月05日
    浏览(49)
  • 【Java程序设计】【C00271】基于Springboot的地方美食分享网站(有论文)

    这是一个基于Springboot的地方美食分享网站 本系统分为系统功能模块、管理员功能模块、以及用户功能模块。 系统功能模块:网站首页可以查看首页,外国美食,中式美食,热门菜品,论坛,新闻资讯,留言板,个人中心,后台管理等内容 管理员功能模块:管理员登录系统后

    2024年02月19日
    浏览(44)
  • java毕业设计——基于Java+Spring Boot+MySQL的论文选题系统设计与实现(毕业论文+程序源码)——论文选题系统

    大家好,今天给大家介绍基于Java+Spring Boot+MySQL的论文选题系统设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦。需要下载开题报告PPT模板及论文答辩PPT模板等的小伙伴,可以进入我的博客主页查看左侧最下面栏目中的自助下载方法哦 文章目录: 伴随着我国高

    2024年01月21日
    浏览(57)
  • 基于springboot+uniapp的图书馆座位预约小程序(Java毕业设计)

     大家好,我是DeBug,很高兴你能来阅读!作为一名热爱编程的程序员,我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里,我将会结合实际项目经验,分享编程技巧、最佳实践以及解决问题的方法。无论你是初学者还是有一定经验的程序员,我都希望能够为你

    2024年01月21日
    浏览(49)
  • 基于Java+SpringBoot+Vue+uniapp微信小程序外卖系统设计和实现

    博主介绍 : ✌ 全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟 2022-2024年

    2024年02月13日
    浏览(143)
  • 【java毕业设计】 基于Spring Boot+mysql的房屋租赁系统设计与实现(程序源码)-房屋租赁系统

    大家好,今天给大家介绍基于Spring Boot+mysql的房屋租赁系统设计与实现,本论文只截取部分文章重点,文章末尾附有本毕业设计完整源码及论文的获取方式。更多毕业设计源码可订阅查看上方【毕业设计】专栏获取哦。 社会的发展和科学技术的进步,互联网技术越来越受欢迎

    2024年04月23日
    浏览(44)
  • 基于Java+SpringBoot+Vue+uniapp微信小程序零食商城系统设计和实现

     博主介绍 : ✌ 全网粉丝20W+,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟  java项

    2024年02月03日
    浏览(56)
  • 【附源码】JAVA计算机毕业设计应聘小程序(springboot+mysql+开题+论文)

    本系统 (程序+源码) 带文档lw万字以上   文末可获取一份本项目的java源码和数据库参考。 研究背景 随着信息技术的快速发展,互联网已经深入到人们生活的方方面面,特别是在求职招聘领域,传统的线下招聘方式已经无法满足现代社会的需求。目前,企业招聘和求职者应

    2024年04月13日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包