Java学习第19天 - 分布式缓存与Redis高级应用

学习时间: 4-5小时
学习目标: 掌握Redis高级特性,学会分布式缓存设计,理解缓存一致性策略


详细学习清单


✅ 第一部分:Redis高级数据结构与应用(60分钟)

1. Redis高级数据结构详解

Redis数据结构应用场景

java 复制代码
// RedisDataStructuresDemo.java
package com.example.demo.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.*;

public class RedisDataStructuresDemo {
    
    private JedisPool jedisPool;
    
    public RedisDataStructuresDemo() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        config.setTestWhileIdle(true);
        
        this.jedisPool = new JedisPool(config, "localhost", 6379);
    }
    
    /**
     * 1. 字符串操作 - 计数器、缓存
     */
    public void stringOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("=== 字符串操作 ===");
            
            // 基本操作
            jedis.set("user:1001:name", "张三");
            jedis.set("user:1001:age", "25");
            jedis.setex("user:1001:token", 3600, "abc123token");
            
            // 计数器操作
            jedis.set("page:views", "0");
            jedis.incr("page:views");
            jedis.incrBy("page:views", 10);
            jedis.decr("page:views");
            
            // 批量操作
            Map<String, String> userInfo = new HashMap<>();
            userInfo.put("name", "李四");
            userInfo.put("age", "30");
            userInfo.put("email", "lisi@example.com");
            jedis.hmset("user:1002", userInfo);
            
            System.out.println("页面访问量: " + jedis.get("page:views"));
            System.out.println("用户信息: " + jedis.hgetAll("user:1002"));
        }
    }
    
    /**
     * 2. 哈希操作 - 对象存储
     */
    public void hashOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("\n=== 哈希操作 ===");
            
            // 用户信息存储
            jedis.hset("user:1003", "id", "1003");
            jedis.hset("user:1003", "name", "王五");
            jedis.hset("user:1003", "age", "28");
            jedis.hset("user:1003", "city", "北京");
            
            // 获取单个字段
            String userName = jedis.hget("user:1003", "name");
            System.out.println("用户名: " + userName);
            
            // 获取所有字段
            Map<String, String> allFields = jedis.hgetAll("user:1003");
            System.out.println("用户完整信息: " + allFields);
            
            // 字段存在性检查
            boolean hasEmail = jedis.hexists("user:1003", "email");
            System.out.println("是否有邮箱: " + hasEmail);
            
            // 删除字段
            jedis.hdel("user:1003", "age");
            System.out.println("删除年龄后的信息: " + jedis.hgetAll("user:1003"));
        }
    }
    
    /**
     * 3. 列表操作 - 消息队列、最新列表
     */
    public void listOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("\n=== 列表操作 ===");
            
            // 最新消息列表
            jedis.lpush("messages:user:1001", "消息1", "消息2", "消息3");
            jedis.lpush("messages:user:1001", "消息4");
            
            // 获取最新5条消息
            List<String> latestMessages = jedis.lrange("messages:user:1001", 0, 4);
            System.out.println("最新消息: " + latestMessages);
            
            // 任务队列
            jedis.rpush("task:queue", "任务A", "任务B", "任务C");
            String task = jedis.lpop("task:queue");
            System.out.println("处理任务: " + task);
            System.out.println("剩余任务: " + jedis.lrange("task:queue", 0, -1));
            
            // 列表长度
            long listLength = jedis.llen("messages:user:1001");
            System.out.println("消息列表长度: " + listLength);
        }
    }
    
    /**
     * 4. 集合操作 - 标签、去重
     */
    public void setOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("\n=== 集合操作 ===");
            
            // 用户标签
            jedis.sadd("user:1001:tags", "VIP", "活跃用户", "北京用户");
            jedis.sadd("user:1002:tags", "普通用户", "活跃用户", "上海用户");
            jedis.sadd("user:1003:tags", "VIP", "新用户", "北京用户");
            
            // 获取用户标签
            Set<String> userTags = jedis.smembers("user:1001:tags");
            System.out.println("用户1001标签: " + userTags);
            
            // 标签交集 - 共同标签
            Set<String> commonTags = jedis.sinter("user:1001:tags", "user:1003:tags");
            System.out.println("用户1001和1003共同标签: " + commonTags);
            
            // 标签并集 - 所有标签
            Set<String> allTags = jedis.sunion("user:1001:tags", "user:1002:tags", "user:1003:tags");
            System.out.println("所有用户标签: " + allTags);
            
            // 标签差集
            Set<String> diffTags = jedis.sdiff("user:1001:tags", "user:1002:tags");
            System.out.println("用户1001独有标签: " + diffTags);
            
            // 在线用户集合
            jedis.sadd("online:users", "1001", "1002", "1003");
            boolean isOnline = jedis.sismember("online:users", "1001");
            System.out.println("用户1001是否在线: " + isOnline);
        }
    }
    
    /**
     * 5. 有序集合操作 - 排行榜、范围查询
     */
    public void sortedSetOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("\n=== 有序集合操作 ===");
            
            // 用户积分排行榜
            jedis.zadd("user:score:rank", 1000, "user:1001");
            jedis.zadd("user:score:rank", 1500, "user:1002");
            jedis.zadd("user:score:rank", 800, "user:1003");
            jedis.zadd("user:score:rank", 2000, "user:1004");
            
            // 获取排行榜前3名
            Set<String> topUsers = jedis.zrevrange("user:score:rank", 0, 2);
            System.out.println("积分排行榜前3名: " + topUsers);
            
            // 获取用户排名
            Long rank = jedis.zrevrank("user:score:rank", "user:1002");
            System.out.println("用户1002排名: " + (rank != null ? rank + 1 : "未上榜"));
            
            // 获取积分范围用户
            Set<String> scoreRangeUsers = jedis.zrangeByScore("user:score:rank", 1000, 1500);
            System.out.println("积分1000-1500的用户: " + scoreRangeUsers);
            
            // 增加用户积分
            jedis.zincrby("user:score:rank", 500, "user:1001");
            Double newScore = jedis.zscore("user:score:rank", "user:1001");
            System.out.println("用户1001新积分: " + newScore);
            
            // 文章点赞数排行榜
            jedis.zadd("article:likes:rank", 50, "article:001");
            jedis.zadd("article:likes:rank", 30, "article:002");
            jedis.zadd("article:likes:rank", 80, "article:003");
            
            Set<String> popularArticles = jedis.zrevrange("article:likes:rank", 0, 2);
            System.out.println("热门文章: " + popularArticles);
        }
    }
    
    /**
     * 6. 位图操作 - 用户签到、统计
     */
    public void bitmapOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("\n=== 位图操作 ===");
            
            // 用户签到记录(按天)
            String today = "2024-01-15";
            String yesterday = "2024-01-14";
            
            // 用户1001签到
            jedis.setbit("sign:user:1001:" + today, 0, true);
            jedis.setbit("sign:user:1001:" + yesterday, 0, true);
            
            // 用户1002签到
            jedis.setbit("sign:user:1002:" + today, 0, true);
            
            // 检查用户是否签到
            boolean hasSigned = jedis.getbit("sign:user:1001:" + today, 0);
            System.out.println("用户1001今天是否签到: " + hasSigned);
            
            // 统计签到用户数
            long signCount = jedis.bitcount("sign:user:1001:" + today);
            System.out.println("用户1001今日签到次数: " + signCount);
            
            // 统计连续签到天数
            int consecutiveDays = 0;
            for (int i = 0; i < 7; i++) {
                String date = "2024-01-" + (15 - i);
                if (jedis.getbit("sign:user:1001:" + date, 0)) {
                    consecutiveDays++;
                } else {
                    break;
                }
            }
            System.out.println("用户1001连续签到天数: " + consecutiveDays);
        }
    }
    
    public static void main(String[] args) {
        RedisDataStructuresDemo demo = new RedisDataStructuresDemo();
        
        demo.stringOperations();
        demo.hashOperations();
        demo.listOperations();
        demo.setOperations();
        demo.sortedSetOperations();
        demo.bitmapOperations();
    }
}

2. Redis发布订阅模式

Redis发布订阅实现

java 复制代码
// RedisPubSubDemo.java
package com.example.demo.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RedisPubSubDemo {
    
    private JedisPool jedisPool;
    private ExecutorService executorService;
    
    public RedisPubSubDemo() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);
        
        this.jedisPool = new JedisPool(config, "localhost", 6379);
        this.executorService = Executors.newFixedThreadPool(5);
    }
    
    /**
     * 消息发布者
     */
    public void publishMessages() {
        try (Jedis jedis = jedisPool.getResource()) {
            System.out.println("=== 消息发布者 ===");
            
            // 发布订单消息
            jedis.publish("order:events", "订单创建: ORDER-001");
            jedis.publish("order:events", "订单支付: ORDER-001");
            jedis.publish("order:events", "订单完成: ORDER-001");
            
            // 发布用户消息
            jedis.publish("user:events", "用户注册: USER-1001");
            jedis.publish("user:events", "用户登录: USER-1001");
            
            // 发布系统消息
            jedis.publish("system:alerts", "系统维护通知");
            jedis.publish("system:alerts", "数据库连接异常");
            
            System.out.println("消息发布完成");
        }
    }
    
    /**
     * 订单事件订阅者
     */
    public void subscribeOrderEvents() {
        executorService.submit(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.subscribe(new JedisPubSub() {
                    @Override
                    public void onMessage(String channel, String message) {
                        System.out.println("订单事件订阅者收到消息: [" + channel + "] " + message);
                        
                        // 处理订单事件
                        if (message.contains("订单创建")) {
                            handleOrderCreated(message);
                        } else if (message.contains("订单支付")) {
                            handleOrderPaid(message);
                        } else if (message.contains("订单完成")) {
                            handleOrderCompleted(message);
                        }
                    }
                    
                    @Override
                    public void onSubscribe(String channel, int subscribedChannels) {
                        System.out.println("订阅订单事件频道: " + channel);
                    }
                    
                    @Override
                    public void onUnsubscribe(String channel, int subscribedChannels) {
                        System.out.println("取消订阅订单事件频道: " + channel);
                    }
                }, "order:events");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    
    /**
     * 用户事件订阅者
     */
    public void subscribeUserEvents() {
        executorService.submit(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.subscribe(new JedisPubSub() {
                    @Override
                    public void onMessage(String channel, String message) {
                        System.out.println("用户事件订阅者收到消息: [" + channel + "] " + message);
                        
                        // 处理用户事件
                        if (message.contains("用户注册")) {
                            handleUserRegistered(message);
                        } else if (message.contains("用户登录")) {
                            handleUserLoggedIn(message);
                        }
                    }
                    
                    @Override
                    public void onSubscribe(String channel, int subscribedChannels) {
                        System.out.println("订阅用户事件频道: " + channel);
                    }
                }, "user:events");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    
    /**
     * 系统告警订阅者
     */
    public void subscribeSystemAlerts() {
        executorService.submit(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.subscribe(new JedisPubSub() {
                    @Override
                    public void onMessage(String channel, String message) {
                        System.out.println("系统告警订阅者收到消息: [" + channel + "] " + message);
                        
                        // 处理系统告警
                        handleSystemAlert(message);
                    }
                    
                    @Override
                    public void onSubscribe(String channel, int subscribedChannels) {
                        System.out.println("订阅系统告警频道: " + channel);
                    }
                }, "system:alerts");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    
    // 事件处理方法
    private void handleOrderCreated(String message) {
        System.out.println("处理订单创建事件: " + message);
    }
    
    private void handleOrderPaid(String message) {
        System.out.println("处理订单支付事件: " + message);
    }
    
    private void handleOrderCompleted(String message) {
        System.out.println("处理订单完成事件: " + message);
    }
    
    private void handleUserRegistered(String message) {
        System.out.println("处理用户注册事件: " + message);
    }
    
    private void handleUserLoggedIn(String message) {
        System.out.println("处理用户登录事件: " + message);
    }
    
    private void handleSystemAlert(String message) {
        System.out.println("处理系统告警: " + message);
    }
    
    public static void main(String[] args) throws InterruptedException {
        RedisPubSubDemo demo = new RedisPubSubDemo();
        
        // 启动订阅者
        demo.subscribeOrderEvents();
        demo.subscribeUserEvents();
        demo.subscribeSystemAlerts();
        
        // 等待订阅者启动
        Thread.sleep(2000);
        
        // 发布消息
        demo.publishMessages();
        
        // 等待消息处理
        Thread.sleep(3000);
        
        demo.executorService.shutdown();
    }
}

✅ 第二部分:Spring Boot Redis集成(90分钟)

1. Redis配置与连接池

Redis配置类

java 复制代码
// RedisConfig.java
package com.example.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.time.Duration;

@Configuration
public class RedisConfig {

    @Value("${spring.redis.host:localhost}")
    private String host;
    
    @Value("${spring.redis.port:6379}")
    private int port;
    
    @Value("${spring.redis.password:}")
    private String password;
    
    @Value("${spring.redis.database:0}")
    private int database;
    
    @Value("${spring.redis.timeout:2000}")
    private int timeout;
    
    @Value("${spring.redis.lettuce.pool.max-active:20}")
    private int maxActive;
    
    @Value("${spring.redis.lettuce.pool.max-idle:10}")
    private int maxIdle;
    
    @Value("${spring.redis.lettuce.pool.min-idle:5}")
    private int minIdle;
    
    @Value("${spring.redis.lettuce.pool.max-wait:2000}")
    private long maxWait;

    /**
     * Redis连接工厂配置
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // Redis单机配置
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName(host);
        redisConfig.setPort(port);
        redisConfig.setPassword(password);
        redisConfig.setDatabase(database);
        
        // 连接池配置
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(maxActive);
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMinIdle(minIdle);
        poolConfig.setMaxWaitMillis(maxWait);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        
        // Lettuce客户端配置
        LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .poolConfig(poolConfig)
                .commandTimeout(Duration.ofMillis(timeout))
                .shutdownTimeout(Duration.ofMillis(100))
                .build();
        
        return new LettuceConnectionFactory(redisConfig, clientConfig);
    }

    /**
     * RedisTemplate配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        
        // 设置key和value的序列化规则
        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(serializer);
        
        // 设置支持事务
        template.setEnableTransactionSupport(true);
        template.afterPropertiesSet();
        
        return template;
    }

    /**
     * 字符串RedisTemplate
     */
    @Bean
    public RedisTemplate<String, String> stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setValueSerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setHashValueSerializer(stringSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

2. Redis工具类

Redis工具类

java 复制代码
// RedisUtil.java
package com.example.demo.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 指定缓存失效时间
     */
    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;
        }
    }

    /**
     * 根据key获取过期时间
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================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);
    }

    // ================================Map=================================

    /**
     * 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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================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 long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } 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;
        }
    }

    /**
     * 将list放入缓存
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ================================zset=================================

    /**
     * 添加元素到有序集合
     */
    public boolean zAdd(String key, Object value, double score) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取有序集合的成员数
     */
    public long zCard(String key) {
        try {
            return redisTemplate.opsForZSet().zCard(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取有序集合中指定成员的分数
     */
    public Double zScore(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().score(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取有序集合中指定排名范围的成员
     */
    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;
        }
    }

    /**
     * 获取有序集合中指定分数范围的成员
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().rangeByScore(key, min, max);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 分布式锁
     */
    public boolean lock(String key, String value, long expireTime) {
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                           "return redis.call('del', KEYS[1]) " +
                           "else " +
                           "return 0 " +
                           "end";
            
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(script);
            redisScript.setResultType(Long.class);
            
            Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
            return result != null && result == 1;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取分布式锁
     */
    public boolean tryLock(String key, String value, long expireTime) {
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

3. 缓存服务实现

缓存服务类

java 复制代码
// CacheService.java
package com.example.demo.service;

import com.example.demo.util.RedisUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class CacheService {

    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 缓存用户信息
     */
    public void cacheUserInfo(Long userId, Object userInfo) {
        String key = "user:info:" + userId;
        try {
            String json = objectMapper.writeValueAsString(userInfo);
            redisUtil.set(key, json, 3600); // 缓存1小时
            log.info("缓存用户信息成功: {}", userId);
        } catch (JsonProcessingException e) {
            log.error("缓存用户信息失败: {}", userId, e);
        }
    }

    /**
     * 获取用户信息
     */
    public <T> T getUserInfo(Long userId, Class<T> clazz) {
        String key = "user:info:" + userId;
        Object value = redisUtil.get(key);
        if (value != null) {
            try {
                return objectMapper.readValue(value.toString(), clazz);
            } catch (JsonProcessingException e) {
                log.error("获取用户信息失败: {}", userId, e);
            }
        }
        return null;
    }

    /**
     * 缓存商品信息
     */
    public void cacheProductInfo(Long productId, Object productInfo) {
        String key = "product:info:" + productId;
        try {
            String json = objectMapper.writeValueAsString(productInfo);
            redisUtil.set(key, json, 1800); // 缓存30分钟
            log.info("缓存商品信息成功: {}", productId);
        } catch (JsonProcessingException e) {
            log.error("缓存商品信息失败: {}", productId, e);
        }
    }

    /**
     * 获取商品信息
     */
    public <T> T getProductInfo(Long productId, Class<T> clazz) {
        String key = "product:info:" + productId;
        Object value = redisUtil.get(key);
        if (value != null) {
            try {
                return objectMapper.readValue(value.toString(), clazz);
            } catch (JsonProcessingException e) {
                log.error("获取商品信息失败: {}", productId, e);
            }
        }
        return null;
    }

    /**
     * 缓存热点数据
     */
    public void cacheHotData(String dataType, String dataKey, Object data) {
        String key = "hot:data:" + dataType + ":" + dataKey;
        try {
            String json = objectMapper.writeValueAsString(data);
            redisUtil.set(key, json, 600); // 缓存10分钟
            log.info("缓存热点数据成功: {}:{}", dataType, dataKey);
        } catch (JsonProcessingException e) {
            log.error("缓存热点数据失败: {}:{}", dataType, dataKey, e);
        }
    }

    /**
     * 获取热点数据
     */
    public <T> T getHotData(String dataType, String dataKey, Class<T> clazz) {
        String key = "hot:data:" + dataType + ":" + dataKey;
        Object value = redisUtil.get(key);
        if (value != null) {
            try {
                return objectMapper.readValue(value.toString(), clazz);
            } catch (JsonProcessingException e) {
                log.error("获取热点数据失败: {}:{}", dataType, dataKey, e);
            }
        }
        return null;
    }

    /**
     * 删除缓存
     */
    public void deleteCache(String key) {
        redisUtil.del(key);
        log.info("删除缓存成功: {}", key);
    }

    /**
     * 批量删除缓存
     */
    public void deleteCacheByPattern(String pattern) {
        // 这里需要实现根据模式删除缓存的逻辑
        log.info("批量删除缓存: {}", pattern);
    }

    /**
     * 缓存计数器
     */
    public long incrementCounter(String key, long delta) {
        return redisUtil.incr(key, delta);
    }

    /**
     * 获取计数器值
     */
    public long getCounter(String key) {
        Object value = redisUtil.get(key);
        return value != null ? Long.parseLong(value.toString()) : 0;
    }

    /**
     * 设置计数器过期时间
     */
    public void setCounterExpire(String key, long seconds) {
        redisUtil.expire(key, seconds);
    }
}

✅ 第三部分:分布式缓存一致性(90分钟)

1. 缓存一致性策略

缓存一致性策略实现

java 复制代码
// CacheConsistencyStrategy.java
package com.example.demo.cache;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class CacheConsistencyStrategy {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 1. Cache Aside模式 - 旁路缓存
     * 读:先读缓存,缓存没有则读数据库,然后写入缓存
     * 写:先写数据库,然后删除缓存
     */
    public class CacheAsideStrategy {
        
        /**
         * 读取数据
         */
        public <T> T read(String cacheKey, Class<T> clazz, DataLoader<T> dataLoader) {
            // 1. 先读缓存
            T cachedData = (T) redisTemplate.opsForValue().get(cacheKey);
            if (cachedData != null) {
                log.info("缓存命中: {}", cacheKey);
                return cachedData;
            }
            
            // 2. 缓存未命中,读数据库
            log.info("缓存未命中,从数据库读取: {}", cacheKey);
            T data = dataLoader.load();
            
            // 3. 写入缓存
            if (data != null) {
                redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
                log.info("数据写入缓存: {}", cacheKey);
            }
            
            return data;
        }
        
        /**
         * 写入数据
         */
        public <T> void write(String cacheKey, T data, DataWriter<T> dataWriter) {
            // 1. 先写数据库
            dataWriter.write(data);
            log.info("数据写入数据库: {}", cacheKey);
            
            // 2. 删除缓存
            redisTemplate.delete(cacheKey);
            log.info("删除缓存: {}", cacheKey);
        }
    }

    /**
     * 2. Write Through模式 - 写穿透
     * 写:先写缓存,缓存负责同步写入数据库
     * 读:从缓存读取
     */
    public class WriteThroughStrategy {
        
        /**
         * 写入数据
         */
        public <T> void write(String cacheKey, T data, DataWriter<T> dataWriter) {
            // 1. 先写缓存
            redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
            log.info("数据写入缓存: {}", cacheKey);
            
            // 2. 缓存负责写入数据库
            dataWriter.write(data);
            log.info("数据写入数据库: {}", cacheKey);
        }
        
        /**
         * 读取数据
         */
        public <T> T read(String cacheKey, Class<T> clazz) {
            T data = (T) redisTemplate.opsForValue().get(cacheKey);
            if (data != null) {
                log.info("缓存命中: {}", cacheKey);
            } else {
                log.info("缓存未命中: {}", cacheKey);
            }
            return data;
        }
    }

    /**
     * 3. Write Behind模式 - 写回
     * 写:只写缓存,异步批量写入数据库
     * 读:从缓存读取
     */
    public class WriteBehindStrategy {
        
        /**
         * 写入数据
         */
        public <T> void write(String cacheKey, T data) {
            // 只写缓存,异步写入数据库
            redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
            log.info("数据写入缓存: {}", cacheKey);
            
            // 异步写入数据库(这里简化处理)
            asyncWriteToDatabase(cacheKey, data);
        }
        
        /**
         * 异步写入数据库
         */
        private <T> void asyncWriteToDatabase(String cacheKey, T data) {
            // 实际项目中应该使用消息队列或线程池异步处理
            log.info("异步写入数据库: {}", cacheKey);
        }
    }

    /**
     * 4. 双删策略 - 解决缓存不一致问题
     */
    public class DoubleDeleteStrategy {
        
        /**
         * 更新数据
         */
        public <T> void update(String cacheKey, T data, DataWriter<T> dataWriter) {
            // 1. 删除缓存
            redisTemplate.delete(cacheKey);
            log.info("第一次删除缓存: {}", cacheKey);
            
            // 2. 更新数据库
            dataWriter.write(data);
            log.info("更新数据库: {}", cacheKey);
            
            // 3. 延迟删除缓存
            delayDeleteCache(cacheKey);
        }
        
        /**
         * 延迟删除缓存
         */
        private void delayDeleteCache(String cacheKey) {
            // 延迟删除,避免并发问题
            new Thread(() -> {
                try {
                    Thread.sleep(1000); // 延迟1秒
                    redisTemplate.delete(cacheKey);
                    log.info("延迟删除缓存: {}", cacheKey);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
相关推荐
bemyrunningdog7 小时前
DTO与POJO:核心差异与最佳实践
java
en-route7 小时前
深入理解 MDC(Mapped Diagnostic Context):日志记录的利器
java·log4j
一叶飘零_sweeeet8 小时前
SpringBoot 数据脱敏实战: 构建企业级敏感信息保护体系
java·spring boot·数据安全
float_六七8 小时前
Java Stream流:从入门到精通
java·windows·python
青云交8 小时前
Java 大视界 -- 基于 Java 的大数据分布式存储在智慧城市时空大数据管理与应用中的创新实践(408)
java·hdfs·flink·智慧城市·hbase·java 分布式存储·时空大数据
赶飞机偏偏下雨8 小时前
【Java笔记】单例模式
java·笔记·单例模式
小蒜学长8 小时前
基于Spring Boot的火灾报警系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
武昌库里写JAVA8 小时前
基于Spring Boot + Vue3的办公用品申领管理系统
java·spring boot·后端
中国lanwp8 小时前
Spring Boot的配置文件加载顺序和规则
java·spring boot·后端