Redis Java 开发系列#2 数据结构

前言

本篇读者收益

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

先修要求

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

关键要点

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

背景简述

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

Redis 数据结构体系​:

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

环境准备与快速上手

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

复制代码

Java

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

体验AI代码助手

代码解读

复制代码

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

小结

业务场景 推荐数据结构 关键优势
缓存数据 字符串 简单高效,支持过期
用户信息 哈希 字段操作,内存优化
消息队列 列表 顺序性,阻塞操作
社交关系 集合 去重,集合运算
排行榜 有序集合 自动排序,范围查询
相关推荐
a努力。4 小时前
腾讯Java面试被问:String、StringBuffer、StringBuilder区别
java·开发语言·后端·面试·职场和发展·架构
Vic101015 小时前
解决 Spring Security 在异步线程中用户信息丢失的问题
java·前端·spring
立志成为大牛的小牛5 小时前
数据结构——五十五、散列查找的性能分析(线性探测法)(王道408)
数据结构·程序人生·考研·算法
QD_IT伟5 小时前
SpringBoot项目整合Tlog 数据链路的规范加强
java·spring boot·后端
源码获取_wx:Fegn08956 小时前
基于springboot + vue二手交易管理系统
java·vue.js·spring boot·后端·spring·课程设计
Zsh-cs6 小时前
Spring
java·数据库·spring
爬山算法6 小时前
Springboot请求和响应相关注解及使用场景
java·spring boot·后端
程序员水自流6 小时前
MySQL InnoDB存储引擎详细介绍之事务
java·数据库·mysql·oracle
请为小H留灯6 小时前
Java实际开发@常用注解(附实战场景)
java·后端·个人开发