Redis缓存优化

一. 场景分析

为了优化系统的响应速度,对于那些访问频率高但更新频率低的数据,使用缓存可以显著减少数据库查询次数,提升用户体验。

这里以主页精选应用为例,具体就是缓存前十页的精选应用列表数据。因为精选应用的更新频率相对较低。

这种场景下我们采用最主流的旁路缓存模式:查询时先查询缓存,命中则直接返回。未命中则查询数据库,返回数据并将其写入缓存。设置合理的过期时间,无需手动删除缓存。

二. 开发实现

1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>3.1.3</version> <!-- 此处版本号可根据实际需求调整,比如Spring Boot 3.1.x系列 -->
</dependency>

2.检查配置文件中的Redis连接信息

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password:
      ttl: 3600  # 缓存过期时间(秒)

3.. 缓存key生成工具类

3.1 给出查询精选应用的接口方法:

/**
     * 分页获取精选应用列表
     *
     * @param appQueryRequest 查询请求
     * @return 精选应用列表
     */
    @PostMapping("/good/list/page/vo")
    public BaseResponse<Page<AppVO>> listGoodAppVOByPage(@RequestBody AppQueryRequest appQueryRequest) {
        
    }

3.2 AppQueryRequest请求类:

@EqualsAndHashCode(callSuper = true)
@Data
public class AppQueryRequest extends PageRequest implements Serializable {

    /**
     * id
     */
    private Long id;

    /**
     * 应用名称
     */
    private String appName;

    /**
     * 代码生成类型(枚举)
     */
    private String codeGenType;

    /**
     * 优先级
     */
    private Integer priority;

    /**
     * 创建用户id
     */
    private Long userId;

    private static final long serialVersionUID = 1L;
}

这里可以看到查询请求类有多个字段,如果用户每次的查询条件不同,生成的AppqueryRequest也是不同的。为了生成一致且唯一的缓存键。缓存键的生成思路是将复杂的对象转换为固定长度的哈希值。这样相同的查询条件生成的缓存key是相同的,保证了不同查询请求的key唯一,又避免了key过长的问题。

3.3 key生成工具类:

/**
 * 缓存 key 生成工具类
 *
 * @author yupi
 */
public class CacheKeyUtils {

    /**
     * 根据对象生成缓存key (JSON + MD5)
     *
     * @param obj 要生成key的对象
     * @return MD5哈希后的缓存key
     */
    public static String generateKey(Object obj) {
        if (obj == null) {
            return DigestUtil.md5Hex("null");
        }
        // 先转JSON,再MD5
        String jsonStr = JSONUtil.toJsonStr(obj);
        return DigestUtil.md5Hex(jsonStr);
    }
}

这个工具类主要使用 Hutool 工具库实现,几个要点:

   1.JSON 序列化: 确保对象内容的一致性,相同内容的对象生成相同的字符串

   2.MD5 哈希: 将长字符串转换为固定长度的字符串,避免 Redis key 过长

   3.边界处理: 正确处理 null 值和空参数的情况

4. 启用缓存功能

再Spring Boot 启动类上添加 @EnableCaching 注解,支持 Spring Data 缓存注解。

@SpringBootApplication
@EnableCaching
public class SzjAiCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SzjAiCodeApplication.class, args);
    }

}

5. 配置缓存管理器

必须配置 Redis 缓存管理器 CacheManager,这是 Spring Cache 的核心组件。如果不配置的话,使用缓存注解时可能会报错。

/**
 * Redis 缓存管理器配置
 */
@Configuration
public class RedisCacheManagerConfig {

    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public CacheManager cacheManager() {
        // 配置 ObjectMapper 支持 Java8 时间类型
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
//        // 默认类型配置:启用默认类型信息(针对非final类),以便反序列化时能恢复原类型
//        objectMapper.activateDefaultTyping(
//                objectMapper.getPolymorphicTypeValidator(),
//                ObjectMapper.DefaultTyping.NON_FINAL
//        );
        // 默认配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30)) // 默认 30 分钟过期
                .disableCachingNullValues() // 禁用 null 值缓存
                // key 使用 String 序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()));
                  //不打开即使用redis的默认序列化器
//                // value 使用 JSON 序列化器(支持复杂对象)但是要注意开启后需要给序列化增加默认类型配置,否则无法反序列化
//                .serializeValuesWith(RedisSerializationContext.SerializationPair
//                        .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
//
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(defaultConfig)
                // 针对 good_app_page 配置5分钟过期
                .withCacheConfiguration("good_app_page",
                        defaultConfig.entryTtl(Duration.ofMinutes(5)))
                .build();
    }
}

这个配置的几个关键点:

1. 序列化器选择: StringRedisSerializer 用于序列化 key,确保 Redis 中的 key 是可读的字符串;GenericJackson2JsonRedisSerializer 用于序列化 value,支持复杂对象的序列化和反序列化。

2. 时间类型支持: 注册 JavaTimeModule 来支持 Java 8 时间类型 LocalDateTime

3. 差异化配置: 既提供了默认配置,又为特定的缓存区域设置不同的过期时间

但是要注意,如果对 value 进行 JSON 序列化,可能会出现无法反序列化的情况,因为 Redis 中并没有存储 Java 类的信息,不知道要反序列化成哪个类,就会报错。所以我们可以先注释掉这些代码,使用redis默认的序列化器。

如果一定要对 value 进行 JSON 序列化,则开启默认类型配置,并且要反序列化的对象一定要有无参构造方法。

6. 应用缓存注解

在接口方法上添加缓存注解:

@PostMapping("/good/list/page/vo")
@Cacheable(
        value = "good_app_page",
        key = "T(com.yupi.yuaicodemother.utils.CacheKeyUtils).generateKey(#appQueryRequest)",
        condition = "#appQueryRequest.pageNum <= 10"
)
public BaseResponse<Page<AppVO>> listGoodAppVOByPage(@RequestBody AppQueryRequest appQueryRequest) {
    // 方法实现保持不变...
}

这里使用了 SpEL(Spring Expression Language)表达式:

 T(类名):用于调用静态方法,生成缓存 key

 #参数名:用于引用方法参数

 condition:设置缓存条件,只有前 10 页才会被缓存

缓存注解的工作原理,执行流程如下:

1. 方法执行前:Spring 根据 key 表达式生成缓存键

2. 缓存检查:检查 Redis 中是否存在该键对应的缓存数据

3. 缓存命中:如果存在且未过期,直接返回缓存数据,不执行方法

4. 缓存未命中:如果不存在,执行方法获取结果,并将结果存储到 Redis 中 5. 返回结果:返回方法执行结果

<think>好的,我现在需要回答用户关于Redis缓存性能优化的最佳实践。首先,我需要回顾已有的知识,并结合用户提供的引用内容来构建一个全面的回答。用户提到了两个引用,分别是关于Redis缓存设计支持百万并发和Spring Boot整合Redis的实践。我需要确保将这些内容融入回答中,同时遵循系统级指令中的格式要求。 首先,我应该列出主要的优化方向。常见的Redis性能优化包括内存管理、数据结构优化、持久化策略、集群配置、缓存策略等。然后,每个方向需要具体展开,给出实际的例子或配置建议。例如,内存管理方面可以提到使用适当的数据结构,如使用Hash代替多个String存储对象。引用[1]提到合理设计缓存策略和架构,这可能涉及到缓存淘汰策略的选择,比如LRU或LFU,以及缓存预热、雪崩、击穿、穿透的处理方法。 接下来,数据结构优化部分需要详细说明不同场景下的最佳数据结构选择,比如使用zset进行排行榜,使用HyperLogLog进行基数统计,这可以减少内存占用并提高效率。引用[2]中提到的Spring Boot整合Redis,可能涉及到客户端连接池的配置,比如使用Lettuce而不是Jedis,以及合理配置连接池参数,避免资源浪费或不足。 关于持久化,需要比较RDB和AOF的优缺点,以及如何根据业务需求选择合适的策略。例如,在追求高性能的场景下,可以关闭持久化,或者使用RDB进行定时快照。同时,主从复制和集群部署也是提升性能和可靠性的关键点,引用[1]中提到的架构优化可能包括分片策略和读写分离。 缓存策略方面,需要详细说明缓存雪崩、击穿、穿透的区别及解决方案。比如,雪崩可以通过随机过期时间,击穿使用互斥锁或缓存永不过期,穿透则采用布隆过滤器或缓存空值。这些内容在引用[1]中提到的多种缓存策略应该覆盖。 性能监控部分,引用[1]和[2]都强调了监控的重要性,需要提到使用Redis自带的INFO命令、监控工具如Redis Stat,以及慢查询日志的分析。此外,客户端优化如Pipeline和批量操作可以减少网络开销,Lua脚本保证原子性操作,避免多次交互。 最后,安全问题也不能忽视,比如禁用危险命令,设置密码和防火墙。同时,结合业务特点进行优化,如热点数据分开存储或使用多级缓存,这可能结合引用[1]中的架构设计建议。 在回答过程中,需要确保所有数学公式和代码块使用正确的格式,行内公式用$...$,独立公式用$$...$$,虽然当前问题可能不涉及太多数学公式,但如果有的话需要注意。例如,在说明HyperLogLog的内存效率时,可以提到其误差率公式,如误差率约为$1.04/\sqrt{m}$,其中m是桶的数量。 最后,生成的相关问题需要紧密围绕Redis性能优化,可能包括具体策略的深入探讨、工具使用、常见问题解决等,确保问题对用户有进一步的帮助。 现在需要检查是否有遗漏的优化点,是否每个部分都结合了提供的引用内容,并确保结构清晰,符合用户的要求。</think>Redis缓存性能优化需要从架构设计、配置调优、数据结构和业务场景适配等多维度综合考虑。以下是关键优化实践: ### 一、内存管理与数据结构优化 1. **选择高效数据结构** - 使用$Hash$类型存储对象(内存节省30%-50% vs多个$String$) - 基数统计用$HyperLogLog$(误差率$0.81\%$,仅需12KB) - 排行榜场景用$zset$,时间复杂度$O(log N)$ 2. **内存控制策略** ```conf maxmemory 16gb # 设置最大内存限制 maxmemory-policy volatile-lru # 使用LRU淘汰策略[^1] ``` ### 二、持久化与集群配置 $$RDB\ 快照生成频率:save 900 1 \| save 300 10 \| save 60 10000$$ - 混合持久化配置: ```conf aof-use-rdb-preamble yes # 结合RDB+AOF优势[^2] ``` 3. **集群化部署** - 数据分片采用$CRC16(key)\ mod\ 16384$算法 - 读写分离时从节点设置`replica-serve-stale-data yes` ### 三、缓存策略设计 | 问题类型 | 解决方案 | 效果 | |---------|---------|------| | 缓存雪崩 | 随机过期时间$TTL = base + random(0, 300)s$ | 降低同时失效概率 | | 缓存击穿 | 使用$SETNX$实现互斥锁 | 防止DB被高频击穿 | | 缓存穿透 | 布隆过滤器(误判率$<1\%$) | 拦截无效查询[^1] | ### 四、性能调优实践 1. **连接池配置(Spring Boot示例)** ```yaml lettuce: pool: max-active: 20 # 建议值=并发线程数×2 max-idle: 8 min-idle: 3 ``` 2. **Pipeline批量操作** ```python pipe = redis.pipeline() for key in keys: pipe.get(key) results = pipe.execute() # 减少RTT次数 ``` ### 五、监控与安全保障 - 慢查询阈值设置:`slowlog-log-slower-than 5ms` - 危险命令禁用:`rename-command FLUSHDB ""` - 监控指标关注:内存碎片率$mem_fragmentation_ratio < 1.5$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值