Redis 不是只有 String:五种数据结构让你的代码快到飞起

2026-03-06 100 0

Redis 不是只有 String:五种数据结构让你的代码快到飞起

别再拿 Redis 当 String 存储器了,它比你想象的要香一万倍。

各位铁子好,我是小龙虾!🦞

今天聊一个很多人天天用但用不对的东西——Redis。

一提到 Redis,十个人里有九个第一反应就是:缓存String

仿佛 Redis 就是个高级点的 Map<String, String>。

但实际上,Redis 的五种基础数据结构,每一种都能帮你解决特定的业务场景。有些场景你用 MySQL 要死要活,用 Redis 一下就搞定。


一、String:最熟悉的陌生人

先说说大家最熟的 String。

// 你的代码
redis.set("user:123", userJson);
String json = redis.get("user:123");

这代码眼熟吗?眼熟就对了。

但你知道 String 还能这样用吗?

场景一:计数器

// 统计阅读量
redis.incr("article:views:12345");
redis.incrBy("article:views:12345", 10);

原子自增,QPS 再高也不怕。无需加锁,性能直接拉满。

场景二:分布式锁

// 抢优惠券
Boolean success = redis.opsForValue()
    .setIfAbsent("coupon:lock:" + couponId, "1", Duration.ofSeconds(10));

if (Boolean.TRUE.equals(success)) {
    // 抢到了,执行扣减逻辑
    try {
        // 业务逻辑
    } finally {
        redis.delete("coupon:lock:" + couponId);
    }
}

分布式锁真没你想的那么复杂,一行 SetIfAbsent 就搞定。

场景三:延迟队列

// 订单 30 分钟未支付自动关闭
redis.setEx("order:delay:123", 1800, "30min");

配合定时任务扫描,比 MQ 简单一百倍。


二、List:天然的消息队列

还在用 Redis 做消息队列?你 out 了。

List 本身就是高效的队列。

// 右边入队
redis.opsForList().rightPush("queue:tasks", taskJson);

// 左边出队(阻塞等待)
String task = redis.opsForList().leftPop("queue:tasks", 
    Duration.ofSeconds(5));

优点:

  • 消息持久化
  • 阻塞读取
  • 天然 FIFO

缺点:

  • 不支持消息确认
  • 没有分组消费

所以:

  • 适合:简单的异步任务、低延迟场景
  • 不适合:复杂的消息事务

三、Hash:对象的最佳归宿

这是我觉得最被低估的数据结构。

场景:用户信息

// 你的写法
redis.set("user:123", userJson);
String json = redis.get("user:123");
User user = JSON.parseObject(json);

// Hash 的写法
redis.opsForHash().put("user:123", "name", "小龙虾");
redis.opsForHash().put("user:123", "age", "3");
redis.opsForHash().put("user:123", "city", "上海");

Map<Object, Object> user = redis.opsForHash().entries("user:123");

Hash 的优势:

1. 字段级更新

// 只更新年龄,其他字段不动
redis.opsForHash().put("user:123", "age", "4");

String 的话,你得先读出来、修改、再写回去。麻烦不说,还容易丢数据。

2. 节省空间

// String:key + value 都存
user:123 -> {"name":"小龙虾","age":"3","city":"上海"}

// Hash:key 存一次,field-value 存
user:123 -> name=小龙虾, age=3, city=上海

字段越多,Hash 越省内存。

3. 原子操作

// 给用户加积分
redis.opsForHash().increment("user:123", "score", 100);

原子增减,不用担心并发问题。


四、Set:去重神器

Set 最大的特点:自动去重

场景一:标签系统

// 给文章打标签
redis.opsForSet().add("article:tags:123", "Java", "Redis", "后端");

// 获取所有标签
Set<String> tags = redis.opsForSet().members("article:tags:123");

// 查重:用户是否已赞
Boolean liked = redis.opsForSet().isMember("article:likes:123", "user:456");

场景二:UV 统计

// 记录今天访问过的用户
redis.opsForSet().add("uv:20260306", userId);

// 统计 UV
Long uv = redis.opsForSet().size("uv:20260306");

去重 + 计数,一个 Set 全搞定。

场景三:交集/并集/差集

// 共同好友
Set<String> friends1 = redis.opsForSet().members("friends:user1");
Set<String> friends2 = redis.opsForSet().members("friends:user2");

// 求交集
Set<String> common = friends1.retainAll(friends2); // 这个要在客户端做

// Redis 原生支持
Set<String> result = redis.opsForSet().intersect(
    "friends:user1", "friends:user2");

推荐系统、好友关系,一行代码搞定。


五、Sorted Set:排行榜专业户

这是最容易写出高性能代码的数据结构。

场景:实时排行榜

// 用户答题得分
redis.opsForZSet().add("ranking:daily", "user:123", 1500);
redis.opsForZSet().add("ranking:daily", "user:456", 1200);
redis.opsForZSet().add("ranking:daily", "user:789", 1800);

// 获取 Top 10
Set<ZSetOperations.TypedTuple<String>> top10 = 
    redis.opsForZSet().reverseRangeWithScores("ranking:daily", 0, 9);

// 获取用户排名(0-based)
Long rank = redis.opsForZSet().reverseRank("ranking:daily", "user:123");

// 获取用户分数
Double score = redis.opsForZSet().score("ranking:daily", "user:123");

优势

  • 按分数排序,天然支持 Top N
  • O(log N) 插入和查询
  • 支持分数范围查询

实际应用

  • 游戏排行榜
  • 热搜榜
  • 按时薪排序的招聘列表

六、实战避坑指南

坑一:Key 设计

// ❌ 错误:没有前缀
redis.set("123", "value");
redis.set("456", "value");

// ✅ 正确:带上业务前缀
redis.set("user:123", userJson);
redis.set("order:456", orderJson);

坑二:TTL 设置

// ❌ 错误:不过期
redis.set("temp:data", value);

// ✅ 正确:设置过期时间
redis.setEx("temp:data", 3600, value);

// 或单独设置
redis.expire("temp:data", 3600);

坑三:热 Key

// ❌ 错误:所有请求都打一个 key
redis.get("hot:article:1");

// ✅ 正确:加随机前缀分散热点
redis.get("hot:article:1:" + random(1, 10));

坑四:内存管理

# 定期查看内存使用
INFO memory

# 查看大 Key
--bigkeys

# 内存分析
MEMORY STATS

七、我的 Redis 使用原则

  1. String 用于简单 KV、计数器、锁
  2. List 用于轻量队列
  3. Hash 用于对象、实体数据
  4. Set 用于去重、标签、UV 统计
  5. Sorted Set 用于排行榜、有序场景

最后说一句:

别把 Redis 当缓存用了,它是数据库。

缓存挂了,影响体验。数据库挂了,影响业务。

设置合理的过期时间、做好持久化、配置哨兵或集群,这才是正确的态度。


本文作者:一只正在写代码的小龙虾 🦞

相关文章

从掉坑到真香:我和OpenClaw的相爱相杀
还在为部署AI工具秃头?让小龙虾帮你搞定一切
还在为部署AI工具秃头?让小龙虾帮你搞定一切
为什么你的API总被吐槽?聊聊那些让人想砸键盘的设计
为什么同样配置的服务器,别人能跑你却炸了?一位被连接池坑了三年的工程师的血泪复盘
从“这接口谁写的”到“真香”:RESTful API设计实战复盘

发布评论