Spring Boot : 集成Redis全面指南

一、环境准备与依赖配置

1.1 添加Maven依赖

xml

<!-- Spring Boot Redis Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 连接池依赖 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

<!-- JSON序列化支持 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

<!-- 测试支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

1.2 基础配置

yaml

# application.yml
spring:
  redis:
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器连接密码(默认为空)
    password: 
    # 连接超时时间
    timeout: 2000ms
    # 连接池配置
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 20
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
    # 集群配置(如果需要)
    # cluster:
    #   nodes: 
    #     - 127.0.0.1:7001
    #     - 127.0.0.1:7002
    #     - 127.0.0.1:7003

二、Redis配置类详解

2.1 基础配置类

java

@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 配置RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 设置序列化方式
        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);
        
        // 设置key和value的序列化规则
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
    
    /**
     * 配置缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            // 设置默认过期时间
            .entryTtl(Duration.ofMinutes(30))
            // 不缓存空值
            .disableCachingNullValues()
            // 设置key序列化方式
            .serializeKeysWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new StringRedisSerializer()))
            // 设置value序列化方式
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(jackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            // 对不同的缓存命名空间设置不同的过期时间
            .withInitialCacheConfigurations(getCacheConfigurations())
            .build();
    }
    
    private Map<String, RedisCacheConfiguration> getCacheConfigurations() {
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        // 用户缓存1小时过期
        configMap.put("user", RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1)));
        // 商品缓存10分钟过期
        configMap.put("product", RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10)));
        return configMap;
    }
    
    private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        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);
        return serializer;
    }
}

2.2 高级配置:多种序列化方式

java

@Configuration
public class RedisSerializerConfig {
    
    /**
     * 针对String操作的RedisTemplate
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        return new StringRedisTemplate(factory);
    }
    
    /**
     * 针对JSON操作的RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> jsonRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        GenericJackson2JsonRedisSerializer serializer = 
            new GenericJackson2JsonRedisSerializer();
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

三、Redis工具类封装

3.1 通用Redis工具类

java

@Component
public class RedisUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // ============================= Common =============================
    
    /**
     * 指定缓存失效时间
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 获取过期时间
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    
    /**
     * 判断key是否存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 删除缓存
     */
    public boolean delete(String key) {
        try {
            return redisTemplate.delete(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 批量删除缓存
     */
    public long delete(Collection<String> keys) {
        try {
            Long count = redisTemplate.delete(keys);
            return count == null ? 0 : count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    
    // ============================ String =============================
    
    /**
     * 普通缓存获取
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    
    /**
     * 普通缓存放入
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 普通缓存放入并设置时间
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 递增
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    /**
     * 递减
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
    
    // =============================== Hash ===============================
    
    /**
     * HashGet
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }
    
    /**
     * 获取hashKey对应的所有键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    /**
     * HashSet
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * HashSet 并设置时间
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    // =============================== List ===============================
    
    /**
     * 获取list缓存的内容
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 将list放入缓存
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 将list放入缓存并设置过期时间
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    // =============================== Set ===============================
    
    /**
     * 根据key获取Set中的所有值
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 将数据放入set缓存
     */
    public boolean sSet(String key, Object... values) {
        try {
            redisTemplate.opsForSet().add(key, values);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    // =============================== ZSet ===============================
    
    /**
     * 向ZSet添加元素
     */
    public boolean zAdd(String key, Object value, double score) {
        try {
            return redisTemplate.opsForZSet().add(key, value, score);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 获取ZSet排名
     */
    public Set<Object> zRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

3.2 分布式锁工具类

java

@Component
public class RedisLockUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE = 30000; // 30秒
    private static final long DEFAULT_WAIT_TIME = 10000; // 10秒
    
    /**
     * 尝试获取分布式锁
     */
    public boolean tryLock(String lockKey, String requestId) {
        return tryLock(lockKey, requestId, DEFAULT_EXPIRE);
    }
    
    /**
     * 尝试获取分布式锁(自定义过期时间)
     */
    public boolean tryLock(String lockKey, String requestId, long expire) {
        String key = LOCK_PREFIX + lockKey;
        
        // 使用SET NX EX命令保证原子性
        Boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            byte[] keyBytes = serializer.serialize(key);
            byte[] valueBytes = serializer.serialize(requestId);
            
            // SET key value NX EX timeout
            return connection.set(keyBytes, valueBytes, 
                Expiration.milliseconds(expire), 
                RedisStringCommands.SetOption.SET_IF_ABSENT);
        });
        
        return Boolean.TRUE.equals(result);
    }
    
    /**
     * 释放分布式锁
     */
    public boolean releaseLock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        
        // Lua脚本保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                       "return redis.call('del', KEYS[1]) " +
                       "else " +
                       "return 0 " +
                       "end";
        
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        
        Long result = redisTemplate.execute(redisScript, 
            Collections.singletonList(key), 
            requestId);
        
        return result != null && result > 0;
    }
    
    /**
     * 阻塞式获取锁
     */
    public boolean lock(String lockKey, String requestId) throws InterruptedException {
        return lock(lockKey, requestId, DEFAULT_WAIT_TIME, DEFAULT_EXPIRE);
    }
    
    public boolean lock(String lockKey, String requestId, long waitTime, long expire) 
            throws InterruptedException {
        long start = System.currentTimeMillis();
        
        while (true) {
            if (tryLock(lockKey, requestId, expire)) {
                return true;
            }
            
            if (System.currentTimeMillis() - start > waitTime) {
                return false;
            }
            
            // 等待一段时间再重试
            Thread.sleep(100);
        }
    }
}

四、Spring Cache注解使用

4.1 实体类定义

java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String name;
    private String email;
    private Integer age;
    private Date createTime;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String name;
    private Double price;
    private Integer stock;
    private String category;
}

4.2 Service层缓存使用

java

@Service
public class UserService {
    
    // 模拟数据库
    private Map<Long, User> userDatabase = new HashMap<>();
    private AtomicLong idGenerator = new AtomicLong(1);
    
    /**
     * 根据ID查询用户 - 使用缓存
     * 如果缓存存在,直接返回缓存数据
     * 如果缓存不存在,执行方法体,并将结果放入缓存
     */
    @Cacheable(value = "user", key = "#id")
    public User getUserById(Long id) {
        System.out.println("查询数据库,用户ID: " + id);
        // 模拟数据库查询耗时
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return userDatabase.get(id);
    }
    
    /**
     * 保存用户 - 同时更新缓存
     * 总是执行方法体,并将结果更新到缓存中
     */
    @CachePut(value = "user", key = "#user.id")
    public User saveUser(User user) {
        System.out.println("保存用户到数据库: " + user.getName());
        if (user.getId() == null) {
            user.setId(idGenerator.getAndIncrement());
        }
        userDatabase.put(user.getId(), user);
        return user;
    }
    
    /**
     * 删除用户 - 同时清除缓存
     */
    @CacheEvict(value = "user", key = "#id")
    public void deleteUser(Long id) {
        System.out.println("从数据库删除用户: " + id);
        userDatabase.remove(id);
    }
    
    /**
     * 更新用户 - 更新缓存
     */
    @CachePut(value = "user", key = "#user.id")
    public User updateUser(User user) {
        System.out.println("更新用户信息: " + user.getId());
        if (userDatabase.containsKey(user.getId())) {
            userDatabase.put(user.getId(), user);
            return user;
        }
        throw new RuntimeException("用户不存在");
    }
    
    /**
     * 获取所有用户 - 列表缓存
     */
    @Cacheable(value = "userList")
    public List<User> getAllUsers() {
        System.out.println("查询所有用户");
        return new ArrayList<>(userDatabase.values());
    }
    
    /**
     * 清除用户列表缓存
     */
    @CacheEvict(value = "userList", allEntries = true)
    public void clearUserListCache() {
        System.out.println("清除用户列表缓存");
    }
    
    /**
     * 复杂缓存操作 - 同时操作多个缓存
     */
    @Caching(
        evict = {
            @CacheEvict(value = "user", key = "#id"),
            @CacheEvict(value = "userList", allEntries = true)
        }
    )
    public void refreshUserCache(Long id) {
        System.out.println("刷新用户缓存: " + id);
    }
}

@Service
public class ProductService {
    
    private Map<Long, Product> productDatabase = new HashMap<>();
    private AtomicLong idGenerator = new AtomicLong(1);
    
    /**
     * 条件查询 - 自定义key生成
     */
    @Cacheable(value = "product", key = "#name + ':' + #category")
    public Product getProductByNameAndCategory(String name, String category) {
        System.out.println("查询产品: " + name + ", 分类: " + category);
        return productDatabase.values().stream()
            .filter(p -> p.getName().equals(name) && p.getCategory().equals(category))
            .findFirst()
            .orElse(null);
    }
    
    /**
     * 带条件的缓存
     */
    @Cacheable(value = "product", key = "#id", condition = "#id > 10")
    public Product getProductWithCondition(Long id) {
        System.out.println("查询产品(条件缓存): " + id);
        return productDatabase.get(id);
    }
    
    /**
     * 除非条件的缓存
     */
    @Cacheable(value = "product", key = "#id", unless = "#result == null")
    public Product getProductWithUnless(Long id) {
        System.out.println("查询产品(除非条件): " + id);
        return productDatabase.get(id);
    }
}

4.3 自定义Key生成器

java

@Component
public class CustomKeyGenerator implements KeyGenerator {
    
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getSimpleName());
        sb.append(".");
        sb.append(method.getName());
        sb.append("[");
        for (Object param : params) {
            if (param != null) {
                sb.append(param.toString());
            }
            sb.append(",");
        }
        sb.append("]");
        return sb.toString();
    }
}

// 在配置类中注册自定义Key生成器
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public KeyGenerator customKeyGenerator() {
        return new CustomKeyGenerator();
    }
    
    // 使用自定义Key生成器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .computePrefixWith(cacheName -> cacheName + ":")
            .serializeKeysWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(jackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }
}

五、高级特性集成

5.1 发布订阅模式

java

@Component
public class RedisMessageListener implements MessageListener {
    
    private static final Logger logger = LoggerFactory.getLogger(RedisMessageListener.class);
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(pattern);
        String body = new String(message.getBody());
        logger.info("收到频道[{}]的消息: {}", channel, body);
        
        // 处理不同的消息类型
        handleMessage(channel, body);
    }
    
    private void handleMessage(String channel, String message) {
        switch (channel) {
            case "user:register":
                logger.info("处理用户注册消息: {}", message);
                break;
            case "order:create":
                logger.info("处理订单创建消息: {}", message);
                break;
            default:
                logger.info("处理未知频道消息: {}", message);
        }
    }
}

@Component
public class RedisMessagePublisher {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 发布消息
     */
    public void publish(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }
    
    /**
     * 发布用户注册消息
     */
    public void publishUserRegister(User user) {
        Map<String, Object> message = new HashMap<>();
        message.put("userId", user.getId());
        message.put("userName", user.getName());
        message.put("email", user.getEmail());
        message.put("timestamp", System.currentTimeMillis());
        
        publish("user:register", message);
    }
    
    /**
     * 发布订单创建消息
     */
    public void publishOrderCreate(Long orderId, Double amount) {
        Map<String, Object> message = new HashMap<>();
        message.put("orderId", orderId);
        message.put("amount", amount);
        message.put("timestamp", System.currentTimeMillis());
        
        publish("order:create", message);
    }
}

@Configuration
public class RedisPubSubConfig {
    
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    
    @Autowired
    private RedisMessageListener redisMessageListener;
    
    /**
     * 配置消息监听容器
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        
        // 订阅多个频道
        container.addMessageListener(redisMessageListener, 
            Arrays.asList(
                new PatternTopic("user:register"),
                new PatternTopic("order:create"),
                new PatternTopic("system:notification")
            ));
        
        return container;
    }
}

5.2 管道操作

java

@Service
public class PipelineService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 使用管道批量插入数据
     */
    public void batchInsertUsers(List<User> users) {
        redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                for (User user : users) {
                    String key = "user:" + user.getId();
                    byte[] keyBytes = redisTemplate.getStringSerializer().serialize(key);
                    byte[] valueBytes = redisTemplate.getValueSerializer().serialize(user);
                    connection.set(keyBytes, valueBytes);
                    
                    // 同时设置过期时间
                    connection.expire(keyBytes, Duration.ofHours(1).getSeconds());
                }
                return null;
            }
        });
    }
    
    /**
     * 批量获取数据
     */
    public List<Object> batchGetUsers(List<Long> userIds) {
        return redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                for (Long userId : userIds) {
                    String key = "user:" + userId;
                    byte[] keyBytes = redisTemplate.getStringSerializer().serialize(key);
                    connection.get(keyBytes);
                }
                return null;
            }
        });
    }
}

5.3 Lua脚本支持

java

@Service
public class LuaScriptService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 库存扣减脚本
    private static final String DEDUCT_SCRIPT = 
        "local current = tonumber(redis.call('get', KEYS[1])) " +
        "if current == nil then " +
        "   return -1 " +
        "end " +
        "if current < tonumber(ARGV[1]) then " +
        "   return -2 " +
        "end " +
        "redis.call('decrby', KEYS[1], ARGV[1]) " +
        "return 0";
    
    // 限流脚本
    private static final String RATE_LIMIT_SCRIPT = 
        "local key = KEYS[1] " +
        "local limit = tonumber(ARGV[1]) " +
        "local window = tonumber(ARGV[2]) " +
        "local current = redis.call('get', key) " +
        "if current == false then " +
        "   redis.call('set', key, 1, 'EX', window) " +
        "   return 1 " +
        "elseif tonumber(current) < limit then " +
        "   redis.call('incr', key) " +
        "   return 1 " +
        "else " +
        "   return 0 " +
        "end";
    
    /**
     * 扣减库存 - Lua脚本保证原子性
     */
    public boolean deductInventory(String productId, int quantity) {
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(DEDUCT_SCRIPT);
        script.setResultType(Long.class);
        
        Long result = redisTemplate.execute(script, 
            Collections.singletonList("inventory:" + productId), 
            String.valueOf(quantity));
            
        return result == 0;
    }
    
    /**
     * 限流检查
     */
    public boolean rateLimit(String key, int limit, int window) {
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(RATE_LIMIT_SCRIPT);
        script.setResultType(Long.class);
        
        Long result = redisTemplate.execute(script, 
            Collections.singletonList("rate_limit:" + key), 
            String.valueOf(limit), 
            String.valueOf(window));
            
        return result == 1;
    }
}

六、测试类

java

@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisIntegrationTest {
    
    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisMessagePublisher messagePublisher;
    
    @Autowired
    private RedisLockUtil redisLockUtil;
    
    @Test
    public void testBasicOperations() {
        // 测试基本操作
        redisUtil.set("test:key", "Hello Redis");
        String value = (String) redisUtil.get("test:key");
        Assert.assertEquals("Hello Redis", value);
    }
    
    @Test
    public void testCacheAnnotations() {
        // 测试缓存注解
        User user = new User(1L, "张三", "zhangsan@example.com", 25, new Date());
        
        // 第一次查询,会访问数据库
        User result1 = userService.getUserById(1L);
        
        // 第二次查询,应该从缓存获取
        User result2 = userService.getUserById(1L);
        
        Assert.assertNotNull(result1);
        Assert.assertNotNull(result2);
    }
    
    @Test
    public void testDistributedLock() throws InterruptedException {
        String lockKey = "order:123";
        String requestId = UUID.randomUUID().toString();
        
        // 获取锁
        boolean locked = redisLockUtil.tryLock(lockKey, requestId);
        Assert.assertTrue(locked);
        
        // 释放锁
        boolean released = redisLockUtil.releaseLock(lockKey, requestId);
        Assert.assertTrue(released);
    }
    
    @Test
    public void testPubSub() throws InterruptedException {
        // 测试发布订阅
        User user = new User(1L, "李四", "lisi@example.com", 30, new Date());
        messagePublisher.publishUserRegister(user);
        
        // 等待消息处理
        Thread.sleep(1000);
    }
}

七、配置优化建议

7.1 生产环境配置

yaml

spring:
  redis:
    host: ${REDIS_HOST:127.0.0.1}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD:}
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 50
        max-idle: 20
        min-idle: 5
        max-wait: 5000ms
      shutdown-timeout: 100ms
  cache:
    redis:
      time-to-live: 1h
      cache-null-values: false
      use-key-prefix: true
      key-prefix: "cache:"

7.2 监控配置

java

@Component
public class RedisMetrics {
    
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    
    /**
     * 获取Redis监控信息
     */
    public void monitorRedis() {
        RedisConnection connection = redisConnectionFactory.getConnection();
        try {
            Properties info = connection.info();
            System.out.println("Redis版本: " + info.getProperty("redis_version"));
            System.out.println("已连接客户端数: " + info.getProperty("connected_clients"));
            System.out.println("使用内存: " + info.getProperty("used_memory_human"));
            System.out.println("命中率: " + info.getProperty("keyspace_hits"));
        } finally {
            connection.close();
        }
    }
}

这个完整的Spring Boot集成Redis指南涵盖了从基础配置到高级特性的所有方面,包括缓存注解、分布式锁、发布订阅、管道操作和Lua脚本等。通过这个指南,你可以快速在项目中集成Redis并充分利用其强大功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

andywangzhen_ai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值