一、数据结构与操作
(一)基本数据结构
1. 字符串(String)
String 是 Redis 中最基础、最常用的数据结构。它是二进制安全的,也就是说可以存储任意类型的数据,例如:
普通字符串
整数、浮点数
图片(Base64 编码后)
JSON / 序列化对象 / 二进制内容
(1)典型应用场景
①简单数据存储
适用于存储常规文本或对象的场景:
用户 session、token
图片地址 / 用户昵称等基础属性
存储序列化对象(相比 Hash 更节省内存)
相关命令:SET、GET
②计数器业务
Redis 对整数操作原子化,非常适合:
接口限流(统计用户单位时间请求数)
文章浏览量 PV
点赞次数计数
相关命令:INCR、DECR
③分布式锁(SETNX 原理版)
可通过 SETNX key value 实现简单锁(生产需改良,如 RedLock)。
(2)基本操作
| 操作 | 含义 |
|---|---|
SET key value |
设置 key 的值 |
GET key |
获取 key 的值 |
EXISTS key |
判断 key 是否存在 |
DEL key |
删除指定 key |
(3)批量操作
| 命令 | 含义 |
|---|---|
MSET key value [key value ...] |
批量设置键值对(批量写) |
MGET key1 [key2 ...] |
批量获取多个 key 的值(批量读) |
(4)数值操作(原子性)
| 命令 | 含义 |
|---|---|
INCR key |
值加 1 |
DECR key |
值减 1 |
INCRBY key increment |
增加指定整数 |
INCRBYFLOAT key increment |
增加浮点数 |
(5)设置过期时间
| 命令 | 含义 |
|---|---|
EXPIRE key seconds |
设置过期时间(秒) |
TTL key |
查询剩余生存时间 |
SETEX key seconds value |
设置值并设置过期时间 |
PSETEX key milliseconds value |
毫秒级过期时间 |
SETNX key value |
不存在时才设置(可用于锁) |
(6)String 常用命令(按功能分类)
设置/获取
SET key value
GET key
GETSET key value:设置新值并返回旧值
STRLEN key:返回字符串长度
追加、覆盖
APPEND key value:追加字符串
SETRANGE key offset value:从 offset 开始覆盖
范围和位图
GETRANGE key start end:获取子字符串
GETBIT key offset:获取某位的二进制值
SETBIT key offset value:修改某位的二进制值
批量操作
MSET key value [...]
MGET key1 [...]
(7)Java 中的 String 类型映射(Jedis / Spring Data Redis)
①Jedis 使用示例
基本 set/get
java
Jedis jedis = new Jedis("localhost", 6379);
jedis.set("username", "xiaoman");
String name = jedis.get("username");
System.out.println(name);
数值操作(INCR / DECR)
java
Jedis jedis = new Jedis("localhost", 6379);
jedis.incr("count");
jedis.decr("count");
System.out.println(jedis.get("count"));
SETNX 分布式锁
java
/**
* 获取锁(SETNX)
*/
public boolean tryLock(Jedis jedis, String lockKey, String lockValue) {
Long result = jedis.setnx(lockKey, lockValue);
return result == 1; // 返回 1 代表成功获取锁
}
/**
* 释放锁
*/
public void unlock(Jedis jedis, String lockKey, String lockValue) {
String current = jedis.get(lockKey);
if (lockValue.equals(current)) {
jedis.del(lockKey);
}
}
说明:这是最基础的分布式锁实现,仅用于理解原理。
②Spring Data Redis 使用示例
基本 set/get
java
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 写入
stringRedisTemplate.opsForValue().set("token", "abc123");
// 读取
String token = stringRedisTemplate.opsForValue().get("token");
数值操作(increment/decrement)
java
// 自增
stringRedisTemplate.opsForValue().increment("count");
// 自减
stringRedisTemplate.opsForValue().decrement("count");
分布式锁(SETNX + 过期时间)
java
/**
* 获取分布式锁:setIfAbsent 相当于 SETNX
*/
public boolean tryLock(String key, String value, long expireSeconds) {
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
key, value, Duration.ofSeconds(expireSeconds)
);
return Boolean.TRUE.equals(success);
}
/**
* 释放锁(确保只释放自己的锁)
*/
public void unlock(String key, String value) {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if (value.equals(currentValue)) {
stringRedisTemplate.delete(key);
}
}
2. 列表(List)
List 是 按插入顺序排序 的字符串列表,支持:
双端插入/弹出(栈/队列)
范围查询(LRANGE)
阻塞操作(BLPOP)
(1)核心特性
| 特性 | 说明 | 类似的数据结构 |
|---|---|---|
| 有序 | 元素按插入顺序排序 | 链表 |
| 可从两端插入/弹出 | LPUSH / RPUSH / LPOP / RPOP | 栈、队列、消息队列 |
| 支持范围查询 | LRANGE 速度快(O(n)) | 分页展示 |
(2)典型应用场景
① 消息流(Timeline)
如:微博最新动态
常用命令:LPUSH + LRANGE
② 队列(先进先出 FIFO)
异步任务队列
RPUSH(入队)+ LPOP(出队)
③ 栈(后进先出 LIFO)
浏览器前进后退
LPUSH(入栈)+ LPOP(出栈)
④ 阻塞队列(BLPOP/BRPOP)
可实现简单 MQ,消费者等待生产者。
(3)常用操作汇总
插入
| 命令 | 说明 |
|---|---|
LPUSH key v1 [v2...] |
从左侧插入 |
RPUSH key v1 [v2...] |
从右侧插入 |
LPUSHX key v |
列表存在才左插 |
RPUSHX key v |
列表存在才右插 |
删除 / 弹出
| 命令 | 说明 |
|---|---|
LPOP key |
左侧弹出 |
RPOP key |
右侧弹出 |
LREM key count value |
删除 count 个指定 value |
获取 / 查询
| 命令 | 说明 |
|---|---|
LRANGE key start stop |
获取区间元素,如:0 -1 获取全部 |
LINDEX key index |
按索引取值 |
LLEN key |
获取列表长度 |
修改 / 限制
| 命令 | 说明 |
|---|---|
LSET key index value |
修改指定位置的值 |
LTRIM key start stop |
保留某一段,其余删除 |
阻塞操作(适合消息队列)
| 命令 | 说明 |
|---|---|
BLPOP key timeout |
阻塞式 LPOP |
BRPOP key timeout |
阻塞式 RPOP |
BRPOPLPUSH source dest timeout |
阻塞式 RPOPLPUSH |
(4)Java 中操作 Redis List 的示例(Spring Data Redis + Lettuce)
配置 RedisTemplate:
java
@Autowired
private StringRedisTemplate redisTemplate;
插入元素(LPUSH / RPUSH)
java
// 左侧插入
redisTemplate.opsForList().leftPush("list1", "a");
// 右侧插入
redisTemplate.opsForList().rightPush("list1", "b");
// 批量插入
redisTemplate.opsForList().rightPushAll("list1", "c", "d", "e");
弹出元素(LPOP / RPOP)
java
String v1 = redisTemplate.opsForList().leftPop("list1");
String v2 = redisTemplate.opsForList().rightPop("list1");
查看区间元素(LRANGE)
java
List<String> list = redisTemplate.opsForList().range("list1", 0, -1);
获取长度(LLEN)
java
Long len = redisTemplate.opsForList().size("list1");
获取某一个索引的值(LINDEX)
java
String value = redisTemplate.opsForList().index("list1", 2);
设置某一位置的值(LSET)
java
redisTemplate.opsForList().set("list1", 1, "newValue");
删除指定元素(LREM)
java
redisTemplate.opsForList().remove("list1", 2, "a");
// 删除2个"a"
保留指定区间(LTRIM)
java
redisTemplate.opsForList().trim("list1", 0, 10);
// 只保留前11个元素
阻塞式弹出(BLPOP / BRPOP)
java
// 等待 5 秒,直到有元素可取
String v = redisTemplate.opsForList().leftPop("queue", 5, TimeUnit.SECONDS);
适合做消费者。
(5)生产环境 List
用 List 实现简单队列
生产者:RPUSH
消费者:BLPOP(阻塞)
不推荐用 List 做无限长度队列
建议配合 LTRIM 控制长度:
java
redisTemplate.opsForList().rightPush("feed", "item");
redisTemplate.opsForList().trim("feed", 0, 999); // 控制列表最多 1000 条
3. 哈希(Hash)
Redis Hash 是一个 field → value 的映射表,非常适合存储对象,支持对对象字段进行独立修改,而不需要整个对象序列化后再存一次。
(1)Hash 的典型应用场景
对象存储
非常适合保存结构化对象,例如:
用户信息
商品信息
文章信息
配置项信息
示例结构:
java
user:1001
├── name → xiaoman
├── age → 18
└── city → beijing
相关命令:
HSET / HMSET(设置字段)
HGET / HMGET(获取字段)
HDEL(删除字段)
HGETALL(获取整个对象)
购物车
key = cart:userId
field = 商品ID
value = 数量或 JSON 信息
常用命令:
添加商品:HSET
数量+1:HINCRBY
删除商品:HDEL
商品总数:HLEN
获取全部商品:HGETALL
(2)基本操作:
| 命令 | 说明 |
|---|---|
HSET key field value |
设置某个字段值(覆盖) |
HMSET key field1 value1 ... |
设置多个字段(Redis 4.0 后建议使用 HSET 多参数形式) |
HGET key field |
获取某字段 |
HMGET key f1 f2 ... |
获取多个字段 |
HGETALL key |
获取所有字段和值 |
HDEL key f1 [f2 ...] |
删除一个或多个字段 |
HEXISTS key field |
判断字段是否存在 |
HLEN key |
字段数量 |
HKEYS key |
获取所有 field |
HVALS key |
获取所有 value |
HINCRBY key field n |
对整数字段加 n |
HINCRBYFLOAT key field n |
对浮点字段加 n |
HSETNX key field value |
字段不存在时设置 |
HSCAN key cursor [MATCH pattern] [COUNT n] |
遍历大哈希表(分页迭代) |
(3)Hash 操作汇总
写字段
HSET key field value
HSET key field1 value1 field2 value2 ...(Redis 4.0+ 推荐)
HSETNX key field value
HINCRBY key field increment
HINCRBYFLOAT key field increment
读字段
HGET key field
HMGET key field1 field2 ...
HGETALL key
HKEYS key
HVALS key
HLEN key
删字段
HDEL key field1 [field2 ...]
遍历
HSCAN key cursor MATCH xx COUNT n
(4)Hash 使用注意事项
不要把特别大的对象放在一个 Hash
例如包含 10 万字段,这会:
HGETALL 非常慢
集群模式下扩容不均衡
HSCAN 复杂度变大
Hash 一个字段的值不能超过 512MB
Hash 适合结构化数据,不适合 JSON 长文本
如果 JSON 只是单值存储,推荐用 String。
(5)Java(Spring Data Redis + Lettuce)操作 Hash 示例
假设已注入:
java
@Autowired
private StringRedisTemplate redisTemplate;
添加 / 修改字段(HSET)
java
redisTemplate.opsForHash().put("user:1001", "name", "xiaoman");
redisTemplate.opsForHash().put("user:1001", "age", "18");
批量添加字段(HMSET → 使用 putAll)
java
Map<String, String> data = new HashMap<>();
data.put("name", "xiaoman");
data.put("city", "beijing");
redisTemplate.opsForHash().putAll("user:1001", data);
获取字段(HGET)
java
String name = (String) redisTemplate.opsForHash().get("user:1001", "name");
获取多个字段(HMGET)
java
List<Object> list = redisTemplate.opsForHash().multiGet("user:1001", Arrays.asList("name", "city"));
字段+1(HINCRBY)
java
redisTemplate.opsForHash().increment("cart:1001", "item:2001", 1);
获取所有字段和值(HGETALL)
java
Map<Object, Object> entries = redisTemplate.opsForHash().entries("user:1001");
删除字段(HDEL)
java
redisTemplate.opsForHash().delete("user:1001", "city");
4. 集合(Set)
Redis Set 是一个无序、元素唯一 的集合结构,类似于 Java 的 HashSet。
适用于需要:
保证元素不重复
快速判断某元素是否存在
随机获取元素
进行交集、并集、差集计算
(1)Set 的典型应用场景
抽奖 / 随机选人
需要随机从一个集合中抽取成员:
SADD:加入抽奖池
SPOP:随机抽取并移除(适合不可重复中奖)
SRANDMEMBER:随机获取但不移除
场景示例:
抽奖系统随机选中奖人
随机推荐
点赞 / 收藏 / 点踩等去重场景
Set 的"元素唯一"特性非常适合这种场景:
| 操作 | Redis 命令 |
|---|---|
| 点赞 | SADD article:123 likes user1 |
| 取消点赞 | SREM article:123 likes user1 |
| 是否点过赞 | SISMEMBER article:123 likes user1 |
| 点赞人数 | SCARD article:123 likes |
| 所有点赞用户 | SMEMBERS article:123 likes |
共同关注、共同爱好(交集)
例如两个用户共同关注列表:
java
SINTER follows:userA follows:userB
用户标签系统(并集、差集)
并集:两个用户的所有爱好
差集:A 关注但 B 没关注的
这些计算在内存中非常快,非常适合实时推荐系统。
(2)Set 的基本操作
添加元素
java
SADD key member1 [member2...]
获取所有元素
java
SMEMBERS key
是否存在(检查成员)
java
SISMEMBER key member
随机弹出一个元素
java
SPOP key
删除成员
java
SREM key member1 [member2...]
(3)集合运算(Set 的强大之处)
交集(共同元素)
java
SINTER key1 [key2...]
并集(所有不同元素)
java
SUNION key1 [key2...]
差集(A 有 B 没有)
java
SDIFF key1 [key2...]
(4)Set 操作汇总
| 操作分类 | 命令 | 功能说明 |
|---|---|---|
| 添加 | SADD key m1... | 添加成员 |
| 删除 | SREM key m1... | 删除成员 |
| 随机弹出 | SPOP key | 移除并返回随机元素 |
| 随机返回但不移除 | SRANDMEMBER key [count] | 随机获取元素 |
| 查询所有成员 | SMEMBERS key | 获取集合全部成员 |
| 判断是否存在 | SISMEMBER key m | 检查是否是成员 |
| 长度 | SCARD key | 成员数量 |
| 移动 | SMOVE source dest member | 将元素从一个集合移动到另一个集合 |
| 交集 | SINTER k1 k2... | 返回交集 |
| 并集 | SUNION k1 k2... | 返回并集 |
| 差集 | SDIFF k1 k2... | 返回差集 |
| 交集存储 | SINTERSTORE dest k1 k2... | 保存交集到 dest |
| 并集存储 | SUNIONSTORE dest k1 k2... | 保存并集到 dest |
| 差集存储 | SDIFFSTORE dest k1 k2... | 保存差集到 dest |
| 遍历 | SSCAN key cursor MATCH p COUNT n | 分批遍历集合 |
(5)Set 结构的常见注意点
Set 是无序的
不要依赖元素顺序。
适合存放小而多的元素
如:用户ID、标签、商品ID。
大集合建议用 SSCAN 避免一次性遍历造成阻塞
适合大并发访问,因为 Set 的写入速度非常快
(6)Java 中操作 Redis Set(Spring Data Redis 示例)
注入:
java
@Autowired
private StringRedisTemplate redisTemplate;
添加元素(SADD)
java
redisTemplate.opsForSet().add("likes:article:1001", "user1", "user2");
获取所有成员(SMEMBERS)
java
Set<String> users = redisTemplate.opsForSet().members("likes:article:1001");
判断是否存在(SISMEMBER)
java
boolean liked = redisTemplate.opsForSet().isMember("likes:article:1001", "user1");
删除成员(SREM)
java
redisTemplate.opsForSet().remove("likes:article:1001", "user1");
随机弹出成员(SPOP)
java
String randomUser = redisTemplate.opsForSet().pop("onlineUsers");
交集/并集/差集
java
Set<String> common = redisTemplate.opsForSet().intersect("follow:a", "follow:b");
Set<String> union = redisTemplate.opsForSet().union("follow:a", "follow:b");
Set<String> diff = redisTemplate.opsForSet().difference("setA", "setB");
5. 有序集合(Sorted Set)
Redis Sorted Set(简称 ZSet)是一种 元素唯一、有序、可按 score 排序 的集合结构。
每个成员都会绑定一个 double 类型的 score(权重),Redis 会根据 score 自动排序。
非常适合做排行榜、热度榜、权重衰减系统等。
(1)应用场景
排行榜系统
Sorted Set 最典型的应用场景:
微信步数排行榜
游戏积分排行
直播送礼物排行榜
话题热度榜(score = 热度)
常用命令:
ZINCRBY:每发生一次操作,就让分值 +1
ZREVRANGE:获取从大到小的 Top N
ZRANK / ZREVRANK:获取用户的当前排名
基于权重的排序系统
例如:
按热度排序文章
按活跃度排序用户
根据时间衰减进行排序(类似知乎、微博热度)
Sorted Set 能自动保持数据有序,非常高效。
定时任务 / 延迟队列(高级用法)
score 可以用来表示"执行时间戳",实现延迟队列。
java
ZADD delay_queue 1700000123 "order123"
组合多天的数据(并集/交集)
java
ZUNIONSTORE 7daysHot 7 day1 day2 ... day7
(2)Sorted Set 基本操作
添加或更新分数(ZADD)
java
ZADD key score1 member1 [score2 member2 ...]
获取元素数量(ZCARD)
java
ZCARD key
移除元素(ZREM)
java
ZREM key member
查看某成员的分数(ZSCORE)
java
ZSCORE key member
按 score 从低到高获取成员(ZRANGE)
java
ZRANGE key start stop [WITHSCORES]
按 score 从高到低获取成员(ZREVRANGE)
java
ZREVRANGE key start stop [WITHSCORES]
(3)获取指定成员的排名
score 从小到大的排名:ZRANK
java
ZRANK key member
score 从大到小的排名:ZREVRANK
java
ZREVRANK key member
常用于排行榜中查看用户排名
(4)交集运算(ZINTERSTORE)
用于获取多个有序集合的 共同成员,并根据 score 进行聚合。
java
ZINTERSTORE destination numkeys key1 key2 ...
用途示例:
多天累计排行榜(必须每天都出现)
交叉活跃用户榜
(5)并集运算(ZUNIONSTORE)
将多个有序集合按 score 合并。
java
ZUNIONSTORE destination numkeys key1 key2 ...
常用于:
周排行榜(7 天热度求和)
多类型权重的总评分排行(例如活跃度 + 点击数 + 关注数)
(6)差集(ZDIFF)
java
ZDIFF numkeys key [key...]
将第一个集合的元素减去其他集合(按 score 计算)。
使用较少,但适用于:
提取 A 排行榜中排除掉 B 中出现的人
找出"今天未活跃但昨天活跃的用户"
(7)Redis Sorted Set(有序集合)Java 示例(Spring Data Redis)
注入:
java
@Autowired
private StringRedisTemplate redisTemplate;
向 Sorted Set 添加元素(ZADD)
java
@Autowired
private StringRedisTemplate redisTemplate;
public void addToZSet() {
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.add("rank:gift", "userA", 10.0);
zset.add("rank:gift", "userB", 20.0);
zset.add("rank:gift", "userC", 15.0);
}
自增分数(ZINCRBY)------排行榜经典用法
java
public void incrementScore(String userId) {
redisTemplate.opsForZSet().incrementScore("rank:gift", userId, 1);
}
获取前 N 名(ZREVRANGE,从大到小)
java
public Set<String> getTopN(int n) {
return redisTemplate.opsForZSet().reverseRange("rank:gift", 0, n - 1);
}
带上分数:
java
public Set<ZSetOperations.TypedTuple<String>> getTopNWithScore(int n) {
return redisTemplate.opsForZSet().reverseRangeWithScores("rank:gift", 0, n - 1);
}
获取指定成员排名(ZRANK / ZREVRANK)
默认 ZRANK 是从 低到高排序
排行榜一般用 ZREVRANK(高到低)
java
public Long getUserRank(String userId) {
return redisTemplate.opsForZSet().reverseRank("rank:gift", userId);
}
删除成员(ZREM)
java
public void removeMember(String userId) {
redisTemplate.opsForZSet().remove("rank:gift", userId);
}
获取成员的分数(ZSCORE)
java
public Double getUserScore(String userId) {
return redisTemplate.opsForZSet().score("rank:gift", userId);
}
获取指定分数区间内成员(ZRANGEBYSCORE)
例如获取步数 1 万--2 万之间的用户:
java
public Set<String> getByScoreRange(double min, double max) {
return redisTemplate.opsForZSet().rangeByScore("rank:step", min, max);
}
交集(ZINTERSTORE)
Spring Data Redis 以 ZSetOperations 的 intersectAndStore 实现:
java
public void zsetIntersect() {
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
// 求交集并存储到 newKey
zset.intersectAndStore("day1:rank", List.of("day2:rank", "day3:rank"), "rank:intersect");
}
并集(ZUNIONSTORE)
java
public void zsetUnion() {
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.unionAndStore("day1:rank", List.of("day2:rank", "day3:rank"), "rank:union");
}
差集(ZDIFF)---Spring 6.0+
java
public void zsetDiff() {
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.differenceAndStore("rank:A", List.of("rank:B"), "rank:diff");
}
常见排行榜完整示例
例如直播礼物排行榜:
java
public class RankService {
private static final String KEY = "rank:gift";
@Autowired
private StringRedisTemplate redisTemplate;
// 礼物增加积分
public void addGift(String userId, int score) {
redisTemplate.opsForZSet().incrementScore(KEY, userId, score);
}
// 获取前10名
public List<Map<String, Object>> getTop10() {
Set<ZSetOperations.TypedTuple<String>> result =
redisTemplate.opsForZSet().reverseRangeWithScores(KEY, 0, 9);
if (result == null) return List.of();
List<Map<String, Object>> list = new ArrayList<>();
for (ZSetOperations.TypedTuple<String> t : result) {
Map<String, Object> m = new HashMap<>();
m.put("userId", t.getValue());
m.put("score", t.getScore());
list.add(m);
}
return list;
}
}