艾体宝干货 | 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;
            }
        }
    }
}

小结

业务场景 推荐数据结构 关键优势
缓存数据 字符串 简单高效,支持过期
用户信息 哈希 字段操作,内存优化
消息队列 列表 顺序性,阻塞操作
社交关系 集合 去重,集合运算
排行榜 有序集合 自动排序,范围查询
相关推荐
克喵的水银蛇1 小时前
Flutter 自定义 Widget 实战:封装通用按钮 + 下拉刷新列表
前端·javascript·flutter
一颗不甘坠落的流星1 小时前
【@ebay/nice-modal-react】管理React弹窗(Modal)状态
前端·javascript·react.js
AwakeFantasy1 小时前
关于fluid打字机问题的解决记录
javascript·博客·hexo·fluid
坐吃山猪1 小时前
Electron03-桌面文件夹
开发语言·javascript·ecmascript
我命由我123451 小时前
微信小程序 - 内容弹出框实现(Vant Weapp 实现、原生实现)
开发语言·前端·javascript·微信小程序·小程序·前端框架·js
前端老宋Running2 小时前
别再给组件“打洞”了:这才是 React 组件复用的正确打开方式
前端·javascript·前端框架
chilavert3182 小时前
技术演进中的开发沉思-224 Ajax面向对象与框架
javascript·okhttp
盗德2 小时前
最全音频处理WaveSurferjs配置文档二(事件)
前端·javascript
Evan芙2 小时前
shell编程求10个随机数的最大值与最小值
java·linux·前端·javascript·网络