6.Spring Boot 整合Redis

Spring Boot整合redis

redis介绍:

  • RedisRemote Dictionary Server)是一个开源的、基于内存的 键值存储系统(Key-Value Store),同时支持持久化,广泛用于缓存、消息队列、实时数据分析等场景。以下是 Redis 的详细介绍,涵盖其核心特性、数据结构、持久化机制、高可用方案及使用场景。

1. Redis 核心特性
(1)内存存储 + 持久化
  • 内存存储:数据主要存储在内存中,读写性能极高(10万+ QPS)。
  • 持久化:支持两种方式将内存数据保存到磁盘:
    • RDB(快照):定时生成数据快照,适合备份。
    • AOF(日志追加):记录所有写操作命令,重启时重放,保证数据安全。
(2)丰富的数据结构

Redis 不仅仅是简单的 Key-Value 存储,还支持多种数据结构:

数据结构 描述 示例命令
String 文本、数字、二进制数据 SET key value, GET key
List 有序列表(支持双向操作) LPUSH list elem, RPOP list
Hash 键值对集合(适合存储对象) HSET user:1 name "Alice"
Set 无序唯一集合(去重、交集/并集) SADD tags "redis", SINTER
Sorted Set 带权重的有序集合(排行榜场景) ZADD leaderboard 100 "user1"
Bitmaps 位操作(用户签到、布隆过滤器) SETBIT login:2023-01-01 1 1
HyperLogLog 基数统计(UV 去重统计) PFADD visits user1, PFCOUNT
Stream 消息队列(类似 Kafka) XADD stream * field value
(3)高性能与单线程模型
  • 单线程处理请求:避免多线程竞争,通过非阻塞 I/O 和事件驱动(类似 Node.js)实现高吞吐。
  • 纯内存操作:无需磁盘 I/O 延迟,响应时间在微秒级。
(4)扩展功能
  • 发布/订阅(Pub/Sub):支持消息的实时广播。
  • Lua 脚本:原子性执行复杂逻辑(如秒杀扣库存)。
  • 事务MULTI/EXEC 支持简单事务(不保证原子性,仅保证顺序执行)。
  • 地理空间索引:存储经纬度,计算距离(如附近的人)。

2. Redis 持久化机制
(1)RDB(Redis Database)
  • 原理:定时生成内存数据的二进制快照(默认文件 dump.rdb)。
  • 优点
    • 文件紧凑,适合备份和灾难恢复。
    • 重启恢复速度快。
  • 缺点
    • 可能丢失最后一次快照后的数据(依赖配置的保存周期)。
    • 大数据量时 fork 子进程可能阻塞主线程。
(2)AOF(Append Only File)
  • 原理:记录所有写操作命令(类似 MySQL 的 binlog),支持每秒同步或每次操作同步。
  • 优点
    • 数据安全性高(可配置为实时持久化)。
    • 文件易读(可通过 redis-check-aof 工具修复)。
  • 缺点
    • 文件体积较大,恢复速度慢。
    • 开启 AOF 后 QPS 略有下降。
(3)混合持久化(Redis 4.0+)
  • 结合 RDB 和 AOF:先以 RDB 格式存储全量数据,后续增量操作以 AOF 追加。
  • 恢复时:先加载 RDB 快照,再重放 AOF 日志。

3. Redis 高可用方案
(1)主从复制(Replication)
  • 作用:数据从主节点(Master)同步到从节点(Slave),实现读写分离。
  • 特点
    • 异步复制,从节点可处理读请求。
    • 主节点宕机后需手动切换从节点。
(2)哨兵模式(Sentinel)
  • 作用:监控主从节点,自动故障转移(主节点宕机时选举新主节点)。
  • 特点
    • 提供高可用,但不解决数据分片问题。
    • 客户端需连接 Sentinel 获取主节点地址。
(3)集群模式(Cluster)
  • 作用:数据分片(16384 个槽),支持水平扩展。
  • 特点
    • 无中心节点,每个节点存储部分数据。
    • 自动故障转移(类似 Sentinel)。
    • 客户端需支持集群协议(如 Jedis Cluster、Lettuce)。

4. Redis 使用场景
(1)缓存
  • 减轻数据库压力:将热点数据(如商品详情)缓存到 Redis。
  • 缓存策略
    • 主动更新:数据库变更时同步更新缓存。
    • 惰性删除:缓存失效后从数据库加载。
(2)会话存储(Session Storage)
  • 分布式会话:用户登录状态集中存储,解决服务端无状态问题。
(3)排行榜
  • Sorted Set:通过 ZADDZRANGE 实现实时排名。
(4)消息队列
  • ListLPUSH/BRPOP 实现简单队列。
  • Stream:支持多消费者组(类似 Kafka)。
(5)分布式锁
  • SETNX:通过 SET key value NX EX 10 实现互斥锁(需配合 Lua 脚本避免误删)。

5. Redis 常见问题
(1)缓存穿透
  • 问题:大量请求查询不存在的 key(如恶意攻击)。
  • 解决:布隆过滤器(Bloom Filter)拦截非法请求。
(2)缓存雪崩
  • 问题:大量 key 同时过期,导致请求直接打到数据库。
  • 解决:设置随机过期时间,或使用永不过期 + 后台更新策略。
(3)内存淘汰策略

当内存不足时,Redis 支持多种淘汰策略(通过 maxmemory-policy 配置):

  • volatile-lru:从已设置过期时间的 key 中淘汰最近最少使用的。
  • allkeys-lru:从所有 key 中淘汰最近最少使用的。
  • noeviction:不淘汰,直接返回错误(默认)。

6. Redis 与 Memcached 对比
特性 Redis Memcached
数据结构 支持多种复杂结构(Hash、Set 等) 仅支持 String
持久化 支持 RDB 和 AOF 不支持
线程模型 单线程(避免竞争) 多线程
适用场景 缓存、消息队列、实时计算 纯缓存

7. 总结
  • Redis 优势:高性能、丰富的数据结构、持久化、高可用。
  • 适用场景:缓存、会话存储、排行榜、消息队列、分布式锁等。
  • 注意事项:合理配置持久化、内存淘汰策略,避免缓存穿透/雪崩。

Spring Boot 整合 Redis 详细指南

Spring Boot 提供了对 Redis 的自动配置支持,使得在应用中集成 Redis 变得非常简单。下面我将详细介绍如何整合 Redis,包括基本配置、使用方式以及高级功能。

1. 添加依赖

首先需要在 pom.xml 中添加 Redis 相关依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>


<dependency>
    <groupId>org.apache.commonsgroupId>
    <artifactId>commons-pool2artifactId>
dependency>
2. 基本配置

application.ymlapplication.properties 中配置 Redis 连接信息:

spring:
  redis:
    host: localhost
    port: 6379
    password:  # 如果有密码
    database: 0 # 默认使用0号数据库
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8   # 连接池最大空闲连接数
        min-idle: 0   # 连接池最小空闲连接数
3. 使用 RedisTemplate

Spring Boot 自动配置了 RedisTemplateStringRedisTemplate,可以直接注入使用:

@RestController
public class RedisController {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @GetMapping("/set")
    public String setValue() {
        // 使用 StringRedisTemplate 操作字符串
        stringRedisTemplate.opsForValue().set("test:string", "Hello Redis");
        
        // 使用 RedisTemplate 操作对象
        User user = new User("张三", 25);
        redisTemplate.opsForValue().set("user:1", user);
        
        return "设置成功";
    }
    
    @GetMapping("/get")
    public Object getValue() {
        String str = stringRedisTemplate.opsForValue().get("test:string");
        User user = (User) redisTemplate.opsForValue().get("user:1");
        
        return Map.of("string", str, "user", user);
    }
}
4. 自定义 RedisTemplate

如果需要自定义序列化方式,可以配置自己的 RedisTemplate:

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), 
                                   ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        
        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        
        template.afterPropertiesSet();
            return template;
    }
}
5. 使用 Redis Repository

Spring Data Redis 支持类似 JPA 的 Repository 接口:

@RedisHash("persons")
public class Person {
    @Id
    private String id;
    private String name;
    private Integer age;
    // getters and setters
}

public interface PersonRepository extends CrudRepository<Person, String> {
    List<Person> findByName(String name);
}

@Service
public class PersonService {
    
    @Autowired
    private PersonRepository personRepository;
    
    public void savePerson(Person person) {
        personRepository.save(person);
    }
    
    public Person getPerson(String id) {
        return personRepository.findById(id).orElse(null);
    }
}
6. 缓存集成

Spring Boot 可以轻松将 Redis 作为缓存使用:

  1. 启用缓存支持:
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 配置缓存:
spring:
  cache:
    type: redis
    redis:
      time-to-live: 60000 # 缓存过期时间(毫秒)
      cache-null-values: false # 是否缓存空值
  1. 使用缓存注解:
@Service
public class UserService {
    
    @Cacheable(value = "userCache", key = "#id")
    public User getUserById(String id) {
        // 模拟数据库查询
        return new User(id, "用户" + id, new Random().nextInt(100));
    }
    
    @CachePut(value = "userCache", key = "#user.id")
    public User updateUser(User user) {
        // 更新逻辑
        return user;
    }
    
    @CacheEvict(value = "userCache", key = "#id")
    public void deleteUser(String id) {
        // 删除逻辑
    }
}
7. 高级功能
7.1 发布/订阅
@Configuration
public class RedisPubSubConfig {
    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory factory,
                                                  MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(listenerAdapter, new ChannelTopic("chat"));
        return container;
    }
    
    @Bean
    public MessageListenerAdapter listenerAdapter(Receiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
    
    @Bean
    public Receiver receiver() {
        return new Receiver();
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        return template;
    }
}

@Component
public class Receiver {
    public void receiveMessage(String message) {
        System.out.println("收到消息: " + message);
    }
}

@Service
public class Publisher {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void publish(String message) {
        redisTemplate.convertAndSend("chat", message);
    }
}
7.2 分布式锁
@Service
public class LockService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
    }
    
    public boolean releaseLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(
            new DefaultRedisScript<>(script, Long.class),
            Collections.singletonList(lockKey),
            requestId
        );
        return result != null && result == 1;
    }
}
8. 集群和哨兵配置
8.1 集群模式
spring:
  redis:
    cluster:
      nodes:
        - 192.168.0.1:6379
        - 192.168.0.2:6379
        - 192.168.0.3:6379
      max-redirects: 3 # 最大重定向次数
8.2 哨兵模式
spring:
  redis:
    sentinel:
      master: mymaster
      nodes:
        - 192.168.0.1:26379
        - 192.168.0.2:26379
        - 192.168.0.3:26379
9. 性能优化建议
  1. 合理配置连接池参数
  2. 选择合适的序列化方式
  3. 对大value进行压缩
  4. 使用Pipeline减少网络往返
  5. 避免使用KEYS命令,改用SCAN
  6. 合理设置过期时间
10. 常见问题解决

问题1: 连接超时
解决: 检查网络连接,增加超时时间配置:

spring:
  redis:
    timeout: 3000 # 连接超时时间(毫秒)

问题2: 序列化异常
解决: 确保所有存储的对象实现了Serializable接口,或配置自定义序列化器

问题3: Lettuce连接池不生效
解决: 确保添加了commons-pool2依赖,并正确配置了pool属性


Jedis与Lettuce的区别

  • Jedis 和 Lettuce 都是用于 Java 应用程序与 Redis 数据库交互的客户端库,但它们在设计、性能和功能上有显著区别。以下是它们的详细对比:
1. Jedis
  • 简介
    Jedis 是一个轻量级、同步阻塞的 Redis Java 客户端,API 简单直接,适合快速开发。

  • 特点

    • 同步阻塞:每个操作会阻塞线程直到返回结果。
    • 连接模式:支持单机、哨兵(Sentinel)和集群(Cluster)模式。
    • 连接管理:需要手动管理连接池(如 JedisPool),避免频繁创建连接的开销。
    • 线程安全:单个 Jedis 实例非线程安全,需通过连接池实现多线程共享。
    • 依赖:仅依赖 Apache Commons Pool 实现连接池。
  • 示例代码

    JedisPool pool = new JedisPool("localhost", 6379);
    try (Jedis jedis = pool.getResource()) {
        jedis.set("key", "value");
        System.out.println(jedis.get("key"));
    }
    

2. Lettuce
  • 简介
    Lettuce 是一个基于 Netty 的高性能、异步非阻塞的 Redis 客户端,支持响应式编程模型。

  • 特点

    • 异步非阻塞:基于 Netty 的事件驱动模型,支持异步操作和回调。
    • 响应式支持:与 Reactor 或 RxJava 集成,支持响应式编程。
    • 连接模式:支持单机、哨兵、集群和自定义拓扑。
    • 线程安全:单个连接可被多线程共享(通过 StatefulRedisConnection)。
    • 长连接:默认使用长连接,减少连接建立开销。
    • 高级功能:支持 SSL、Unix Domain Socket、流式操作等。
  • 示例代码

    RedisClient client = RedisClient.create("redis://localhost:6379");
    StatefulRedisConnection<String, String> connection = client.connect();
    RedisAsyncCommands<String, String> commands = connection.async();
    commands.set("key", "value").thenAccept(System.out::println);
    // 响应式调用
    RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
    reactiveCommands.get("key").subscribe(System.out::println);
    

3. 核心区别
特性 Jedis Lettuce
IO 模型 同步阻塞 异步非阻塞(基于 Netty)
线程安全 需通过连接池实现 单连接线程安全
性能 高吞吐但线程受限 更高并发,适合高负载场景
连接管理 依赖连接池(如 JedisPool 内置长连接,自动管理
编程模型 传统同步调用 支持异步、响应式(Reactor/RxJava)
功能扩展 基础功能完善 支持流式操作、SSL 等高级特性
适用场景 简单应用或旧项目 高并发、微服务、响应式系统

4. 如何选择?
  • 选 Jedis

    • 需要简单同步 API 的小型项目。
    • 对异步编程无需求,或依赖旧版 Java 环境(如 Java 6/7)。
  • 选 Lettuce

    • 高并发、低延迟场景(如微服务)。
    • 需要异步或响应式编程(如 Spring WebFlux)。
    • 需要长连接减少开销(如云环境下的 Redis)。

5. 补充说明
  • Spring 生态的默认选择
    Spring Boot 2.x+ 默认使用 Lettuce,因其更好的性能和现代特性。Jedis 仍可通过配置显式启用。
  • 资源开销
    Lettuce 的 Netty 依赖可能增加包体积,但通常对性能影响可忽略。

建议根据项目需求和团队技术栈选择。新项目优先考虑 Lettuce,尤其是需要响应式支持时。


作者声明:本博客仅为记录作者自学过程中的点滴思考与心得,旨在分享学习之路的所见所感。由于个人知识水平有限,文中难免存在疏漏或不够严谨之处,恳请各位读者不吝赐教,提出宝贵意见。让我们携手共进,共同成长。另本博客有参考网上资料,如有侵犯,联系删除。

你可能感兴趣的:(Spring,Boot,spring,boot,redis,后端)