2026/4/6 9:48:57
网站建设
项目流程
苏州网站制作价格,沧州网络制作公司有哪些,seo公司排行,网站建设运营费用包括哪些#x1f4bb; Hello World, 我是 予枫。代码不止#xff0c;折腾不息。作为一个正在升级打怪的 Java 后端练习生#xff0c;我喜欢把踩过的坑和学到的招式记录下来。 保持空杯心态#xff0c;让我们开始今天的技术分享。Redis 作为一款高性能的内存数据库#xff0c;其核心… Hello World, 我是 予枫。代码不止折腾不息。作为一个正在升级打怪的Java 后端练习生我喜欢把踩过的坑和学到的招式记录下来。 保持空杯心态让我们开始今天的技术分享。Redis 作为一款高性能的内存数据库其核心优势不仅在于快更在于灵活强大的数据结构设计。Redis 提供了 5 种基础数据结构String字符串、Hash哈希、List列表、Set集合、Sorted Set有序集合。这些结构并非简单复刻传统数据结构而是结合内存存储特性做了极致优化每种结构都有其独特的底层实现、适用场景和命令体系。本文将从「底层实现原理」「核心常用命令」「Java 实战场景」三个维度逐一拆解这 5 种数据结构重点对比不同结构的适用场景差异比如“为什么存购物车用 Hash 而不是 String”让你既能懂原理又能落地用。一、StringRedis 最基础的“万能容器”String 是 Redis 最核心、最常用的数据结构所有键的底层都是 String 类型同时它也能存储字符串、数字整数/浮点数、二进制数据如图片Base64编码最大存储容量为 512MB。1.1 底层实现SDS简单动态字符串很多人以为 String 底层是 C 语言的原生字符串以 \0 结尾的字符数组但实际上 Redis 自定义了 SDSSimple Dynamic String来实现 String 结构原因是 C 原生字符串存在三大缺陷长度计算低效获取字符串长度需遍历整个数组时间复杂度 O(n)内存溢出风险修改字符串时如拼接需手动扩容否则会溢出二进制安全差无法存储包含 \0 的二进制数据\0 会被当作字符串结束符。SDS 的结构如下Redis 3.2 版本优化后struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; // 已使用字节数长度O(1)获取 uint8_t alloc; // 总分配字节数不含头部和终止符 unsigned char flags; // 标志位区分sdshdr8/16/32/64 char buf[]; // 存储字符串的字节数组 };SDS 解决了 C 原生字符串的痛点长度计算 O(1)通过 len 字段直接获取自动扩容修改时先检查空间不足则按“当前长度*2小于1MB时”或“1MB大于1MB时”扩容避免溢出二进制安全以 len 字段判断结束而非 \0可存储图片、视频等二进制数据。1.2 核心常用命令String 命令简洁直观重点关注“原子操作”和“过期时间”相关命令命令功能描述示例备注SET key value设置键值对SET verify_code:13800138000 123456覆盖已有keyGET key获取key对应的值GET verify_code:13800138000不存在返回nilSETNX key value仅当key不存在时设置SETNX lock:order 1分布式锁核心命令SETEX key seconds value设置键值并指定过期时间秒SETEX verify_code:13800138000 300 123456验证码场景常用INCR key整数自增1原子操作INCR counter:page_view仅适用于数字类型值DECR key整数自减1原子操作DECR stock:goods1001库存扣减场景常用APPEND key value字符串拼接APPEND user:name Zhang原value为Li结果为LiZhang1.3 Java 实战场景String 适用场景单值存储、计数器、验证码、token、分布式锁等。以下是基于 Spring Data Redis 的实战示例需提前配置 RedisTemplate场景1存储手机验证码5分钟过期Autowired private RedisTemplateString, String redisTemplate; // 生成并存储验证码 public String generateVerifyCode(String phone) { // 生成6位验证码 String code RandomStringUtils.randomNumeric(6); // 存储keyverify_code:手机号过期时间300秒 redisTemplate.opsForValue().set( verify_code: phone, code, 300, TimeUnit.SECONDS ); return code; } // 验证验证码 public boolean checkVerifyCode(String phone, String inputCode) { String key verify_code: phone; // 获取存储的验证码 String storedCode redisTemplate.opsForValue().get(key); // 对比注意空值判断 return inputCode ! null inputCode.equals(storedCode); }场景2页面访问量计数器原子自增// 页面访问量自增 public Long incrementPageView(String pageId) { String key page_view: pageId; // 原子自增1返回自增后的值 return redisTemplate.opsForValue().increment(key); } // 获取页面访问量 public Long getPageView(String pageId) { String key page_view: pageId; String viewCount redisTemplate.opsForValue().get(key); return viewCount null ? 0 : Long.parseLong(viewCount); }注意String 适合“单键单值”场景若需存储“单键多字段”数据如用户信息id、name、age用 String 会存在弊端下文 Hash 部分对比。二、Hash单键多字段的“高效容器”Hash 是 Redis 专门为“单键多字段”场景设计的数据结构类似 Java 中的 HashMap存储的是“键值对集合”key → field → value其中 key 是Redis的键field 和 value 是 Hash 内部的字段和值适合存储对象类数据如用户、商品、购物车。2.1 底层实现哈希表 压缩列表ziplistHash 底层采用“双编码”机制根据存储数据的大小自动切换实现目的是平衡内存占用和查询效率1压缩列表ziplist小数据场景当 Hash 满足以下两个条件时底层使用压缩列表一种紧凑的连续内存结构哈希表中元素个数 ≤ 配置值hash-max-ziplist-entries默认512每个元素的 value 长度 ≤ 配置值hash-max-ziplist-value默认64字节。压缩列表的优势是内存占用极低连续内存无冗余指针缺点是修改插入/删除时需移动后续元素效率较低O(n)适合小数据量场景。2哈希表dict大数据场景当上述两个条件任意一个不满足时底层自动切换为哈希表类似 Java 的 HashMap。Redis 的哈希表采用“链地址法”解决哈希冲突结构如下哈希表数组dictEntry[]每个元素是一个链表的头节点dictEntry 节点存储 field、value、next 指针指向冲突节点扩容机制当负载因子used/size≥ 1 时自动扩容为原来的 2 倍翻倍扩容。哈希表的优势是查询、插入、删除效率高平均 O(1)缺点是内存占用比压缩列表大适合大数据量场景。2.2 核心常用命令命令功能描述示例备注HSET key field value设置Hash的field-valueHSET user:100 name Zhang age 25可同时设置多个fieldHGET key field获取Hash的field对应的值HGET user:100 name不存在返回nilHMGET key field1 field2批量获取多个field的值HMGET user:100 name age返回值列表HGETALL key获取Hash的所有field和valueHGETALL user:100大数据量时慎用阻塞HKEYS key获取Hash的所有fieldHKEYS user:100返回field列表HVALS key获取Hash的所有valueHVALS user:100返回value列表HDEL key field1 field2删除Hash的多个fieldHDEL user:100 age返回删除的field个数HEXISTS key field判断Hash的field是否存在HEXISTS user:100 name存在返回1不存在返回02.3 Java 实战场景 适用场景对比Hash 适用场景存储对象用户、商品、购物车、多字段配置等。核心优势是“精准操作单个字段”无需全量更新/获取数据这是 String 无法比拟的。核心对比为什么存购物车用 Hash 而不是 String假设要存储用户1001的购物车包含商品ID、名称、数量、单价两种方案对比方案1用 String 存储JSON格式// 存储购物车keycart:1001valueJSON字符串 String cartJson {\goods1001\:{\name\:\iPhone15\,\num\:1,\price\:5999},\goods1002\:{\name\:\AirPods\,\num\:2,\price\:1299}}; redisTemplate.opsForValue().set(cart:1001, cartJson); // 需求修改goods1001的数量为2 // 步骤1. 获取全量JSON → 2. 反序列化为对象 → 3. 修改num字段 → 4. 重新序列化 → 5. 全量存储 String storedJson redisTemplate.opsForValue().get(cart:1001); MapString, Goods cartMap JSON.parseObject(storedJson, new TypeReferenceMapString, Goods(){}); cartMap.get(goods1001).setNum(2); redisTemplate.opsForValue().set(cart:1001, JSON.toJSONString(cartMap));弊端性能差每次修改都要全量获取/序列化/存储数据越大开销越大内存浪费即使只改一个字段也要存储整个JSON字符串原子性差多线程修改时可能出现并发问题需额外加锁。方案2用 Hash 存储keycart:1001field商品IDvalue商品信息JSON// 存储购物车keycart:1001fieldgoods1001/goods1002value商品信息JSON redisTemplate.opsForHash().put(cart:1001, goods1001, JSON.toJSONString(new Goods(iPhone15, 1, 5999))); redisTemplate.opsForHash().put(cart:1001, goods1002, JSON.toJSONString(new Goods(AirPods, 2, 1299))); // 需求修改goods1001的数量为2 // 步骤1. 获取该商品的信息 → 2. 修改字段 → 3. 仅更新该field String goodsJson (String) redisTemplate.opsForHash().get(cart:1001, goods1001); Goods goods JSON.parseObject(goodsJson, Goods.class); goods.setNum(2); redisTemplate.opsForHash().put(cart:1001, goods1001, JSON.toJSONString(goods));优势性能优仅操作单个商品的field无需全量处理数据内存高效修改时仅更新单个field的value无冗余数据操作灵活可单独添加/删除商品HDEL、批量获取商品HMGET。场景完整购物车实现添加、修改数量、删除、查询// 购物车商品实体 Data class CartGoods { private String goodsId; private String name; private Integer num; private BigDecimal price; } // 1. 添加商品到购物车 public void addCart(String userId, CartGoods goods) { String key cart: userId; // field商品IDvalue商品JSON redisTemplate.opsForHash().put(key, goods.getGoodsId(), JSON.toJSONString(goods)); } // 2. 修改购物车商品数量 public void updateCartNum(String userId, String goodsId, Integer num) { String key cart: userId; // 先判断商品是否在购物车中 Boolean hasGoods redisTemplate.opsForHash().hasKey(key, goodsId); if (Boolean.TRUE.equals(hasGoods)) { String goodsJson (String) redisTemplate.opsForHash().get(key, goodsId); CartGoods goods JSON.parseObject(goodsJson, CartGoods.class); goods.setNum(num); redisTemplate.opsForHash().put(key, goodsId, JSON.toJSONString(goods)); } } // 3. 删除购物车商品 public void deleteCartGoods(String userId, String... goodsIds) { String key cart: userId; redisTemplate.opsForHash().delete(key, (Object[]) goodsIds); } // 4. 查询用户购物车所有商品 public ListCartGoods getCartAll(String userId) { String key cart: userId; // 获取所有field对应的value商品JSON ListString goodsJsonList redisTemplate.opsForHash().values(key); // 反序列化为CartGoods列表 return goodsJsonList.stream() .map(json - JSON.parseObject(json, CartGoods.class)) .collect(Collectors.toList()); }三、List有序可重复的“队列容器”List 是 Redis 中的有序、可重复的集合类似 Java 中的 LinkedList双向链表支持从两端插入/删除元素核心优势是“有序性”和“双向操作”适合实现队列、栈、最新消息列表等场景。3.1 底层实现双向链表 压缩列表ziplistList 的底层实现和 Hash 类似也是“双编码”机制根据数据大小自动切换1压缩列表ziplist小数据场景满足以下两个条件时使用压缩列表列表中元素个数 ≤ 配置值list-max-ziplist-entries默认512每个元素的长度 ≤ 配置值list-max-ziplist-value默认64字节。2双向链表adlist大数据场景当上述条件不满足时切换为双向链表。双向链表的结构特点每个节点listNode包含 prev、next 指针支持双向遍历链表头list包含 head、tail 指针和 len 字段长度支持 O(1) 获取首尾元素和长度优势插入/删除元素效率高O(1)仅需修改指针缺点内存占用大每个节点有额外指针。注意Redis 3.2 版本引入了 quicklist快速列表替代了“双向链表压缩列表”的组合。quicklist 是“压缩列表的双向链表”将多个压缩列表用双向链表连接既保证了内存效率又支持快速的两端操作。3.2 核心常用命令List 命令核心围绕“两端操作”和“范围查询”重点关注阻塞命令用于消息队列命令功能描述示例备注LPUSH key value1 value2从列表左侧头部插入元素LPUSH msg:list msg1 msg2插入后列表msg2, msg1RPUSH key value1 value2从列表右侧尾部插入元素RPUSH msg:list msg3 msg4插入后列表msg2, msg1, msg3, msg4LPOP key从列表左侧弹出元素删除并返回LPOP msg:list返回msg2列表剩余msg1, msg3, msg4RPOP key从列表右侧弹出元素RPOP msg:list返回msg4列表剩余msg1, msg3LRANGE key start stop获取列表中[start, stop]范围的元素LRANGE msg:list 0 -1获取所有元素0第一个-1最后一个LLEN key获取列表长度LLEN msg:list返回列表元素个数BLPOP key timeout阻塞式左侧弹出无元素时阻塞timeout秒BLPOP queue:order 0timeout0表示永久阻塞消息队列核心命令3.3 Java 实战场景List 适用场景消息队列生产者-消费者模型、最新消息列表如朋友圈动态、栈LPUSHLPOP、队列LPUSHRPOP 或 RPUSHLPOP。场景1基于 List 实现简单消息队列阻塞式消费// 生产者发送消息从右侧插入 public void sendMessage(String queueName, String message) { redisTemplate.opsForList().rightPush(queueName, message); } // 消费者阻塞式接收消息从左侧弹出永久阻塞 Async // 异步消费避免阻塞主线程 public void consumeMessage(String queueName) { while (true) { try { // 阻塞式弹出无消息时阻塞有消息时立即返回 String message (String) redisTemplate.opsForList().leftPop(queueName, 0, TimeUnit.SECONDS); if (message ! null) { // 处理消息如订单支付回调、通知推送 System.out.println(消费消息 message); handleMessage(message); } } catch (Exception e) { // 异常处理如重试、日志记录 log.error(消息消费异常, e); try { Thread.sleep(1000); // 异常时休眠1秒避免频繁重试 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } } // 消息处理逻辑 private void handleMessage(String message) { // 业务逻辑如解析消息JSON执行对应操作 }场景2展示用户最新5条动态按时间倒序// 发布动态从左侧插入保证最新动态在最前面 public void publishDynamic(String userId, String dynamicContent) { String key dynamic: userId; // 左侧插入最新动态排在第一位 redisTemplate.opsForList().leftPush(key, dynamicContent); // 只保留最新5条动态超出部分从右侧删除 redisTemplate.opsForList().trim(key, 0, 4); // [0,4] 共5个元素 } // 获取用户最新5条动态 public ListString getLatestDynamics(String userId, int count) { String key dynamic: userId; // 获取前count条动态从0到count-1 return redisTemplate.opsForList().range(key, 0, count - 1); }注意List 是有序可重复的若需要“去重”的有序集合需使用 Sorted Set下文讲解。四、Set无序不可重复的“集合容器”Set 是 Redis 中的无序、不可重复的集合类似 Java 中的 HashSet底层基于哈希表实现核心优势是“自动去重”和“高效的集合运算”交集、并集、差集适合存储无重复的元素集合如好友列表、标签、黑名单。4.1 底层实现哈希表 整数集合intsetSet 底层同样采用“双编码”机制根据存储元素的类型和数量自动切换1整数集合intset小整数场景当 Set 满足以下两个条件时底层使用整数集合一种紧凑的整数存储结构集合中所有元素都是整数int16_t、int32_t、int64_t集合中元素个数 ≤ 配置值set-max-intset-entries默认512。整数集合的优势是内存占用极低连续内存存储整数缺点是修改时需移动元素适合小量整数场景。2哈希表dict通用场景当上述条件不满足时底层使用哈希表。Set 的哈希表实现中仅存储 keySet 中的元素value 固定为 null利用哈希表的“键唯一”特性实现 Set 的“不可重复”功能。优势添加、删除、查询元素效率高平均 O(1)支持高效的集合运算缺点无序Redis 3.2 版本新增 Sorted Set 解决有序问题。4.2 核心常用命令命令功能描述示例备注SADD key member1 member2向Set中添加元素自动去重SADD user:1001:friends 2001 2002 2003重复元素会被忽略SREM key member1 member2从Set中删除元素SREM user:1001:friends 2003返回删除的元素个数SMEMBERS key获取Set中的所有元素SMEMBERS user:1001:friends无序返回SISMEMBER key member判断元素是否在Set中SISMEMBER user:1001:friends 2002存在返回1不存在返回0SCARD key获取Set的元素个数SCARD user:1001:friends返回元素总数SINTER key1 key2求两个Set的交集共同元素SINTER user:1001:friends user:1002:friends返回交集元素SUNION key1 key2求两个Set的并集所有元素去重SUNION user:1001:friends user:1002:friends返回并集元素SDIFF key1 key2求两个Set的差集key1有但key2没有SDIFF user:1001:friends user:1002:friends返回差集元素4.3 Java 实战场景Set 适用场景好友列表、粉丝列表、标签存储如文章标签、黑名单/白名单、共同好友计算等。核心优势是“自动去重”和“高效集合运算”。场景1存储用户标签自动去重// 给用户添加标签自动去重 public void addUserTags(String userId, String... tags) { String key tag:user: userId; redisTemplate.opsForSet().add(key, tags); } // 移除用户的某个标签 public void removeUserTag(String userId, String tag) { String key tag:user: userId; redisTemplate.opsForSet().remove(key, tag); } // 获取用户的所有标签 public SetString getUserTags(String userId) { String key tag:user: userId; return redisTemplate.opsForSet().members(key); } // 判断用户是否有某个标签 public boolean hasUserTag(String userId, String tag) { String key tag:user: userId; return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, tag)); }场景2计算两个用户的共同好友// 给用户添加好友自动去重 public void addFriend(String userId, String friendId) { String key friend: userId; redisTemplate.opsForSet().add(key, friendId); } // 计算用户A和用户B的共同好友 public SetString getCommonFriends(String userIdA, String userIdB) { String keyA friend: userIdA; String keyB friend: userIdB; // 求两个Set的交集 return redisTemplate.opsForSet().intersect(keyA, keyB); }五、Sorted Set有序不可重复的“排序集合”Sorted Set简称 ZSet是 Redis 中最强大的数据结构之一兼具 Set 的“不可重复”特性和 List 的“有序”特性同时支持按分数score排序和范围查询类似 Java 中的“TreeSet HashMap”组合分数对应排序键元素对应唯一值。5.1 底层实现跳跃表skiplist 哈希表dictZSet 底层采用“跳跃表 哈希表”的组合实现两种结构协同工作兼顾排序和查询效率1跳跃表skiplist负责排序和范围查询跳跃表是一种有序数据结构通过“多层索引”提升查询效率类似书籍的目录一级目录、二级目录。其结构特点每个节点zskiplistNode包含元素member、分数score、多层前进指针forward最底层的节点按分数有序排列上层索引节点是底层节点的子集用于快速定位查询效率平均 O(log n)最坏 O(n)优于链表的 O(n)略逊于红黑树的 O(log n)但实现更简单。2哈希表dict负责快速查询元素的分数哈希表的 key 是 ZSet 中的元素membervalue 是该元素的分数score支持 O(1) 时间复杂度的查询元素的分数判断元素是否存在更新元素的分数。总结ZSet 底层通过“跳跃表排序 哈希表快速查询”完美解决了“有序”和“高效操作”的矛盾是 Redis 最核心的优化之一。5.2 核心常用命令ZSet 命令核心围绕“分数操作”和“有序范围查询”重点关注排序方向升序/降序和范围条件按排名/按分数命令功能描述示例备注ZADD key score1 member1 score2 member2向ZSet中添加元素按score排序ZADD rank:score 95 Zhang 88 Li 92 Wang元素重复时更新score并重新排序ZSCORE key member获取元素的scoreZSCORE rank:score Zhang返回元素的分数浮点数ZREM key member1 member2从ZSet中删除元素ZREM rank:score Li返回删除的元素个数ZRANGE key start stop [WITHSCORES]按score升序获取[start,stop]排名的元素ZRANGE rank:score 0 2 WITHSCORESWITHSCORES同时返回scoreZREVRANGE key start stop [WITHSCORES]按score降序获取[start,stop]排名的元素ZREVRANGE rank:score 0 2 WITHSCORES排行榜常用降序取前N名ZRANK key member获取元素的升序排名从0开始ZRANK rank:score Zhangscore最小的排名为0ZREVRANK key member获取元素的降序排名从0开始ZREVRANK rank:score Zhangscore最大的排名为0排行榜第1名ZCARD key获取ZSet的元素个数ZCARD rank:score返回元素总数ZINCRBY key increment member给元素的score增加increment原子操作ZINCRBY rank:score 2 Wang适合积分、分数累加场景5.3 Java 实战场景ZSet 适用场景排行榜如积分排名、销量排名、延迟队列、有序去重集合如热门商品排序等。核心优势是“有序性不可重复高效分数操作”是复杂业务场景的首选数据结构。场景1实现用户积分排行榜降序排名// 1. 新增/更新用户积分原子操作 public void updateUserScore(String userId, double score) { String key rank:user:score; // ZADD会自动去重重复用户会更新分数并重新排序 redisTemplate.opsForZSet().add(key, userId, score); } // 2. 给用户积分累加如签到加10分原子操作 public void incrementUserScore(String userId, double increment) { String key rank:user:score; redisTemplate.opsForZSet().incrementScore(key, userId, increment); } // 3. 获取积分排行榜前10名降序带排名和分数 public ListMapString, Object getTop10ScoreRank() { String key rank:user:score; // ZREVRANGE降序取前10名0-9带分数 SetZSetOperations.TypedTupleString typedTuples redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, 9); ListMapString, Objectgt;gt; rankList new ArrayList(); if (typedTuples ! null) { int rank 1; for (ZSetOperations.TypedTupleString tuple : typedTuples) { MapString, Object rankInfo new HashMap(); rankInfo.put(rank, rank); // 排名从1开始 rankInfo.put(userId, tuple.getValue()); // 用户ID rankInfo.put(score, tuple.getScore()); // 积分 rankList.add(rankInfo); } } return rankList; } // 4. 获取某个用户的积分和排名 public MapString, Object getUserScoreAndRank(String userId) { String key rank:user:score; MapString, Object userRankInfo new HashMap(); // 获取积分 Double score redisTemplate.opsForZSet().score(key, userId); // 获取降序排名排名从0开始需1 Long rank redisTemplate.opsForZSet().reverseRank(key, userId); userRankInfo.put(userId, userId); userRankInfo.put(score, score null ? 0 : score); userRankInfo.put(rank, rank null ? -1 : rank 1); // -1表示未上榜 return userRankInfo; }场景2基于ZSet实现延迟队列按时间戳排序// 延迟队列核心score延迟执行时间戳毫秒member消息内容 private static final String DELAY_QUEUE_KEY queue:delay; // 1. 发送延迟消息如3秒后执行 public void sendDelayMessage(String message, long delayMillis) { // 计算消息执行时间戳当前时间延迟时间 long executeTimestamp System.currentTimeMillis() delayMillis; redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, message, executeTimestamp); } // 2. 消费延迟消息定时轮询获取已到执行时间的消息 Scheduled(fixedRate 1000) // 每秒轮询一次 public void consumeDelayMessage() { long currentTimestamp System.currentTimeMillis(); // ZRANGEBYSCORE获取score≤当前时间戳的所有消息已到执行时间 SetString messages redisTemplate.opsForZSet().rangeByScore(DELAY_QUEUE_KEY, 0, currentTimestamp); if (messages ! null !messages.isEmpty()) { for (String message : messages) { // 先删除消息避免重复消费再处理业务 Boolean isRemoved redisTemplate.opsForZSet().remove(DELAY_QUEUE_KEY, message); if (Boolean.TRUE.equals(isRemoved)) { // 处理延迟消息如订单超时取消、短信延迟推送 System.out.println(处理延迟消息 message); handleDelayMessage(message); } } } } // 延迟消息处理逻辑 private void handleDelayMessage(String message) { // 业务逻辑如解析消息执行对应操作 }六、全文总结Redis 5种基础数据结构并非孤立存在而是针对不同业务场景设计的“最优解”。掌握它们的核心关键是“懂底层、会选型、能落地”String单键单值的“万能容器”适合验证码、计数器、token等简单场景底层SDS保证高效和二进制安全Hash单键多字段的“对象容器”适合存储用户、商品、购物车等精准操作单个字段比String更高效灵活List有序可重复的“队列容器”适合消息队列、最新动态列表等双向操作和阻塞命令是核心优势Set无序不可重复的“集合容器”适合好友列表、标签、共同元素计算等自动去重和高效集合运算为核心价值Sorted Set有序不可重复的“高级容器”适合排行榜、延迟队列等复杂场景跳跃表哈希表的组合兼顾排序和查询效率。核心选型原则不追求“最强大”只选择“最适配”。比如存购物车不用String全量操作低效做排行榜不用List排序和去重麻烦根据“是否有序、是否重复、是否多字段、是否需要复杂运算”四个维度就能快速锁定合适的数据结构。Redis的强大本质上是数据结构的强大。熟练掌握这5种基础结构的底层原理和实战场景才能真正发挥Redis的高性能优势应对各类业务场景的挑战。关注【予枫】获取更多技术干货身份一名热爱技术的研二学生️标签Java / 算法 / 个人成长Slogan只写对自己和他人有用的文字。