“Redis与Spring整合及缓存优化“

文章目录

  • 引言
  • [1. Spring整合Redis](#1. Spring整合Redis)
  • [2. Redis注解式缓存](#2. Redis注解式缓存)
    • [2.1. Spring提供的缓存注解介绍](#2.1. Spring提供的缓存注解介绍)
    • [2.2. 使用注解实现方法级别的缓存](#2.2. 使用注解实现方法级别的缓存)
  • [3. Redis的击穿、穿透和雪崩问题及解决方案](#3. Redis的击穿、穿透和雪崩问题及解决方案)
    • [3.1. 几种常见的Redis缓存问题](#3.1. 几种常见的Redis缓存问题)
    • [3.2. 解决方案和优化策略](#3.2. 解决方案和优化策略)
  • 总结

引言

在现代应用开发中,缓存是提升性能和扩展性的关键技术之一。而Redis作为一种高性能、内存存储型数据库,与Spring框架的结合可以带来更加优雅和灵活的缓存解决方案。本篇博客将深入探讨Spring整合Redis的方法和最佳实践,并重点介绍Redis注解式缓存以及解决常见的Redis问题。

1. Spring整合Redis

1.1. 为什么选择Redis作为缓存解决方案?

Redis的特点和优势

  • 内存存储:Redis将数据存储在内存中,因此具有非常高的读写速度。相比传统数据库的磁盘存储,Redis能够快速处理大量请求。

  • 键值存储:Redis使用键值对(key-value)的数据结构来存储数据。这种简单的数据模型使得数据操作非常高效和灵活,适用于各种场景。

  • 支持多种数据类型:Redis支持丰富的数据类型,包括字符串、哈希、列表、集合、有序集合等。这些数据类型的支持使得开发者可以更方便地构建复杂的应用逻辑。

  • 缓存功能:Redis常被用作缓存服务器,可以将频繁访问的数据存储在内存中,从而加快数据读取速度。同时,Redis还提供了一些高级的缓存策略,如过期时间、LRU淘汰等。

  • 发布订阅系统:Redis提供了发布订阅(pub/sub)功能,允许客户端通过订阅某个频道来接收消息的推送。这种消息发布订阅模式对于实时通信、消息队列等场景非常有用。

  • 分布式支持:Redis支持分布式部署,可以将数据分布在多个节点上进行存储和访问。这种分布式的特性使得Redis具备高可用性和横向扩展的能力。

Redis与传统关系数据库的对比

  • 数据模型:Redis采用键值对的数据模型,而传统关系数据库使用表格模型。这使得Redis在处理简单查询和高并发读写方面更加高效,而关系数据库更适合复杂的数据关联和查询操作。

  • 存储方式:Redis将数据存储在内存中,而关系数据库通常将数据持久化到磁盘中。因此,Redis在读写速度方面更快,但关系数据库在数据持久化和容量方面具有优势。

  • 功能特性:Redis提供了丰富的功能特性,如发布订阅、缓存策略等,而关系数据库更加注重数据一致性、事务管理以及复杂的查询能力。

  • 扩展性:Redis支持分布式部署和横向扩展,可以通过添加新的节点来提高性能和容量。而关系数据库通常需要进行垂直扩展,即增加更强大的硬件来应对高负载。

  • 数据一致性:Redis默认情况下是单机的,不保证数据的强一致性,而关系数据库通常会保证数据的一致性。

1.2. Spring与Redis整合的基本步骤

准备步骤:

  1. 打开redis服务
  2. 导入spring整合redis项目
  • 当spring-context.xml文件需要注册多个.properties结尾的配置文件,不能在spring-*中添加注册

applicationContext.xml

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--1. 引入外部多文件方式 -->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:redis.properties</value>
            </list>
        </property>
    </bean>

<!--  随着后续学习,框架会越学越多,不能将所有的框架配置,放到同一个配制间,否者不便于管理  -->
    <import resource="applicationContext-mybatis.xml"></import>
    <import resource="spring-redis.xml"></import>
    <import resource="applicationContext-shiro.xml"></import>
</beans>

redis.properties

java 复制代码
redis.hostName=localhost
redis.port=6379
redis.password=123456
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
redis.maxWaitMillis=1000
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.testOnBorrow=true
redis.testWhileIdle=true
redis.expiration=3600

spring-redis.xml

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- 1. 引入properties配置文件 -->
    <!--<context:property-placeholder location="classpath:redis.properties" />-->

    <!-- 2. redis连接池配置-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数-->
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <!--连接池的最大数据库连接数  -->
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <!--最大建立连接等待时间-->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)-->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3-->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1-->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <!--在空闲时检查有效性, 默认false  -->
        <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
    </bean>

    <!-- 3. redis连接工厂 -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          destroy-method="destroy">
        <property name="poolConfig" ref="poolConfig"/>
        <!--IP地址 -->
        <property name="hostName" value="${redis.hostName}"/>
        <!--端口号  -->
        <property name="port" value="${redis.port}"/>
        <!--如果Redis设置有密码  -->
        <property name="password" value="${redis.password}"/>
        <!--客户端超时时间单位是毫秒  -->
        <property name="timeout" value="${redis.timeout}"/>
    </bean>

    <!-- 4. redis操作模板,使用该对象可以操作redis
        hibernate课程中hibernatetemplete,相当于session,专门操作数据库。
    -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <!--开启事务  -->
        <property name="enableTransactionSupport" value="true"/>
    </bean>

    <!--  5.配置缓存管理器  -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate"/>
        <!--redis缓存数据过期时间单位秒-->
        <property name="defaultExpiration" value="${redis.expiration}"/>
        <!--是否使用缓存前缀,与cachePrefix相关-->
        <property name="usePrefix" value="true"/>
        <!--配置缓存前缀名称-->
        <property name="cachePrefix">
            <bean class="org.springframework.data.redis.cache.DefaultRedisCachePrefix">
                <constructor-arg index="0" value="-cache-"/>
            </bean>
        </property>
    </bean>

    <!--6.配置缓存生成键名的生成规则-->
    <bean id="cacheKeyGenerator" class="com.zking.ssm.redis.CacheKeyGenerator"></bean>

    <!--7.启用缓存注解功能-->
    <cache:annotation-driven cache-manager="redisCacheManager" key-generator="cacheKeyGenerator"/>
</beans>
  • resources的配置文件的配置必须要涵盖读取.properties结尾的文件
  • redisTemplate的使用,可以参照jdbcTemplate、amqpTemplate、rabbitMQtemplate等

2. Redis注解式缓存

2.1. Spring提供的缓存注解介绍

java 复制代码
package com.zking.ssm.biz;

import com.zking.ssm.model.Clazz;
import com.zking.ssm.util.PageBean;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;

import java.util.List;
import java.util.Map;

public interface ClazzBiz {
    @CacheEvict(value = "xx",key = "'cid:'+#cid",allEntries = true)
    int deleteByPrimaryKey(Integer cid);

    int insert(Clazz record);

    int insertSelective(Clazz record);

//    xx=cache-cid:1
//    key的作用改变原有的key生成规则
//    @Cacheable(value = "xx",key = "'cid:'+#cid",condition = "#cid > 6")
    @CachePut(value = "xx",key = "'cid:'+#cid",condition = "#cid > 6")
    Clazz selectByPrimaryKey(Integer cid);

    int updateByPrimaryKeySelective(Clazz record);

    int updateByPrimaryKey(Clazz record);

    List<Clazz> listPager(Clazz clazz, PageBean pageBean);
    List<Map> listMapPager(Clazz clazz, PageBean pageBean);
}

@Cacheable:缓存方法的返回值,可读写

@CachePut:更新缓存的方法,只写不读,适合读写分离

@CacheEvict:移除缓存中的数据

2.2. 使用注解实现方法级别的缓存

在Spring Boot项目中开启注解式缓存

定义缓存的Key和Value的生成策略

编写示例代码演示注解式缓存的使用方式

3. Redis的击穿、穿透和雪崩问题及解决方案

3.1. 几种常见的Redis缓存问题

击穿:大量请求同时查询一个不存在的Key,导致请求都落到数据库上

穿透:恶意请求查询一个不存在的Key,导致请求直接落到数据库上

雪崩:大量缓存失效导致所有请求都落到数据库上

3.2. 解决方案和优化策略

击穿解决方案:设置热点数据永不过期、互斥锁、布隆过滤器等

穿透解决方案:空值缓存、参数校验、布隆过滤器等

雪崩解决方案:限流、降级、异步更新缓存等

总结

在本篇博客中,我们深入探讨了Redis与Spring的整合以及注解式缓存的使用方式。通过学习和实践,我们了解了Redis作为一种高性能缓存解决方案的优势,并且掌握了基于Spring框架使用Redis的方法。

相关推荐
一只哒布刘39 分钟前
NFS服务器
运维·服务器
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天1 小时前
java的threadlocal为何内存泄漏
java
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋31 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan2 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源