Redis:数据结构与基础操作(String、List、Hash、Set、Sorted Set)

一、数据结构与操作

(一)基本数据结构

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;
    }
}
相关推荐
拼好饭和她皆失2 小时前
高效算法的秘诀:滑动窗口(尺取法)全解析
数据结构·算法·滑动窗口·尺取法
alien爱吃蛋挞2 小时前
【JavaEE】Spring Boot日志
java·数据库·spring boot
zjeweler2 小时前
redis tools gui ---Redis图形化漏洞利用工具
数据库·redis·web安全·缓存
浮游本尊2 小时前
Java学习第31天 - 高级主题与深度实战
java
BD_Marathon2 小时前
【JavaWeb】IDEA关联Tomcat并使用Tomcat运行JavaWeb项目
java·tomcat·intellij-idea
断剑zou天涯2 小时前
【算法笔记】二叉树的Morris遍历
数据结构·笔记·算法
柒.梧.2 小时前
手写Tomcat的实现代码分享
java·tomcat
小白程序员成长日记2 小时前
2025.12.11 力扣每日一题
数据结构·算法·leetcode
y1y1z2 小时前
Spring MVC教程
java·spring·mvc