艾体宝干货 | Redis Java 开发系列#2 数据结构

前言

本篇读者收益

  • 理解 Redis 五大数据结构的特性和适用场景
  • 掌握在 Java 中高效操作各种 Redis 数据结构的方法
  • 学会根据业务需求选择合适的数据结构
  • 了解数据结构层面的性能优化技巧

先修要求

  • 已完成第一篇环境搭建
  • 了解 Redis 基本命令
  • 熟悉 Java 集合框架
  • 具备面向对象编程基础

关键要点

  1. 字符串不仅是文本,更是计数器、缓存和分布式锁的基础
  2. 哈希适合存储对象,减少键数量,优化内存使用
  3. 列表实现队列、栈和时间线,支持阻塞操作
  4. 集合处理唯一性、标签系统和社交关系
  5. 有序集合构建排行榜、延迟队列和范围查询

背景简述

Redis 之所以能够提供极高的性能,很大程度上得益于其精心设计的数据结构体系。与传统的键值存储不同,Redis 的值可以是多种数据结构,每种结构都针对特定的使用场景进行了优化。

Redis 数据结构体系​:

理解这些数据结构的特性和适用场景,是高效使用 Redis 的关键。不同的数据结构在内存使用、操作复杂度和适用场景上都有显著差异。

环境准备与快速上手

在开始深入学习数据结构之前,需要已经建立了 Redis 连接。我们基于第一篇的优化连接池继续构建:

Java 复制代码
public class RedisDataStructureBase {
    protected JedisPool jedisPool;
    
    public RedisDataStructureBase() {
        this.jedisPool = OptimizedJedisPool.getJedisPool();
    }
    
    // 统一的资源清理方法
    public void cleanup() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 清理测试数据
            jedis.del("test:*");
        }
    }
}

核心用法与代码示例

字符串(String):不仅仅是文本

字符串是 Redis 最基本的数据类型,但它的应用远不止存储文本那么简单。

内存结构与特性​:

  • 最大 512MB 容量
  • 二进制安全,可存储任何数据
  • 支持数值操作和位操作
  • 常用于缓存、计数器、分布式锁

Java 操作实战​:

Java 复制代码
public class StringOperations extends RedisDataStructureBase {
    
    /**
     * 基础字符串操作 - 缓存场景
     */
    public void basicStringOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 设置键值 - 普通缓存
            jedis.set("user:1001:name", "张三");
            jedis.set("user:1001:email", "zhangsan@example.com");
            
            // 带过期时间的缓存 - 会话数据
            jedis.setex("session:abc123", 3600, "session_data_json");
            
            // 只有键不存在时设置 - 分布式锁基础
            boolean success = jedis.setnx("resource:lock", "locked") == 1;
            System.out.println("获取锁结果: " + success);
            
            // 批量操作 - 提升性能
            jedis.mset("config:timeout", "30", "config:retries", "3", "config:theme", "dark");
        }
    }
    
    /**
     * 计数器应用 - 阅读量统计
     */
    public void counterApplications() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 文章阅读计数
            Long views = jedis.incr("article:1001:views");
            System.out.println("文章阅读量: " + views);
            
            // 带步长的计数
            jedis.incrBy("user:1001:points", 10);
            
            // 递减操作
            jedis.decr("product:1001:stock");
            
            // 获取并设置 - 原子操作
            String oldValue = jedis.getSet("config:version", "2.0");
            System.out.println("旧版本: " + oldValue);
        }
    }
    
    /**
     * 位操作 - 用户标签系统
     */
    public void bitOperations() {
        try (Jedis jedis = jedisPool.getResource()) {
            String userKey = "user:2001:tags";
            
            // 设置位 - 每个位代表一个标签
            jedis.setbit(userKey, 0, true);  // 标签1: VIP用户
            jedis.setbit(userKey, 1, true);  // 标签2: 活跃用户
            jedis.setbit(userKey, 2, false); // 标签3: 新用户(未设置)
            
            // 检查标签
            boolean isVip = jedis.getbit(userKey, 0);
            System.out.println("是否是VIP: " + isVip);
            
            // 统计设置的位数 - 用户标签数量
            long tagCount = jedis.bitcount(userKey);
            System.out.println("用户标签数量: " + tagCount);
        }
    }
}

应用场景分析​:

  1. 缓存系统:存储序列化的对象、HTML 片段
  2. 计数器:网站访问量、用户积分、商品库存
  3. 分布式锁:基于 SETNX 实现互斥访问
  4. 会话存储:用户登录状态、临时配置
  5. 位图统计:用户标签、活跃度统计

哈希(Hash):对象存储的最佳选择

哈希类型适合存储对象,可以将多个字段组合在一个键中,减少键的数量,优化内存使用。

内存优化原理​:

  • 小哈希使用 ziplist 编码,内存紧凑
  • 字段数量少时,比多个字符串键更节省内存
  • 适合存储结构化数据

Java 操作实战​:

Java 复制代码
public class HashOperations extends RedisDataStructureBase {
    
    /**
     * 用户对象存储示例
     */
    public void userObjectStorage() {
        try (Jedis jedis = jedisPool.getResource()) {
            String userKey = "user:3001";
            
            // 单个字段设置
            jedis.hset(userKey, "name", "李四");
            jedis.hset(userKey, "age", "28");
            jedis.hset(userKey, "email", "lisi@example.com");
            
            // 批量设置字段 - 性能更优
            Map<String, String> userFields = new HashMap<>();
            userFields.put("department", "技术部");
            userFields.put("position", "高级工程师");
            userFields.put("salary", "15000");
            jedis.hmset(userKey, userFields);
            
            // 获取单个字段
            String userName = jedis.hget(userKey, "name");
            System.out.println("用户姓名: " + userName);
            
            // 获取多个字段
            List<String> userInfo = jedis.hmget(userKey, "name", "age", "department");
            System.out.println("用户信息: " + userInfo);
            
            // 获取所有字段 - 小心大对象
            Map<String, String> allFields = jedis.hgetAll(userKey);
            System.out.println("完整用户信息: " + allFields);
            
            // 字段递增 - 用户积分
            Long newPoints = jedis.hincrBy(userKey, "points", 5);
            System.out.println("新积分: " + newPoints);
        }
    }
    
    /**
     * 购物车实现
     */
    public void shoppingCartExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String cartKey = "cart:user:4001";
            
            // 添加商品到购物车
            jedis.hset(cartKey, "product:1001", "2"); // 商品ID:1001, 数量:2
            jedis.hset(cartKey, "product:1002", "1");
            jedis.hset(cartKey, "product:1003", "3");
            
            // 更新商品数量
            jedis.hincrBy(cartKey, "product:1001", 1); // 增加1个
            
            // 移除商品
            jedis.hdel(cartKey, "product:1002");
            
            // 获取购物车商品数量
            long itemCount = jedis.hlen(cartKey);
            System.out.println("购物车商品种类: " + itemCount);
            
            // 获取购物车所有商品
            Map<String, String> cartItems = jedis.hgetAll(cartKey);
            System.out.println("购物车内容: " + cartItems);
        }
    }
    
    /**
     * 对象序列化工具方法
     */
    public <T> void storeObject(String key, T obj, Class<T> clazz) {
        try (Jedis jedis = jedisPool.getResource()) {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, String> fieldMap = mapper.convertValue(obj, 
                mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class));
            jedis.hmset(key, fieldMap);
        } catch (Exception e) {
            throw new RuntimeException("对象存储失败", e);
        }
    }
    
    public <T> T getObject(String key, Class<T> clazz) {
        try (Jedis jedis = jedisPool.getResource()) {
            Map<String, String> fieldMap = jedis.hgetAll(key);
            if (fieldMap.isEmpty()) {
                return null;
            }
            ObjectMapper mapper = new ObjectMapper();
            return mapper.convertValue(fieldMap, clazz);
        } catch (Exception e) {
            throw new RuntimeException("对象获取失败", e);
        }
    }
}

应用场景分析​:

  1. 用户信息存储:用户属性字段动态更新
  2. 购物车系统:商品 ID 和数量的映射
  3. 配置信息:应用的动态配置项
  4. 对象缓存:结构化数据的序列化存储
  5. 计数器组:多个相关计数器的集合

列表(List):队列与时间线的实现

Redis 列表基于双向链表实现,支持从两端快速插入和删除,是实现队列、栈和时间线的理想选择。

数据结构特性​:

  • 最大元素数:2³² - 1 个
  • 两端操作时间复杂度 O(1)
  • 支持阻塞操作,适合消息队列
  • 索引操作时间复杂度 O(n)

Java 操作实战​:

Java 复制代码
public class ListOperations extends RedisDataStructureBase {
    
    /**
     * 消息队列实现
     */
    public void messageQueueExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String queueKey = "queue:notifications";
            
            // 生产者:从左侧推入消息
            jedis.lpush(queueKey, "消息1: 用户注册成功");
            jedis.lpush(queueKey, "消息2: 订单创建完成");
            jedis.lpush(queueKey, "消息3: 支付成功");
            
            // 消费者:从右侧弹出消息
            String message = jedis.rpop(queueKey);
            while (message != null) {
                System.out.println("处理消息: " + message);
                message = jedis.rpop(queueKey);
            }
        }
    }
    
    /**
     * 阻塞队列 - 实时消息处理
     */
    public void blockingQueueExample() {
        String queueKey = "queue:real-time";
        
        // 生产者线程
        Thread producer = new Thread(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                for (int i = 0; i < 5; i++) {
                    jedis.lpush(queueKey, "实时消息_" + i);
                    System.out.println("生产消息: 实时消息_" + i);
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        
        // 消费者线程 - 阻塞等待
        Thread consumer = new Thread(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                for (int i = 0; i < 5; i++) {
                    // 阻塞式弹出,最多等待10秒
                    List<String> messages = jedis.blpop(10, queueKey);
                    if (messages != null) {
                        System.out.println("消费消息: " + messages.get(1));
                    }
                }
            }
        });
        
        producer.start();
        consumer.start();
    }
    
    /**
     * 最新消息时间线
     */
    public void timelineExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String timelineKey = "timeline:user:5001";
            
            // 添加动态到时间线
            jedis.lpush(timelineKey, 
                "{\"type\": \"post\", \"content\": \"发布了新文章\", \"time\": \"2024-01-15 10:00:00\"}",
                "{\"type\": \"like\", \"content\": \"点赞了文章\", \"time\": \"2024-01-15 09:30:00\"}",
                "{\"type\": \"comment\", \"content\": \"发表了评论\", \"time\": \"2024-01-15 09:00:00\"}"
            );
            
            // 限制时间线长度,防止无限增长
            jedis.ltrim(timelineKey, 0, 99); // 只保留最新100条
            
            // 分页获取时间线
            List<String> recentActivities = jedis.lrange(timelineKey, 0, 9);
            System.out.println("最近10条动态: " + recentActivities);
            
            // 获取时间线长度
            long timelineLength = jedis.llen(timelineKey);
            System.out.println("时间线长度: " + timelineLength);
        }
    }
    
    /**
     * 栈(Stack)实现 - 后进先出
     */
    public void stackExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String stackKey = "stack:operations";
            
            // 压栈操作
            jedis.lpush(stackKey, "操作1", "操作2", "操作3");
            
            // 弹栈操作
            String operation = jedis.lpop(stackKey);
            while (operation != null) {
                System.out.println("执行操作: " + operation);
                operation = jedis.lpop(stackKey);
            }
        }
    }
}

应用场景分析​:

  1. 消息队列:系统间异步通信
  2. 时间线:用户动态、新闻流
  3. 操作日志:用户操作记录
  4. 任务队列:后台任务处理
  5. 最新列表:最新文章、最新评论

集合(Set):唯一性与关系运算

Redis 集合存储不重复的字符串元素,支持交集、并集、差集等关系运算,是处理唯一性和集合关系的利器。

性能特点​:

  • 添加、删除、查找时间复杂度 O(1)
  • 支持集合间运算
  • 最大元素数:2³² - 1 个
  • 内部实现:整数集合或哈希表

Java 操作实战​:

Java 复制代码
public class SetOperations extends RedisDataStructureBase {
    
    /**
     * 标签系统实现
     */
    public void tagSystemExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 文章标签
            String articleTagsKey = "article:6001:tags";
            jedis.sadd(articleTagsKey, "技术", "Redis", "数据库", "高性能");
            
            // 用户兴趣标签
            String userInterestsKey = "user:6001:interests";
            jedis.sadd(userInterestsKey, "技术", "编程", "Redis", "Java");
            
            // 检查文章是否包含某个标签
            boolean hasTechTag = jedis.sismember(articleTagsKey, "技术");
            System.out.println("文章是否包含技术标签: " + hasTechTag);
            
            // 获取共同兴趣 - 集合交集
            Set<String> commonTags = jedis.sinter(articleTagsKey, userInterestsKey);
            System.out.println("共同标签: " + commonTags);
            
            // 获取所有标签 - 集合并集
            Set<String> allTags = jedis.sunion(articleTagsKey, userInterestsKey);
            System.out.println("所有标签: " + allTags);
            
            // 获取文章特有标签 - 集合差集
            Set<String> articleOnlyTags = jedis.sdiff(articleTagsKey, userInterestsKey);
            System.out.println("文章特有标签: " + articleOnlyTags);
        }
    }
    
    /**
     * 好友关系与社交网络
     */
    public void socialNetworkExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String userAFriends = "user:7001:friends";
            String userBFriends = "user:7002:friends";
            
            // 添加好友
            jedis.sadd(userAFriends, "7002", "7003", "7004");
            jedis.sadd(userBFriends, "7001", "7005", "7006");
            
            // 共同好友
            Set<String> mutualFriends = jedis.sinter(userAFriends, userBFriends);
            System.out.println("共同好友: " + mutualFriends);
            
            // 可能认识的人 - 好友的好友
            Set<String> potentialFriends = jedis.sdiff(userBFriends, userAFriends);
            potentialFriends.remove("7001"); // 排除自己
            System.out.println("可能认识的人: " + potentialFriends);
            
            // 统计好友数量
            long friendCount = jedis.scard(userAFriends);
            System.out.println("用户A好友数量: " + friendCount);
            
            // 随机推荐好友
            String randomFriend = jedis.srandmember(userAFriends);
            System.out.println("随机好友推荐: " + randomFriend);
        }
    }
    
    /**
     * 唯一值处理 - 用户投票系统
     */
    public void uniqueValueExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String votersKey = "poll:8001:voters";
            
            // 用户投票 - 自动去重
            long result1 = jedis.sadd(votersKey, "user:9001");
            long result2 = jedis.sadd(votersKey, "user:9002");
            long result3 = jedis.sadd(votersKey, "user:9001"); // 重复投票
            
            System.out.println("第一次投票结果: " + (result1 == 1 ? "成功" : "失败"));
            System.out.println("第二次投票结果: " + (result2 == 1 ? "成功" : "失败"));
            System.out.println("第三次投票结果: " + (result3 == 1 ? "成功" : "失败"));
            
            // 获取所有投票用户
            Set<String> allVoters = jedis.smembers(votersKey);
            System.out.println("所有投票用户: " + allVoters);
            
            // 统计投票人数
            long voterCount = jedis.scard(votersKey);
            System.out.println("总投票人数: " + voterCount);
        }
    }
    
    /**
     * 抽奖系统 - 随机抽取
     */
    public void lotterySystemExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String participantsKey = "lottery:9001:participants";
            
            // 添加参与者
            jedis.sadd(participantsKey, 
                "user:10001", "user:10002", "user:10003", 
                "user:10004", "user:10005", "user:10006"
            );
            
            // 随机抽取3名获奖者(不重复)
            List<String> winners = jedis.srandmember(participantsKey, 3);
            System.out.println("获奖用户: " + winners);
            
            // 随机抽取并移除(抽奖后移除)
            String grandPrizeWinner = jedis.spop(participantsKey);
            System.out.println("特等奖获得者: " + grandPrizeWinner);
            
            // 剩余参与者数量
            long remaining = jedis.scard(participantsKey);
            System.out.println("剩余参与者: " + remaining);
        }
    }
}

应用场景分析​:

  1. 标签系统:文章标签、用户兴趣
  2. 社交关系:好友列表、关注关系
  3. 唯一值处理:用户投票、访问 IP 记录
  4. 数据过滤:已读文章、已处理任务
  5. 随机抽样:抽奖系统、AB 测试

有序集合(ZSet):排行榜与范围查询

有序集合为每个元素关联一个分数(score),支持按分数排序和范围查询,是实现排行榜、延迟队列的理想选择。

排序原理​:

  • 跳跃表(skiplist)实现,查询效率 O(logN)
  • 元素按分数从小到大排序
  • 分数可重复,元素唯一
  • 支持按分数范围、按排名范围查询

Java 操作实战​:

Java 复制代码
public class SortedSetOperations extends RedisDataStructureBase {
    
    /**
     * 游戏排行榜实现
     */
    public void leaderboardExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String leaderboardKey = "leaderboard:game:10001";
            
            // 添加玩家分数
            jedis.zadd(leaderboardKey, 1500.0, "player:1001");
            jedis.zadd(leaderboardKey, 1800.5, "player:1002");
            jedis.zadd(leaderboardKey, 2200.0, "player:1003");
            jedis.zadd(leaderboardKey, 1900.0, "player:1004");
            jedis.zadd(leaderboardKey, 2100.0, "player:1005");
            
            // 更新玩家分数
            jedis.zincrby(leaderboardKey, 100.0, "player:1001"); // 增加100分
            
            // 获取top3玩家
            List<String> topPlayers = jedis.zrevrange(leaderboardKey, 0, 2);
            System.out.println("排行榜前三名: " + topPlayers);
            
            // 获取玩家排名(从0开始,分数从高到低)
            Long playerRank = jedis.zrevrank(leaderboardKey, "player:1001");
            System.out.println("玩家1001的排名: " + (playerRank != null ? playerRank + 1 : "未上榜"));
            
            // 获取玩家分数
            Double playerScore = jedis.zscore(leaderboardKey, "player:1001");
            System.out.println("玩家1001的分数: " + playerScore);
            
            // 获取分数区间内的玩家(1800-2100分)
            Set<String> rangePlayers = jedis.zrangeByScore(leaderboardKey, 1800, 2100);
            System.out.println("1800-2100分数段玩家: " + rangePlayers);
        }
    }
    
    /**
     * 延迟队列实现
     */
    public void delayedQueueExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String delayedQueueKey = "queue:delayed:tasks";
            
            // 当前时间戳
            long currentTime = System.currentTimeMillis();
            
            // 添加延迟任务(分数为执行时间戳)
            jedis.zadd(delayedQueueKey, currentTime + 5000, "task:process_order:1001");    // 5秒后执行
            jedis.zadd(delayedQueueKey, currentTime + 10000, "task:send_email:1001");      // 10秒后执行
            jedis.zadd(delayedQueueKey, currentTime + 15000, "task:cleanup_cache:1001");   // 15秒后执行
            
            // 处理到期任务的工作线程
            Thread worker = new Thread(() -> {
                try (Jedis workerJedis = jedisPool.getResource()) {
                    while (!Thread.currentThread().isInterrupted()) {
                        long now = System.currentTimeMillis();
                        
                        // 获取所有到期的任务(分数小于等于当前时间)
                        Set<String> readyTasks = workerJedis.zrangeByScore(delayedQueueKey, 0, now);
                        
                        for (String task : readyTasks) {
                            // 处理任务
                            System.out.println("执行任务: " + task + " at " + new Date(now));
                            
                            // 从队列中移除已处理任务
                            workerJedis.zrem(delayedQueueKey, task);
                        }
                        
                        if (readyTasks.isEmpty()) {
                            // 没有任务,休眠1秒
                            Thread.sleep(1000);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            
            worker.start();
            
            // 运行10秒后停止
            try {
                Thread.sleep(10000);
                worker.interrupt();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    /**
     * 时间序列数据 - 股票价格记录
     */
    public void timeSeriesExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String stockPriceKey = "stock:AAPL:prices";
            
            // 记录不同时间点的股价(时间戳作为分数)
            long baseTime = System.currentTimeMillis();
            jedis.zadd(stockPriceKey, baseTime, "150.25");
            jedis.zadd(stockPriceKey, baseTime + 60000, "151.10");    // 1分钟后
            jedis.zadd(stockPriceKey, baseTime + 120000, "150.75");   // 2分钟后
            jedis.zadd(stockPriceKey, baseTime + 180000, "152.30");   // 3分钟后
            jedis.zadd(stockPriceKey, baseTime + 240000, "153.15");   // 4分钟后
            
            // 查询最近3分钟的股价数据
            long threeMinutesAgo = baseTime + 180000; // 从开始时间算3分钟后
            Set<String> recentPrices = jedis.zrangeByScore(stockPriceKey, threeMinutesAgo, baseTime + 240000);
            System.out.println("最近股价: " + recentPrices);
            
            // 获取股价范围统计
            long priceCount = jedis.zcount(stockPriceKey, 150.0, 152.0);
            System.out.println("150-152价格区间的数据点数量: " + priceCount);
        }
    }
    
    /**
     * 带权重的标签系统
     */
    public void weightedTagsExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            String articleWeightedTags = "article:11001:weighted_tags";
            
            // 添加标签及权重(权重代表相关性强度)
            jedis.zadd(articleWeightedTags, 0.9, "Redis");
            jedis.zadd(articleWeightedTags, 0.7, "数据库");
            jedis.zadd(articleWeightedTags, 0.5, "缓存");
            jedis.zadd(articleWeightedTags, 0.3, "NoSQL");
            jedis.zadd(articleWeightedTags, 0.1, "开源");
            
            // 获取相关性最高的3个标签
            Set<String> topTags = jedis.zrevrange(articleWeightedTags, 0, 2);
            System.out.println("最相关标签: " + topTags);
            
            // 按权重范围查询标签
            Set<String> strongTags = jedis.zrangeByScore(articleWeightedTags, 0.7, 1.0);
            System.out.println("强相关标签: " + strongTags);
            
            // 增加标签权重
            jedis.zincrby(articleWeightedTags, 0.1, "Redis");
            
            // 获取标签权重
            Double redisWeight = jedis.zscore(articleWeightedTags, "Redis");
            System.out.println("Redis标签权重: " + redisWeight);
        }
    }
}

应用场景分析​:

  1. 排行榜系统:游戏分数、商品销量、内容热度
  2. 延迟队列:定时任务、消息延迟投递
  3. 时间序列:监控数据、股票价格、传感器数据
  4. 带权重标签:内容相关性、用户兴趣强度
  5. 范围查询:地理位置、价格区间、时间范围

性能优化

数据结构选择策略

业务需求 推荐数据结构 理由
简单缓存 字符串 直接、高效
对象存储 哈希 内存优化、字段操作
消息队列 列表 顺序性、阻塞操作
唯一集合 集合 去重、集合运算
排序需求 有序集合 自动排序、范围查询

内存优化技巧

Java 复制代码
public class MemoryOptimizationTips {
    
    /**
     * 小对象存储优化
     */
    public void smallObjectOptimization() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 错误方式:大量小字符串键
            // jedis.set("user:1001:name", "张三");
            // jedis.set("user:1001:age", "25");
            // jedis.set("user:1001:email", "zhangsan@example.com");
            
            // 正确方式:使用哈希存储小对象
            Map<String, String> userInfo = new HashMap<>();
            userInfo.put("name", "张三");
            userInfo.put("age", "25");
            userInfo.put("email", "zhangsan@example.com");
            jedis.hmset("user:1001", userInfo);
        }
    }
    
    /**
     * 大集合分片策略
     */
    public void largeSetSharding() {
        try (Jedis jedis = jedisPool.getResource()) {
            String largeSetKey = "large:set";
            int shardCount = 10;
            
            // 添加元素时分配到不同的分片
            for (int i = 0; i < 10000; i++) {
                String element = "element_" + i;
                int shardIndex = Math.abs(element.hashCode()) % shardCount;
                String shardKey = largeSetKey + ":" + shardIndex;
                jedis.sadd(shardKey, element);
            }
            
            // 查询时检查所有分片
            String targetElement = "element_5000";
            int targetShard = Math.abs(targetElement.hashCode()) % shardCount;
            boolean exists = jedis.sismember(largeSetKey + ":" + targetShard, targetElement);
            System.out.println("元素是否存在: " + exists);
        }
    }
}

案例:电商平台数据模型设计

Java 复制代码
public class ECommerceDataModel {
    private JedisPool jedisPool;
    
    public ECommerceDataModel(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
    
    /**
     * 完整的电商数据模型示例
     */
    public void completeEcommerceExample() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 1. 用户信息 - 使用哈希
            Map<String, String> userInfo = new HashMap<>();
            userInfo.put("name", "王五");
            userInfo.put("level", "VIP");
            userInfo.put("points", "1500");
            jedis.hmset("user:12001", userInfo);
            
            // 2. 商品信息 - 使用哈希
            Map<String, String> productInfo = new HashMap<>();
            productInfo.put("name", "iPhone 15");
            productInfo.put("price", "5999");
            productInfo.put("stock", "100");
            productInfo.put("category", "electronics");
            jedis.hmset("product:2001", productInfo);
            
            // 3. 购物车 - 使用哈希(用户ID -> 商品ID:数量)
            jedis.hset("cart:12001", "product:2001", "2");
            jedis.hset("cart:12001", "product:2002", "1");
            
            // 4. 商品分类索引 - 使用集合
            jedis.sadd("category:electronics:products", "product:2001", "product:2002");
            jedis.sadd("category:books:products", "product:3001", "product:3002");
            
            // 5. 商品销量排行榜 - 使用有序集合
            jedis.zadd("leaderboard:products:sales", 150, "product:2001");
            jedis.zadd("leaderboard:products:sales", 89, "product:2002");
            jedis.zadd("leaderboard:products:sales", 203, "product:3001");
            
            // 6. 用户浏览历史 - 使用列表(最近浏览)
            jedis.lpush("user:12001:history", "product:2001", "product:3002");
            jedis.ltrim("user:12001:history", 0, 49); // 只保留最近50条
            
            // 7. 用户收藏夹 - 使用集合
            jedis.sadd("user:12001:favorites", "product:2001", "product:3001");
            
            // 8. 库存计数器 - 使用字符串
            jedis.set("product:2001:stock", "100");
            
            System.out.println("电商数据模型初始化完成");
        }
    }
    
    /**
     * 复杂的业务操作:用户下单
     */
    public boolean placeOrder(String userId, String productId, int quantity) {
        try (Jedis jedis = jedisPool.getResource()) {
            // 使用事务保证原子性
            jedis.watch("product:" + productId + ":stock", "user:" + userId + ":points");
            
            // 检查库存
            String stockStr = jedis.hget("product:" + productId, "stock");
            if (stockStr == null || Integer.parseInt(stockStr) < quantity) {
                jedis.unwatch();
                return false; // 库存不足
            }
            
            // 检查用户积分
            String pointsStr = jedis.hget("user:" + userId, "points");
            int userPoints = pointsStr != null ? Integer.parseInt(pointsStr) : 0;
            int requiredPoints = quantity * 10; // 假设每件商品需要10积分
            
            if (userPoints < requiredPoints) {
                jedis.unwatch();
                return false; // 积分不足
            }
            
            // 开启事务
            Transaction transaction = jedis.multi();
            
            try {
                // 扣减库存
                transaction.hincrBy("product:" + productId, "stock", -quantity);
                
                // 扣减积分
                transaction.hincrBy("user:" + userId, "points", -requiredPoints);
                
                // 增加销量
                transaction.zincrby("leaderboard:products:sales", quantity, productId);
                
                // 生成订单记录
                String orderId = "order:" + System.currentTimeMillis();
                Map<String, String> orderInfo = new HashMap<>();
                orderInfo.put("userId", userId);
                orderInfo.put("productId", productId);
                orderInfo.put("quantity", String.valueOf(quantity));
                orderInfo.put("status", "created");
                transaction.hmset(orderId, orderInfo);
                
                // 添加到用户订单列表
                transaction.lpush("user:" + userId + ":orders", orderId);
                
                // 提交事务
                List<Object> results = transaction.exec();
                return results != null; // null表示事务失败
                
            } catch (Exception e) {
                transaction.discard();
                throw e;
            }
        }
    }
}

小结

业务场景 推荐数据结构 关键优势
缓存数据 字符串 简单高效,支持过期
用户信息 哈希 字段操作,内存优化
消息队列 列表 顺序性,阻塞操作
社交关系 集合 去重,集合运算
排行榜 有序集合 自动排序,范围查询
相关推荐
num_killer18 小时前
小白的Langchain学习
java·python·学习·langchain
D_FW19 小时前
数据结构第六章:图
数据结构·算法
期待のcode19 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐19 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲19 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红19 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥19 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v20 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地20 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl2002092520 小时前
Guava Cache 原理与实战
java·后端·spring