Redis相关知识点总结

Redis相关知识

一、Redis基础概念和数据类型

1.1 Redis简介

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可用作数据库、缓存和消息中间件。

核心特性:

  • 基于内存,读写速度极快
  • 支持数据持久化
  • 支持多种数据结构
  • 支持事务、发布订阅、主从复制等特性

1.2 五大基础数据类型

1. String(字符串)

最基本的数据类型,可以存储字符串、整数或浮点数。

命令行示例:

bash 复制代码
# 设置和获取
SET name "张三"
GET name

# 设置过期时间(秒)
SETEX token 3600 "abc123"

# 原子操作
INCR counter
DECR counter
INCRBY counter 5

# 批量操作
MSET key1 "value1" key2 "value2"
MGET key1 key2

Java代码示例(Jedis):

java 复制代码
import redis.clients.jedis.Jedis;

public class StringExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 设置和获取
        jedis.set("username", "张三");
        String username = jedis.get("username");
        System.out.println("用户名: " + username);
        
        // 设置过期时间
        jedis.setex("session_id", 3600, "xyz789");
        
        // 原子递增
        jedis.set("visitor_count", "0");
        Long count = jedis.incr("visitor_count");
        System.out.println("访问次数: " + count);
        
        // 批量操作
        jedis.mset("name", "李四", "age", "25", "city", "北京");
        List<String> values = jedis.mget("name", "age", "city");
        
        jedis.close();
    }
}

Spring Data Redis示例:

java 复制代码
@Service
public class StringRedisService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    // 设置值
    public void setValue(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }
    
    // 设置值并设置过期时间
    public void setValueWithExpire(String key, String value, long timeout) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }
    
    // 获取值
    public String getValue(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
    
    // 原子递增
    public Long increment(String key) {
        return stringRedisTemplate.opsForValue().increment(key);
    }
}
2. Hash(哈希)

存储对象,类似于HashMap。

命令行示例:

bash 复制代码
# 设置和获取
HSET user:1001 name "王五"
HSET user:1001 age 30
HGET user:1001 name

# 批量设置
HMSET user:1002 name "赵六" age 25 city "上海"
HGETALL user:1002

# 获取所有字段
HKEYS user:1002
HVALS user:1002

# 检查字段是否存在
HEXISTS user:1002 name

# 删除字段
HDEL user:1002 city

Java代码示例:

java 复制代码
public class HashExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 存储用户对象
        Map<String, String> user = new HashMap<>();
        user.put("id", "1001");
        user.put("name", "张三");
        user.put("email", "zhangsan@example.com");
        user.put("age", "28");
        
        jedis.hmset("user:1001", user);
        
        // 获取单个字段
        String name = jedis.hget("user:1001", "name");
        
        // 获取多个字段
        List<String> fields = jedis.hmget("user:1001", "name", "email");
        
        // 获取所有字段和值
        Map<String, String> userInfo = jedis.hgetAll("user:1001");
        
        // 修改字段
        jedis.hset("user:1001", "age", "29");
        
        // 字段递增
        jedis.hincrBy("user:1001", "age", 1);
        
        jedis.close();
    }
}

Spring Data Redis示例:

java 复制代码
@Service
public class HashRedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 存储对象
    public void saveUser(String userId, User user) {
        String key = "user:" + userId;
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("id", user.getId());
        userMap.put("name", user.getName());
        userMap.put("email", user.getEmail());
        userMap.put("age", String.valueOf(user.getAge()));
        
        redisTemplate.opsForHash().putAll(key, userMap);
    }
    
    // 获取对象
    public User getUser(String userId) {
        String key = "user:" + userId;
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        
        User user = new User();
        user.setId((String) entries.get("id"));
        user.setName((String) entries.get("name"));
        user.setEmail((String) entries.get("email"));
        user.setAge(Integer.parseInt((String) entries.get("age")));
        
        return user;
    }
    
    // 更新字段
    public void updateField(String userId, String field, Object value) {
        String key = "user:" + userId;
        redisTemplate.opsForHash().put(key, field, value);
    }
}
3. List(列表)

有序的字符串列表,可以从两端插入和删除。

命令行示例:

bash 复制代码
# 左侧插入
LPUSH messages "消息1"
LPUSH messages "消息2"

# 右侧插入
RPUSH messages "消息3"

# 获取列表
LRANGE messages 0 -1

# 弹出元素
LPOP messages
RPOP messages

# 获取长度
LLEN messages

# 按索引获取
LINDEX messages 0

# 修剪列表(保留指定范围)
LTRIM messages 0 99

Java代码示例:

java 复制代码
public class ListExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 消息队列示例
        String queueKey = "task:queue";
        
        // 生产者:添加任务
        jedis.lpush(queueKey, "任务1");
        jedis.lpush(queueKey, "任务2");
        jedis.lpush(queueKey, "任务3");
        
        // 消费者:处理任务(阻塞弹出)
        List<String> task = jedis.brpop(10, queueKey); // 最多等待10秒
        if (task != null) {
            System.out.println("处理任务: " + task.get(1));
        }
        
        // 获取最新的N条记录
        String logKey = "app:logs";
        jedis.lpush(logKey, "日志1");
        jedis.lpush(logKey, "日志2");
        jedis.lpush(logKey, "日志3");
        
        // 只保留最新的100条
        jedis.ltrim(logKey, 0, 99);
        
        // 获取最新的10条
        List<String> recentLogs = jedis.lrange(logKey, 0, 9);
        
        jedis.close();
    }
}

Spring Data Redis示例:

java 复制代码
@Service
public class ListRedisService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 消息队列 - 生产者
    public void pushMessage(String queue, String message) {
        redisTemplate.opsForList().leftPush(queue, message);
    }
    
    // 消息队列 - 消费者
    public String popMessage(String queue) {
        return redisTemplate.opsForList().rightPop(queue, 10, TimeUnit.SECONDS);
    }
    
    // 添加到最近浏览列表
    public void addToRecentList(String userId, String itemId) {
        String key = "recent:" + userId;
        
        // 移除已存在的
        redisTemplate.opsForList().remove(key, 1, itemId);
        
        // 添加到列表头部
        redisTemplate.opsForList().leftPush(key, itemId);
        
        // 只保留最近的10个
        redisTemplate.opsForList().trim(key, 0, 9);
    }
    
    // 获取最近浏览
    public List<String> getRecentList(String userId) {
        String key = "recent:" + userId;
        return redisTemplate.opsForList().range(key, 0, -1);
    }
}
4. Set(集合)

无序的字符串集合,元素唯一。

命令行示例:

bash 复制代码
# 添加元素
SADD tags "Java"
SADD tags "Redis" "Spring"

# 获取所有元素
SMEMBERS tags

# 检查元素是否存在
SISMEMBER tags "Java"

# 获取集合大小
SCARD tags

# 随机获取元素
SRANDMEMBER tags 2

# 删除元素
SREM tags "Java"

# 集合运算
SADD user:1:follows "user:2" "user:3"
SADD user:2:follows "user:1" "user:3"

# 交集(共同关注)
SINTER user:1:follows user:2:follows

# 并集(所有关注)
SUNION user:1:follows user:2:follows

# 差集(user1关注但user2未关注的)
SDIFF user:1:follows user:2:follows

Java代码示例:

java 复制代码
public class SetExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 标签系统
        String articleKey = "article:1001:tags";
        jedis.sadd(articleKey, "Java", "Spring", "Redis");
        
        // 获取所有标签
        Set<String> tags = jedis.smembers(articleKey);
        
        // 用户关注系统
        String user1Follows = "user:1001:follows";
        String user2Follows = "user:1002:follows";
        
        jedis.sadd(user1Follows, "1002", "1003", "1004");
        jedis.sadd(user2Follows, "1001", "1003", "1005");
        
        // 共同关注
        Set<String> commonFollows = jedis.sinter(user1Follows, user2Follows);
        System.out.println("共同关注: " + commonFollows);
        
        // 推荐关注(user2关注但user1未关注的)
        Set<String> recommendations = jedis.sdiff(user2Follows, user1Follows);
        System.out.println("推荐关注: " + recommendations);
        
        // 抽奖系统
        String lotteryKey = "lottery:participants";
        jedis.sadd(lotteryKey, "user1", "user2", "user3", "user4", "user5");
        
        // 随机抽取3个中奖者
        Set<String> winners = jedis.srandmember(lotteryKey, 3);
        System.out.println("中奖者: " + winners);
        
        jedis.close();
    }
}

Spring Data Redis示例:

java 复制代码
@Service
public class SetRedisService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 点赞功能
    public void like(String articleId, String userId) {
        String key = "article:likes:" + articleId;
        redisTemplate.opsForSet().add(key, userId);
    }
    
    // 取消点赞
    public void unlike(String articleId, String userId) {
        String key = "article:likes:" + articleId;
        redisTemplate.opsForSet().remove(key, userId);
    }
    
    // 检查是否点赞
    public boolean isLiked(String articleId, String userId) {
        String key = "article:likes:" + articleId;
        return redisTemplate.opsForSet().isMember(key, userId);
    }
    
    // 获取点赞数
    public Long getLikeCount(String articleId) {
        String key = "article:likes:" + articleId;
        return redisTemplate.opsForSet().size(key);
    }
    
    // 获取共同好友
    public Set<String> getCommonFriends(String userId1, String userId2) {
        String key1 = "user:friends:" + userId1;
        String key2 = "user:friends:" + userId2;
        return redisTemplate.opsForSet().intersect(key1, key2);
    }
}
5. ZSet(有序集合)

有序的字符串集合,每个元素关联一个分数。

命令行示例:

bash 复制代码
# 添加元素
ZADD ranking 100 "张三"
ZADD ranking 85 "李四"
ZADD ranking 95 "王五"

# 获取排名(从高到低)
ZREVRANGE ranking 0 -1 WITHSCORES

# 获取排名(从低到高)
ZRANGE ranking 0 -1 WITHSCORES

# 获取分数
ZSCORE ranking "张三"

# 获取排名
ZREVRANK ranking "张三"  # 从高到低的排名
ZRANK ranking "张三"      # 从低到高的排名

# 增加分数
ZINCRBY ranking 10 "李四"

# 获取指定分数范围
ZRANGEBYSCORE ranking 90 100

# 删除元素
ZREM ranking "王五"

Java代码示例:

java 复制代码
public class ZSetExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 游戏排行榜
        String leaderboardKey = "game:leaderboard";
        
        // 添加玩家分数
        jedis.zadd(leaderboardKey, 2500, "player1");
        jedis.zadd(leaderboardKey, 3200, "player2");
        jedis.zadd(leaderboardKey, 2800, "player3");
        jedis.zadd(leaderboardKey, 3500, "player4");
        
        // 获取TOP 3
        Set<Tuple> top3 = jedis.zrevrangeWithScores(leaderboardKey, 0, 2);
        System.out.println("TOP 3 玩家:");
        for (Tuple player : top3) {
            System.out.println(player.getElement() + ": " + player.getScore());
        }
        
        // 获取玩家排名(0-based)
        Long rank = jedis.zrevrank(leaderboardKey, "player3");
        System.out.println("player3 排名: " + (rank + 1));
        
        // 增加分数
        jedis.zincrby(leaderboardKey, 500, "player1");
        
        // 热门文章(按浏览量排序)
        String hotArticlesKey = "articles:hot";
        
        jedis.zadd(hotArticlesKey, 1000, "article:1");
        jedis.zadd(hotArticlesKey, 1500, "article:2");
        jedis.zadd(hotArticlesKey, 800, "article:3");
        
        // 文章被浏览
        jedis.zincrby(hotArticlesKey, 1, "article:1");
        
        // 获取热门文章TOP 10
        Set<String> hotArticles = jedis.zrevrange(hotArticlesKey, 0, 9);
        
        jedis.close();
    }
}

Spring Data Redis示例:

java 复制代码
@Service
public class ZSetRedisService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 更新排行榜分数
    public void updateScore(String leaderboard, String player, double score) {
        redisTemplate.opsForZSet().add(leaderboard, player, score);
    }
    
    // 增加分数
    public void incrementScore(String leaderboard, String player, double delta) {
        redisTemplate.opsForZSet().incrementScore(leaderboard, player, delta);
    }
    
    // 获取排行榜TOP N
    public Set<ZSetOperations.TypedTuple<String>> getTopN(String leaderboard, int n) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(leaderboard, 0, n - 1);
    }
    
    // 获取玩家排名
    public Long getRank(String leaderboard, String player) {
        Long rank = redisTemplate.opsForZSet().reverseRank(leaderboard, player);
        return rank != null ? rank + 1 : null;
    }
    
    // 延迟队列实现
    public void addDelayedTask(String queue, String task, long delaySeconds) {
        long score = System.currentTimeMillis() + (delaySeconds * 1000);
        redisTemplate.opsForZSet().add(queue, task, score);
    }
    
    // 获取到期的任务
    public Set<String> getExpiredTasks(String queue) {
        long now = System.currentTimeMillis();
        Set<String> tasks = redisTemplate.opsForZSet().rangeByScore(queue, 0, now);
        
        // 删除已获取的任务
        if (!tasks.isEmpty()) {
            redisTemplate.opsForZSet().removeRangeByScore(queue, 0, now);
        }
        
        return tasks;
    }
}

二、Redis持久化机制

Redis提供两种持久化方式:RDB和AOF。

2.1 RDB(Redis Database)

定时将内存中的数据快照保存到磁盘。

配置示例(redis.conf):

bash 复制代码
# 保存策略
save 900 1      # 900秒内至少1个key变化则保存
save 300 10     # 300秒内至少10个key变化则保存
save 60 10000   # 60秒内至少10000个key变化则保存

# RDB文件名
dbfilename dump.rdb

# RDB文件目录
dir /var/lib/redis/

# 压缩
rdbcompression yes

# 校验
rdbchecksum yes

手动触发RDB:

bash 复制代码
# 阻塞保存
SAVE

# 后台保存(推荐)
BGSAVE

# 获取最后保存时间
LASTSAVE

Java代码示例:

java 复制代码
public class RDBExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 手动触发后台保存
        String result = jedis.bgsave();
        System.out.println("BGSAVE结果: " + result);
        
        // 获取最后保存时间
        Long lastSaveTime = jedis.lastsave();
        Date lastSave = new Date(lastSaveTime * 1000);
        System.out.println("最后保存时间: " + lastSave);
        
        // 通过CONFIG命令查看和修改配置
        List<String> saveConfig = jedis.configGet("save");
        System.out.println("当前save配置: " + saveConfig);
        
        jedis.close();
    }
}

2.2 AOF(Append Only File)

将每个写操作记录到日志文件。

配置示例(redis.conf):

bash 复制代码
# 开启AOF
appendonly yes

# AOF文件名
appendfilename "appendonly.aof"

# 同步策略
appendfsync everysec  # 每秒同步(推荐)
# appendfsync always   # 每次写操作都同步
# appendfsync no       # 由操作系统决定

# AOF重写
auto-aof-rewrite-percentage 100  # 文件增长100%时重写
auto-aof-rewrite-min-size 64mb   # 文件最小64MB才重写

AOF管理命令:

bash 复制代码
# 手动重写AOF
BGREWRITEAOF

# 查看AOF状态
INFO persistence

Java代码配置示例:

java 复制代码
@Configuration
public class RedisPersistenceConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("localhost");
        config.setPort(6379);
        
        // 配置连接池
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(10);
        poolConfig.setMaxIdle(5);
        poolConfig.setMinIdle(1);
        
        LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration
                .builder()
                .poolConfig(poolConfig)
                .build();
        
        return new LettuceConnectionFactory(config, clientConfig);
    }
    
    // 定时触发RDB保存
    @Component
    public class RedisPersistenceTask {
        @Autowired
        private StringRedisTemplate redisTemplate;
        
        @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨2点
        public void triggerBgSave() {
            redisTemplate.execute((RedisCallback<String>) connection -> {
                connection.bgSave();
                return "OK";
            });
        }
    }
}

三、Redis事务和管道

3.1 Redis事务

Redis事务通过MULTI、EXEC、DISCARD和WATCH命令实现。

命令行示例:

bash 复制代码
# 开启事务
MULTI

# 事务中的命令(入队)
SET key1 "value1"
SET key2 "value2"
INCR counter

# 执行事务
EXEC

# 取消事务
MULTI
SET key3 "value3"
DISCARD  # 取消事务

# 乐观锁(WATCH)
WATCH balance
MULTI
DECRBY balance 100
INCRBY spending 100
EXEC  # 如果balance在事务期间被修改,事务会失败

Java事务示例:

java 复制代码
public class TransactionExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 转账示例
        String account1 = "account:1001";
        String account2 = "account:1002";
        
        // 初始化账户
        jedis.set(account1, "1000");
        jedis.set(account2, "500");
        
        // 执行转账事务
        Transaction tx = jedis.multi();
        try {
            tx.decrBy(account1, 200);  // 账户1减200
            tx.incrBy(account2, 200);  // 账户2加200
            
            List<Object> results = tx.exec();
            if (results != null) {
                System.out.println("转账成功");
            } else {
                System.out.println("转账失败");
            }
        } catch (Exception e) {
            tx.discard();
            System.out.println("事务异常: " + e.getMessage());
        }
        
        // 使用WATCH实现乐观锁
        boolean success = false;
        while (!success) {
            jedis.watch(account1);
            
            int balance = Integer.parseInt(jedis.get(account1));
            if (balance >= 100) {
                Transaction transaction = jedis.multi();
                transaction.decrBy(account1, 100);
                List<Object> result = transaction.exec();
                
                if (result != null) {
                    success = true;
                    System.out.println("扣款成功");
                } else {
                    System.out.println("账户被其他事务修改,重试...");
                }
            } else {
                jedis.unwatch();
                System.out.println("余额不足");
                break;
            }
        }
        
        jedis.close();
    }
}

Spring Data Redis事务示例:

java 复制代码
@Service
public class TransactionService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 简单事务
    public void simpleTransaction() {
        redisTemplate.execute(new SessionCallback<List<Object>>() {
            @Override
            public List<Object> execute(RedisOperations operations) {
                operations.multi();
                
                operations.opsForValue().set("key1", "value1");
                operations.opsForValue().set("key2", "value2");
                operations.opsForValue().increment("counter");
                
                return operations.exec();
            }
        });
    }
    
    // 转账事务(带乐观锁)
    public boolean transfer(String fromAccount, String toAccount, int amount) {
        return redisTemplate.execute(new SessionCallback<Boolean>() {
            @Override
            public Boolean execute(RedisOperations operations) {
                operations.watch(fromAccount);
                
                String balanceStr = (String) operations.opsForValue().get(fromAccount);
                int balance = Integer.parseInt(balanceStr);
                
                if (balance < amount) {
                    operations.unwatch();
                    return false;
                }
                
                operations.multi();
                operations.opsForValue().decrement(fromAccount, amount);
                operations.opsForValue().increment(toAccount, amount);
                
                List<Object> results = operations.exec();
                return results != null && !results.isEmpty();
            }
        });
    }
}

3.2 Pipeline(管道)

批量执行命令,减少网络往返时间。

Java Pipeline示例:

java 复制代码
public class PipelineExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 不使用Pipeline(慢)
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            jedis.set("key" + i, "value" + i);
        }
        System.out.println("普通方式耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // 使用Pipeline(快)
        start = System.currentTimeMillis();
        Pipeline pipeline = jedis.pipelined();
        for (int i = 0; i < 10000; i++) {
            pipeline.set("pkey" + i, "value" + i);
        }
        pipeline.sync();  // 执行所有命令
        System.out.println("Pipeline方式耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // Pipeline获取响应
        Pipeline p = jedis.pipelined();
        Response<String> r1 = p.get("key1");
        Response<String> r2 = p.get("key2");
        Response<Long> r3 = p.incr("counter");
        p.sync();
        
        System.out.println("key1: " + r1.get());
        System.out.println("key2: " + r2.get());
        System.out.println("counter: " + r3.get());
        
        jedis.close();
    }
}

Spring Data Redis Pipeline示例:

java 复制代码
@Service
public class PipelineService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 批量设置
    public void batchSet(Map<String, String> data) {
        redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) {
                for (Map.Entry<String, String> entry : data.entrySet()) {
                    operations.opsForValue().set(entry.getKey(), entry.getValue());
                }
                return null;
            }
        });
    }
    
    // 批量获取
    public List<Object> batchGet(List<String> keys) {
        return redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) {
                for (String key : keys) {
                    operations.opsForValue().get(key);
                }
                return null;
            }
        });
    }
    
    // 批量操作不同数据类型
    public void mixedOperations() {
        List<Object> results = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection stringConnection = (StringRedisConnection) connection;
            
            // String操作
            stringConnection.set("string:key", "value");
            
            // Hash操作
            stringConnection.hSet("hash:key", "field", "value");
            
            // List操作
            stringConnection.lPush("list:key", "item1", "item2");
            
            // Set操作
            stringConnection.sAdd("set:key", "member1", "member2");
            
            // ZSet操作
            stringConnection.zAdd("zset:key", 100, "member1");
            
            return null;
        });
    }
}

四、Redis发布订阅

Redis提供了发布订阅(Pub/Sub)消息模式。

4.1 基本命令

命令行示例:

bash 复制代码
# 订阅者终端1
SUBSCRIBE news sports

# 订阅者终端2(模式订阅)
PSUBSCRIBE news:*

# 发布者终端
PUBLISH news "重要新闻"
PUBLISH news:tech "科技新闻"
PUBLISH sports "体育新闻"

# 查看活跃频道
PUBSUB CHANNELS

# 查看频道订阅者数量
PUBSUB NUMSUB news sports

4.2 Java实现

Jedis发布订阅示例:

java 复制代码
// 订阅者
public class Subscriber extends JedisPubSub {
    @Override
    public void onMessage(String channel, String message) {
        System.out.println("收到消息 - 频道: " + channel + ", 内容: " + message);
    }
    
    @Override
    public void onPMessage(String pattern, String channel, String message) {
        System.out.println("模式订阅 - 模式: " + pattern + ", 频道: " + channel + ", 内容: " + message);
    }
    
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        System.out.println("订阅频道: " + channel);
    }
}

// 订阅者线程
public class SubscriberThread extends Thread {
    private Jedis jedis;
    private Subscriber subscriber;
    private String[] channels;
    
    public SubscriberThread(String[] channels) {
        this.jedis = new Jedis("localhost", 6379);
        this.subscriber = new Subscriber();
        this.channels = channels;
    }
    
    @Override
    public void run() {
        jedis.subscribe(subscriber, channels);
    }
    
    public void unsubscribe() {
        subscriber.unsubscribe();
    }
}

// 发布者
public class Publisher {
    public static void main(String[] args) throws InterruptedException {
        Jedis jedis = new Jedis("localhost", 6379);
        
        // 启动订阅者
        SubscriberThread sub1 = new SubscriberThread(new String[]{"news", "sports"});
        sub1.start();
        
        Thread.sleep(1000);  // 等待订阅者就绪
        
        // 发布消息
        jedis.publish("news", "今日头条新闻");
        jedis.publish("sports", "体育赛事直播");
        jedis.publish("news", "突发新闻");
        
        // 查看订阅者数量
        Map<String, String> result = jedis.pubsubNumSub("news", "sports");
        System.out.println("订阅者数量: " + result);
        
        jedis.close();
    }
}

Spring Data Redis发布订阅示例:

java 复制代码
@Configuration
public class RedisPubSubConfig {
    
    @Bean
    public RedisMessageListenerContainer messageListenerContainer(
            RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        
        // 添加消息监听器
        container.addMessageListener(newsMessageListener(), newsTopic());
        container.addMessageListener(sportsMessageListener(), sportsTopic());
        
        // 模式订阅
        container.addMessageListener(patternMessageListener(), 
                new PatternTopic("news:*"));
        
        return container;
    }
    
    @Bean
    public MessageListener newsMessageListener() {
        return (message, pattern) -> {
            String channel = new String(message.getChannel());
            String body = new String(message.getBody());
            System.out.println("新闻频道消息 - " + channel + ": " + body);
        };
    }
    
    @Bean
    public MessageListener sportsMessageListener() {
        return (message, pattern) -> {
            String channel = new String(message.getChannel());
            String body = new String(message.getBody());
            System.out.println("体育频道消息 - " + channel + ": " + body);
        };
    }
    
    @Bean
    public MessageListener patternMessageListener() {
        return (message, pattern) -> {
            String channel = new String(message.getChannel());
            String body = new String(message.getBody());
            System.out.println("模式匹配消息 - " + channel + ": " + body);
        };
    }
    
    @Bean
    public ChannelTopic newsTopic() {
        return new ChannelTopic("news");
    }
    
    @Bean
    public ChannelTopic sportsTopic() {
        return new ChannelTopic("sports");
    }
}

// 发布服务
@Service
public class PublishService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 发布消息
    public void publishMessage(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }
    
    // 发布对象(自动序列化)
    public void publishObject(String channel, Object obj) {
        redisTemplate.convertAndSend(channel, obj);
    }
}

// 实际应用示例:实时通知
@Service
public class NotificationService {
    @Autowired
    private PublishService publishService;
    
    // 发送系统通知
    public void sendSystemNotification(String message) {
        publishService.publishMessage("system:notification", message);
    }
    
    // 发送用户消息
    public void sendUserMessage(String userId, String message) {
        publishService.publishMessage("user:" + userId, message);
    }
    
    // 发送订单状态更新
    public void sendOrderUpdate(String orderId, String status) {
        Map<String, String> update = new HashMap<>();
        update.put("orderId", orderId);
        update.put("status", status);
        update.put("timestamp", String.valueOf(System.currentTimeMillis()));
        
        publishService.publishObject("order:updates", update);
    }
}

五、Redis集群和主从复制

5.1 主从复制

主从复制实现读写分离,提高性能和可用性。

配置从节点(redis.conf):

bash 复制代码
# 从节点配置
replicaof 127.0.0.1 6379  # 指定主节点
masterauth password123     # 主节点密码(如果有)

# 从节点只读
replica-read-only yes

# 复制积压缓冲区大小
repl-backlog-size 64mb

命令行操作:

bash 复制代码
# 动态设置从节点
REPLICAOF 127.0.0.1 6379

# 查看复制信息
INFO replication

# 取消复制(变为主节点)
REPLICAOF NO ONE

Java代码配置主从:

java 复制代码
// 读写分离配置
@Configuration
public class RedisReplicationConfig {
    
    // 主节点(写操作)
    @Bean
    @Primary
    public LettuceConnectionFactory masterConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("127.0.0.1");
        config.setPort(6379);
        config.setPassword("password123");
        return new LettuceConnectionFactory(config);
    }
    
    // 从节点(读操作)
    @Bean
    public LettuceConnectionFactory slaveConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("127.0.0.1");
        config.setPort(6380);  // 从节点端口
        config.setPassword("password123");
        return new LettuceConnectionFactory(config);
    }
    
    // 写操作模板
    @Bean
    public RedisTemplate<String, Object> masterRedisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(masterConnectionFactory());
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
    
    // 读操作模板
    @Bean
    public RedisTemplate<String, Object> slaveRedisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(slaveConnectionFactory());
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

// 读写分离服务
@Service
public class ReadWriteSplitService {
    @Autowired
    @Qualifier("masterRedisTemplate")
    private RedisTemplate<String, Object> masterTemplate;
    
    @Autowired
    @Qualifier("slaveRedisTemplate")
    private RedisTemplate<String, Object> slaveTemplate;
    
    // 写操作使用主节点
    public void write(String key, Object value) {
        masterTemplate.opsForValue().set(key, value);
    }
    
    // 读操作使用从节点
    public Object read(String key) {
        return slaveTemplate.opsForValue().get(key);
    }
}

5.2 哨兵模式(Sentinel)

哨兵提供自动故障转移和监控。

哨兵配置(sentinel.conf):

bash 复制代码
# 端口
port 26379

# 监控主节点
sentinel monitor mymaster 127.0.0.1 6379 2  # 2个哨兵同意才故障转移

# 主节点密码
sentinel auth-pass mymaster password123

# 故障转移超时
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

Spring Boot配置哨兵:

java 复制代码
@Configuration
public class RedisSentinelConfig {
    
    @Bean
    public LettuceConnectionFactory sentinelConnectionFactory() {
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
                .master("mymaster")
                .sentinel("127.0.0.1", 26379)
                .sentinel("127.0.0.1", 26380)
                .sentinel("127.0.0.1", 26381);
        
        sentinelConfig.setPassword("password123");
        
        return new LettuceConnectionFactory(sentinelConfig);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(sentinelConnectionFactory());
        
        // 配置序列化
        Jackson2JsonRedisSerializer<Object> serializer = 
                new Jackson2JsonRedisSerializer<>(Object.class);
        template.setDefaultSerializer(serializer);
        
        return template;
    }
}

5.3 Redis Cluster(集群)

Redis Cluster提供数据分片和高可用性。

集群配置(redis.conf):

bash 复制代码
# 开启集群模式
cluster-enabled yes

# 集群配置文件
cluster-config-file nodes-6379.conf

# 集群节点超时
cluster-node-timeout 15000

# 集群需要的最小副本数
cluster-migration-barrier 1

创建集群命令:

bash 复制代码
# 创建集群(3主3从)
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1

Spring Boot配置集群:

java 复制代码
@Configuration
public class RedisClusterConfig {
    
    @Bean
    public LettuceConnectionFactory clusterConnectionFactory() {
        // 集群节点
        List<String> nodes = Arrays.asList(
            "127.0.0.1:7000",
            "127.0.0.1:7001",
            "127.0.0.1:7002",
            "127.0.0.1:7003",
            "127.0.0.1:7004",
            "127.0.0.1:7005"
        );
        
        RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(nodes);
        clusterConfig.setPassword("password123");
        
        // 配置连接池
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(50);
        poolConfig.setMinIdle(10);
        
        LettucePoolingClientConfiguration clientConfig = 
                LettucePoolingClientConfiguration.builder()
                .poolConfig(poolConfig)
                .commandTimeout(Duration.ofSeconds(2))
                .build();
        
        return new LettuceConnectionFactory(clusterConfig, clientConfig);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(clusterConnectionFactory());
        
        // 配置序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jsonSerializer = 
                new Jackson2JsonRedisSerializer<>(Object.class);
        
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        return template;
    }
}

// 集群操作服务
@Service
public class RedisClusterService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 批量操作(会自动路由到不同节点)
    public void batchSet(Map<String, Object> data) {
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Map.Entry<String, Object> entry : data.entrySet()) {
                byte[] key = entry.getKey().getBytes();
                byte[] value = SerializationUtils.serialize((Serializable) entry.getValue());
                connection.set(key, value);
            }
            return null;
        });
    }
    
    // 获取集群信息
    public Properties getClusterInfo() {
        return redisTemplate.getConnectionFactory()
                .getClusterConnection()
                .clusterGetClusterInfo();
    }
    
    // 获取槽位分配
    public Map<String, List<Integer>> getSlotDistribution() {
        RedisClusterConnection connection = redisTemplate
                .getConnectionFactory()
                .getClusterConnection();
        
        Map<String, List<Integer>> distribution = new HashMap<>();
        
        for (RedisClusterNode node : connection.clusterGetNodes()) {
            if (node.isMaster()) {
                String nodeId = node.getId();
                List<Integer> slots = new ArrayList<>();
                
                for (RedisClusterNode.SlotRange range : node.getSlotRanges()) {
                    for (int i = range.getFrom(); i <= range.getTo(); i++) {
                        slots.add(i);
                    }
                }
                
                distribution.put(nodeId, slots);
            }
        }
        
        return distribution;
    }
}

六、Spring Boot集成Redis

6.1 依赖配置

pom.xml:

xml 复制代码
<dependencies>
    <!-- Spring Boot Redis Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Lettuce连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    <!-- Jackson序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

6.2 配置文件

application.yml:

yaml 复制代码
spring:
  redis:
    # 单机配置
    host: localhost
    port: 6379
    password: password123
    database: 0
    
    # 连接池配置
    lettuce:
      pool:
        max-active: 100
        max-idle: 50
        min-idle: 10
        max-wait: 3000ms
    
    # 超时配置
    timeout: 5000ms
    connect-timeout: 3000ms
    
  # 缓存配置
  cache:
    type: redis
    redis:
      time-to-live: 3600000  # 1小时
      cache-null-values: false
      use-key-prefix: true
      key-prefix: "cache:"

6.3 完整配置类

java 复制代码
@Configuration
@EnableCaching  // 开启缓存
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // Jackson序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = 
                new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        
        // String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        
        // key使用String序列化
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        
        // value使用Jackson序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(factory);
        return template;
    }
    
    // 缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))  // 默认过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();  // 不缓存null值
        
        // 设置不同缓存的过期时间
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("users", config.entryTtl(Duration.ofMinutes(30)));
        cacheConfigurations.put("products", config.entryTtl(Duration.ofHours(2)));
        
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }
    
    // Redis连接工厂配置
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        // 连接池配置
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(50);
        poolConfig.setMinIdle(10);
        poolConfig.setMaxWaitMillis(3000);
        
        // 客户端配置
        LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration
                .builder()
                .poolConfig(poolConfig)
                .commandTimeout(Duration.ofSeconds(2))
                .shutdownTimeout(Duration.ofMillis(100))
                .build();
        
        // Redis配置
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName("localhost");
        redisConfig.setPort(6379);
        redisConfig.setPassword("password123");
        redisConfig.setDatabase(0);
        
        return new LettuceConnectionFactory(redisConfig, clientConfig);
    }
}

6.4 缓存注解使用

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    // 缓存查询结果
    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
        System.out.println("从数据库查询用户: " + id);
        return userRepository.findById(id).orElse(null);
    }
    
    // 更新时清除缓存
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }
    
    // 删除缓存
    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
    
    // 清除所有缓存
    @CacheEvict(value = "users", allEntries = true)
    public void clearAllUserCache() {
        System.out.println("清除所有用户缓存");
    }
    
    // 条件缓存
    @Cacheable(value = "users", key = "#name", condition = "#name.length() > 3")
    public List<User> getUsersByName(String name) {
        return userRepository.findByName(name);
    }
    
    // 多个缓存操作
    @Caching(
        cacheable = @Cacheable(value = "users", key = "#email"),
        put = @CachePut(value = "userEmails", key = "#result.id"),
        evict = @CacheEvict(value = "oldUsers", allEntries = true)
    )
    public User getUserByEmail(String email) {
        return userRepository.findByEmail(email);
    }
}

6.5 Redis工具类

java 复制代码
@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // ==================== Common ====================
    
    public boolean expire(String key, long time) {
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        }
        return false;
    }
    
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    
    public void del(String... keys) {
        if (keys != null && keys.length > 0) {
            redisTemplate.delete(Arrays.asList(keys));
        }
    }
    
    // ==================== String ====================
    
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    
    public boolean set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
        return true;
    }
    
    public boolean set(String key, Object value, long time) {
        if (time > 0) {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        } else {
            set(key, value);
        }
        return true;
    }
    
    public long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    
    public long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }
    
    // ==================== Hash ====================
    
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }
    
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    public boolean hmset(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
        return true;
    }
    
    public boolean hset(String key, String item, Object value) {
        redisTemplate.opsForHash().put(key, item, value);
        return true;
    }
    
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }
    
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }
    
    // ==================== List ====================
    
    public List<Object> lGet(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    
    public long lGetListSize(String key) {
        return redisTemplate.opsForList().size(key);
    }
    
    public Object lGetIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }
    
    public boolean lSet(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
        return true;
    }
    
    public boolean lSet(String key, List<Object> value) {
        redisTemplate.opsForList().rightPushAll(key, value);
        return true;
    }
    
    // ==================== Set ====================
    
    public Set<Object> sGet(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
    public boolean sHasKey(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    
    public long sSet(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    
    public long sGetSetSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }
    
    public long setRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }
    
    // ==================== ZSet ====================
    
    public boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
    
    public Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }
    
    public Set<Object> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }
    
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }
    
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }
    
    // ==================== 分布式锁 ====================
    
    public boolean lock(String key, String value, long expireTime) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
    }
    
    public boolean unlock(String key, String value) {
        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), value);
        
        return result != null && result == 1;
    }
}

七、Redis性能优化和实际应用场景

7.1 性能优化策略

1. 内存优化
java 复制代码
@Component
public class RedisMemoryOptimizer {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 设置key过期时间,避免内存泄漏
    public void setWithExpire(String key, Object value, long seconds) {
        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }
    
    // 批量删除过期key
    @Scheduled(cron = "0 0 3 * * ?")  // 每天凌晨3点执行
    public void cleanExpiredKeys() {
        Set<String> keys = redisTemplate.keys("temp:*");
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }
    
    // 使用位图节省内存(用户签到示例)
    public void userSignIn(long userId, int dayOfMonth) {
        String key = "sign:" + userId + ":" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    }
    
    // 获取用户月签到情况
    public long getMonthSignCount(long userId) {
        String key = "sign:" + userId + ":" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            byte[] keyBytes = key.getBytes();
            return connection.bitCount(keyBytes);
        });
    }
}
2. 命令优化
java 复制代码
@Service
public class RedisCommandOptimizer {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 避免使用KEYS命令,使用SCAN代替
    public Set<String> scanKeys(String pattern) {
        return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keys = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(
                ScanOptions.scanOptions()
                    .match(pattern)
                    .count(1000)
                    .build()
            );
            
            while (cursor.hasNext()) {
                keys.add(new String(cursor.next()));
            }
            
            return keys;
        });
    }
    
    // 使用Pipeline批量操作
    public List<Object> batchGet(List<String> keys) {
        return redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection stringConnection = (StringRedisConnection) connection;
            for (String key : keys) {
                stringConnection.get(key);
            }
            return null;
        });
    }
    
    // 避免大key,分片存储
    public void saveLargeData(String key, List<String> largeList, int chunkSize) {
        int chunks = (largeList.size() + chunkSize - 1) / chunkSize;
        
        for (int i = 0; i < chunks; i++) {
            int start = i * chunkSize;
            int end = Math.min(start + chunkSize, largeList.size());
            List<String> chunk = largeList.subList(start, end);
            
            String chunkKey = key + ":chunk:" + i;
            redisTemplate.opsForList().rightPushAll(chunkKey, chunk);
            redisTemplate.expire(chunkKey, 1, TimeUnit.HOURS);
        }
        
        // 保存分片信息
        redisTemplate.opsForValue().set(key + ":chunks", String.valueOf(chunks));
    }
}

7.2 实际应用场景

1. 分布式锁
java 复制代码
@Component
public class RedisDistributedLock {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 获取锁
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        Boolean result = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
        return result != null && result;
    }
    
    // 释放锁(使用Lua脚本保证原子性)
    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";
        
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, 
                Collections.singletonList(lockKey), requestId);
        
        return result != null && result == 1;
    }
    
    // 使用示例
    public void processWithLock(String businessKey) {
        String lockKey = "lock:" + businessKey;
        String requestId = UUID.randomUUID().toString();
        
        try {
            // 尝试获取锁,最多等待5秒
            boolean locked = false;
            for (int i = 0; i < 10; i++) {
                if (tryLock(lockKey, requestId, 30)) {
                    locked = true;
                    break;
                }
                Thread.sleep(500);
            }
            
            if (locked) {
                // 执行业务逻辑
                System.out.println("获得锁,执行业务逻辑");
                Thread.sleep(1000);
            } else {
                System.out.println("获取锁失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            releaseLock(lockKey, requestId);
        }
    }
}
2. 限流器
java 复制代码
@Component
public class RedisRateLimiter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 简单计数器限流
    public boolean isAllowed(String key, int limit, int window) {
        Long current = redisTemplate.opsForValue().increment(key);
        
        if (current == 1) {
            redisTemplate.expire(key, window, TimeUnit.SECONDS);
        }
        
        return current <= limit;
    }
    
    // 滑动窗口限流
    public boolean isAllowedSlidingWindow(String key, int limit, int window) {
        long now = System.currentTimeMillis();
        long windowStart = now - window * 1000;
        
        // 使用zset实现滑动窗口
        String requestId = UUID.randomUUID().toString();
        
        // 添加当前请求
        redisTemplate.opsForZSet().add(key, requestId, now);
        
        // 移除窗口外的请求
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
        
        // 统计窗口内请求数
        Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
        
        // 设置过期时间
        redisTemplate.expire(key, window + 1, TimeUnit.SECONDS);
        
        return count <= limit;
    }
    
    // 令牌桶限流(使用Lua脚本)
    public boolean isAllowedTokenBucket(String key, int capacity, int rate) {
        String script = 
            "local key = KEYS[1]\n" +
            "local capacity = tonumber(ARGV[1])\n" +
            "local rate = tonumber(ARGV[2])\n" +
            "local now = tonumber(ARGV[3])\n" +
            "local requested = tonumber(ARGV[4])\n" +
            "\n" +
            "local bucket = redis.call('hgetall', key)\n" +
            "local tokens = capacity\n" +
            "local last_refill = now\n" +
            "\n" +
            "if #bucket > 0 then\n" +
            "    tokens = tonumber(bucket[2])\n" +
            "    last_refill = tonumber(bucket[4])\n" +
            "end\n" +
            "\n" +
            "local elapsed = math.max(0, now - last_refill)\n" +
            "local filled_tokens = math.min(capacity, tokens + elapsed * rate / 1000)\n" +
            "\n" +
            "if filled_tokens >= requested then\n" +
            "    redis.call('hset', key, 'tokens', filled_tokens - requested, 'last_refill', now)\n" +
            "    redis.call('expire', key, 10)\n" +
            "    return 1\n" +
            "else\n" +
            "    redis.call('hset', key, 'tokens', filled_tokens, 'last_refill', now)\n" +
            "    redis.call('expire', key, 10)\n" +
            "    return 0\n" +
            "end";
        
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        
        Long result = redisTemplate.execute(redisScript,
                Collections.singletonList(key),
                String.valueOf(capacity),
                String.valueOf(rate),
                String.valueOf(System.currentTimeMillis()),
                "1");
        
        return result != null && result == 1;
    }
}
3. 会话管理
java 复制代码
@Service
public class SessionManager {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final long SESSION_TIMEOUT = 30 * 60; // 30分钟
    
    // 创建会话
    public String createSession(Long userId, Map<String, Object> userInfo) {
        String sessionId = UUID.randomUUID().toString();
        String sessionKey = "session:" + sessionId;
        
        Map<String, Object> sessionData = new HashMap<>();
        sessionData.put("userId", userId);
        sessionData.put("createTime", System.currentTimeMillis());
        sessionData.putAll(userInfo);
        
        redisTemplate.opsForHash().putAll(sessionKey, sessionData);
        redisTemplate.expire(sessionKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
        
        // 维护用户会话索引
        String userSessionKey = "user:sessions:" + userId;
        redisTemplate.opsForSet().add(userSessionKey, sessionId);
        redisTemplate.expire(userSessionKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
        
        return sessionId;
    }
    
    // 获取会话
    public Map<Object, Object> getSession(String sessionId) {
        String sessionKey = "session:" + sessionId;
        Map<Object, Object> session = redisTemplate.opsForHash().entries(sessionKey);
        
        if (!session.isEmpty()) {
            // 刷新过期时间
            redisTemplate.expire(sessionKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
        }
        
        return session;
    }
    
    // 销毁会话
    public void destroySession(String sessionId) {
        String sessionKey = "session:" + sessionId;
        Map<Object, Object> session = redisTemplate.opsForHash().entries(sessionKey);
        
        if (session.containsKey("userId")) {
            Long userId = Long.valueOf(session.get("userId").toString());
            String userSessionKey = "user:sessions:" + userId;
            redisTemplate.opsForSet().remove(userSessionKey, sessionId);
        }
        
        redisTemplate.delete(sessionKey);
    }
    
    // 获取用户所有会话
    public Set<Object> getUserSessions(Long userId) {
        String userSessionKey = "user:sessions:" + userId;
        return redisTemplate.opsForSet().members(userSessionKey);
    }
}
4. 延迟队列
java 复制代码
@Component
public class DelayQueue {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String DELAY_QUEUE_KEY = "delay:queue";
    
    // 添加延迟任务
    public void addDelayTask(String taskId, String taskData, long delaySeconds) {
        long score = System.currentTimeMillis() + (delaySeconds * 1000);
        
        Map<String, String> task = new HashMap<>();
        task.put("id", taskId);
        task.put("data", taskData);
        
        String taskJson = JSON.toJSONString(task);
        redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, taskJson, score);
    }
    
    // 消费延迟任务
    @Scheduled(fixedDelay = 1000)  // 每秒检查一次
    public void consumeDelayTasks() {
        long now = System.currentTimeMillis();
        
        Set<String> tasks = redisTemplate.opsForZSet()
                .rangeByScore(DELAY_QUEUE_KEY, 0, now);
        
        if (tasks != null && !tasks.isEmpty()) {
            for (String taskJson : tasks) {
                // 移除任务
                Long removed = redisTemplate.opsForZSet().remove(DELAY_QUEUE_KEY, taskJson);
                
                if (removed != null && removed > 0) {
                    // 处理任务
                    Map<String, String> task = JSON.parseObject(taskJson, Map.class);
                    processTask(task.get("id"), task.get("data"));
                }
            }
        }
    }
    
    private void processTask(String taskId, String taskData) {
        System.out.println("处理延迟任务 - ID: " + taskId + ", 数据: " + taskData);
        // 实际业务处理逻辑
    }
}
5. 实时排行榜
java 复制代码
@Service
public class LeaderboardService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 更新分数
    public void updateScore(String leaderboard, String player, double score) {
        redisTemplate.opsForZSet().add(leaderboard, player, score);
    }
    
    // 增加分数
    public void incrementScore(String leaderboard, String player, double increment) {
        redisTemplate.opsForZSet().incrementScore(leaderboard, player, increment);
    }
    
    // 获取排行榜
    public List<Map<String, Object>> getLeaderboard(String leaderboard, int topN) {
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(leaderboard, 0, topN - 1);
        
        List<Map<String, Object>> result = new ArrayList<>();
        int rank = 1;
        
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            Map<String, Object> entry = new HashMap<>();
            entry.put("rank", rank++);
            entry.put("player", tuple.getValue());
            entry.put("score", tuple.getScore());
            result.add(entry);
        }
        
        return result;
    }
    
    // 获取玩家排名和分数
    public Map<String, Object> getPlayerRankAndScore(String leaderboard, String player) {
        Map<String, Object> result = new HashMap<>();
        
        Long rank = redisTemplate.opsForZSet().reverseRank(leaderboard, player);
        Double score = redisTemplate.opsForZSet().score(leaderboard, player);
        
        result.put("player", player);
        result.put("rank", rank != null ? rank + 1 : null);
        result.put("score", score);
        
        return result;
    }
    
    // 获取排名区间的玩家
    public Set<String> getPlayersByRankRange(String leaderboard, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(leaderboard, start - 1, end - 1);
    }
    
    // 获取分数区间的玩家
    public Set<String> getPlayersByScoreRange(String leaderboard, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().rangeByScore(leaderboard, minScore, maxScore);
    }
}
相关推荐
xqqxqxxq7 小时前
背单词软件技术笔记(V2.0扩展版)
java·笔记·python
消失的旧时光-19437 小时前
深入理解 Java 线程池(二):ThreadPoolExecutor 执行流程 + 运行状态 + ctl 原理全解析
java·开发语言
哈哈老师啊7 小时前
Springboot学生综合测评系统hxtne(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
4311媒体网7 小时前
帝国cms调用文章内容 二开基本操作
java·开发语言·php
小小8程序员7 小时前
Redis-10
数据库·redis·缓存
liuzhilongDBA7 小时前
从collation mismatch异常到其原理
数据库·version·glibc·postgres·collation
梁萌7 小时前
MySQL数据库分库分表介绍
数据库·mysql·shardingsphere·分库分表
zwxu_8 小时前
Nginx NIO对比Java NIO
java·nginx·nio
可观测性用观测云9 小时前
Pyroscope Java 接入最佳实践
java
占疏9 小时前
dify API访问工作流/聊天
开发语言·数据库·python