Redis 7 核心数据结构指南:从基础到实战应用

一、Redis数据结构概览

Redis不仅仅是一个简单的键值存储,它提供了丰富的数据结构,使其能够适应各种复杂的应用场景。下图展示了Redis数据结构的全貌:

复制代码
基础数据结构(核心):
├── String(字符串)
├── Hash(哈希)
├── List(列表)
├── Set(集合)
└── ZSet(有序集合)

扩展数据结构:
├── Bitmap(位图)
├── HyperLogLog(基数统计)
├── Geo(地理空间)
└── Stream(流)

高级模块(需额外安装):
├── JSON
├── Search and Query
├── Time Series
└── Bloom Filter等

二、永远的神:Redis帮助系统

在深入学习前,先掌握Redis的内置帮助系统:

复制代码
# 进入Redis命令行
redis-cli

# 查看所有命令分组
help @<group>

# 查看特定命令帮助
help <command>

# 查看可能的话题
help <tab>

三、核心数据结构详解

3.1 String(字符串)

基础命令
复制代码
# 基本操作
SET key value                    # 设置键值
GET key                          # 获取值
DEL key                          # 删除键
EXPIRE key seconds              # 设置过期时间

# 批量操作
MSET k1 v1 k2 v2 k3 v3          # 批量设置
MGET k1 k2 k3                   # 批量获取

# 原子操作
SETNX key value                 # 不存在才设置(分布式锁基础)
INCR key                        # 原子递增
DECR key                        # 原子递减
INCRBY key 5                    # 原子加5
DECRBY key 3                    # 原子减3
应用场景

1. 对象缓存

复制代码
# 方式1:JSON格式存储(简单但不易部分更新)
SET user:1 '{"name":"roy","balance":1888}'

# 方式2:拆分存储(推荐,可部分更新)
MSET user:1:name roy user:1:balance 1888
MGET user:1:name user:1:balance

2. 分布式锁

复制代码
# 获取锁(NX:不存在才设置,EX:10秒过期)
SET lock:order:10001 true EX 10 NX

# 执行业务逻辑...

# 释放锁(Lua脚本保证原子性)
DEL lock:order:10001

3. 计数器

复制代码
# 文章阅读量统计
INCR article:10001:views
GET article:10001:views

3.2 Hash(哈希表)

基础命令
复制代码
# 单个操作
HSET user:1 name roy age 25      # 设置字段
HGET user:1 name                 # 获取字段
HSETNX user:1 email test@qq.com # 字段不存在才设置

# 批量操作
HMSET user:1 name roy balance 1888
HMGET user:1 name balance

# 其他操作
HDEL user:1 age                  # 删除字段
HLEN user:1                     # 字段数量
HGETALL user:1                  # 获取所有字段
HINCRBY user:1 balance 100      # 字段值递增
应用场景

1. 购物车实现

复制代码
# 用户ID为key,商品ID为field,数量为value
HSET cart:1001 10088 1          # 添加商品
HINCRBY cart:1001 10088 1       # 增加数量
HLEN cart:1001                  # 商品总数
HDEL cart:1001 10088            # 删除商品
HGETALL cart:1001               # 获取所有商品

2. 用户信息存储

复制代码
# 比String更节省空间,支持部分更新
HSET user:1001 name "张三" age 30 city "北京"
HGET user:1001 name city        # 只获取需要的字段
优缺点
  • ✅ 优点:

    • 同类数据归类存储,易于管理

    • 内存和CPU消耗比String更小

    • 节省存储空间

  • ❌ 缺点:

    • 过期时间只能设置在key上,不能针对field

    • 集群架构下不适合存储大hash


3.3 List(列表)

基础命令
复制代码
# 两端操作
LPUSH list1 a b c                # 左插 → [c, b, a]
RPUSH list1 d e f                # 右插 → [c, b, a, d, e, f]
LPOP list1                       # 左弹 → c
RPOP list1                       # 右弹 → f

# 范围查询
LRANGE list1 0 -1                # 获取所有元素
LRANGE list1 0 2                 # 获取前3个

# 阻塞操作
BLPOP list1 10                   # 左弹,等待10秒
BRPOP list1 0                    # 右弹,无限等待
数据结构实现
  • 栈(Stack)LPUSH + LPOP(先进后出)

  • 队列(Queue)LPUSH + RPOP(先进先出)

  • 阻塞队列LPUSH + BRPOP

应用场景
  1. 消息队列:简单任务队列

  2. 最新列表:朋友圈动态、新闻推送

  3. 历史记录:用户浏览记录

注意事项
  • List最多可存储 2³² - 1 个元素(约42亿)

  • 底层是双向链表,两端操作O(1),中间操作O(n)

  • 注意大Key问题,单个List不宜过大


3.4 Set(集合)

基础命令
复制代码
# 基本操作
SADD tags java redis spring      # 添加元素
SREM tags java                   # 删除元素
SMEMBERS tags                    # 获取所有元素
SCARD tags                       # 元素个数
SISMEMBER tags redis             # 是否包含

# 随机操作
SRANDMEMBER tags 2               # 随机取2个(不删除)
SPOP tags 1                      # 随机取1个(删除)

# 集合运算
SINTER set1 set2                 # 交集
SUNION set1 set2                 # 并集
SDIFF set1 set2                  # 差集(set1 - set2)
应用场景

1. 抽奖系统

复制代码
SADD lottery:20240617 user1 user2 user3  # 参与抽奖
SMEMBERS lottery:20240617                # 查看所有参与者
SRANDMEMBER lottery:20240617 3           # 抽取3名(不删除)
SPOP lottery:20240617 3                  # 抽取3名(删除)

2. 点赞/收藏系统

复制代码
SADD like:article:1001 user:10001       # 点赞
SREM like:article:1001 user:10001       # 取消点赞
SISMEMBER like:article:1001 user:10001  # 是否点赞
SCARD like:article:1001                 # 点赞总数
SMEMBERS like:article:1001              # 点赞用户列表

3. 社交关系

复制代码
# 共同关注
SINTER user:1001:follow user:1002:follow

# 可能认识的人(差集)
SDIFF user:1001:follow user:1002:follow

3.5 ZSet(有序集合)

基础命令
复制代码
# 基本操作
ZADD rank 95 "Alice" 87 "Bob" 92 "Charlie"  # 添加带分数元素
ZSCORE rank "Alice"                         # 获取分数
ZINCRBY rank 5 "Bob"                        # 增加分数
ZREM rank "Charlie"                         # 删除元素
ZCARD rank                                  # 元素个数

# 范围查询
ZRANGE rank 0 -1 WITHSCORES                 # 升序获取全部
ZREVRANGE rank 0 2 WITHSCORES               # 降序获取前3
ZRANGEBYSCORE rank 90 100                   # 获取90-100分的

# 集合运算
ZUNIONSTORE weekly 7 day1 day2 ... day7     # 周榜合并
应用场景

1. 排行榜系统

复制代码
# 日榜
ZINCRBY hotNews:20240617 1 "守护香港"
ZREVRANGE hotNews:20240617 0 9 WITHSCORES

# 周榜计算
ZUNIONSTORE hotNews:week 7 hotNews:0611 ... hotNews:0617
ZREVRANGE hotNews:week 0 9 WITHSCORES

2. 延迟队列

复制代码
# 添加任务(执行时间戳作为分数)
ZADD delay:queue 1741234567 "task:1"
ZADD delay:queue 1741235567 "task:2"

# 获取到期的任务
ZRANGEBYSCORE delay:queue 0 1741234567

3.6 Bitmap(位图)

基础命令
复制代码
# 位操作
SETBIT sign:user:1001 0 1        # 第0位设为1
GETBIT sign:user:1001 0          # 获取第0位
BITCOUNT sign:user:1001          # 统计1的个数
BITPOS sign:user:1001 1          # 第一个1的位置

# 位运算
BITOP AND dest key1 key2         # 与运算
BITOP OR dest key1 key2          # 或运算
BITOP XOR dest key1 key2         # 异或运算
应用场景

1. 用户签到

复制代码
# 用户1001每月签到(偏移量代表日期)
SETBIT sign:1001:202406 17 1     # 6月17日签到
BITCOUNT sign:1001:202406        # 本月签到天数
GETBIT sign:1001:202406 17       # 检查某天是否签到

2. 用户活跃度统计

复制代码
# 每天用户活跃记录
SETBIT active:20240617 1001 1    # 用户1001活跃
BITCOUNT active:20240617         # 当天活跃用户数
BITOP AND daily_active active:0616 active:0617  # 连续活跃用户

3.7 HyperLogLog(基数统计)

基础命令
复制代码
# 添加与统计
PFADD uv:20240617 user1 user2 user1 user3
PFCOUNT uv:20240617              # 统计UV ≈ 3

# 合并统计
PFADD uv:day1 user1 user2
PFADD uv:day2 user2 user3
PFMERGE uv:total uv:day1 uv:day2
PFCOUNT uv:total                 # 统计总UV ≈ 3
特点与应用
  • 优点:极省内存,12KB可统计2⁶⁴个元素

  • 缺点:有约0.81%的标准误差

  • 场景:UV统计、独立IP计数


3.8 Geo(地理空间)

基础命令
复制代码
# 添加位置
GEOADD city:changsha 113.017489 28.200454 "火车站"

# 查询位置
GEOPOS city:changsha "火车站" "橘子洲"

# 计算距离
GEODIST city:changsha "火车站" "橘子洲" KM

# 附近搜索
GEORADIUS city:changsha 113.017489 28.200454 2 km WITHDIST COUNT 5

# 基于成员搜索
GEORADIUSBYMEMBER city:changsha "火车站" 2 km
应用场景
  • 附近的人/商家

  • 配送距离计算

  • 地理围栏


3.9 Stream(流)

基础命令
复制代码
# 生产消息
XADD mystream * name "order" amount "100.00"

# 消费消息
XREAD COUNT 2 STREAMS mystream 0

# 消费者组
XGROUP CREATE mystream order_group 0
XREADGROUP GROUP order_group consumer1 COUNT 1 STREAMS mystream >
应用场景
  • 消息队列(替代RabbitMQ/Kafka的轻量方案)

  • 事件溯源

  • 日志收集


四、SpringBoot集成Redis

4.1 添加依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

4.2 配置文件

复制代码
spring:
  data:
    redis:
      host: 192.168.65.214
      port: 6379
      password: 123qweasd
      database: 0
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0

4.3 RedisTemplate使用

复制代码
@Resource
private RedisTemplate<String, Object> redisTemplate;

// String操作
redisTemplate.opsForValue().set("key", "value");
String value = (String) redisTemplate.opsForValue().get("key");

// Hash操作
redisTemplate.opsForHash().put("user:1", "name", "roy");

// List操作
redisTemplate.opsForList().leftPush("list1", "item1");

// Set操作
redisTemplate.opsForSet().add("tags", "java", "redis");

// ZSet操作
redisTemplate.opsForZSet().add("rank", "Alice", 95.0);

// Geo操作
redisTemplate.opsForGeo().add("city", new Point(113.017489, 28.200454), "火车站");

// HyperLogLog操作
redisTemplate.opsForHyperLogLog().add("uv", "user1", "user2");

// Stream操作
Map<String, String> map = new HashMap<>();
map.put("name", "order");
redisTemplate.opsForStream().add("mystream", map);

// Bit操作
redisTemplate.opsForValue().setBit("bitmap", 0, true);

4.4 解决序列化问题

复制代码
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        
        // 使用String序列化Key
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        
        // 使用Jackson序列化Value
        Jackson2JsonRedisSerializer<Object> jsonSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

五、数据结构选择指南

数据结构 特点 适用场景 不适用场景
String 最简单,支持数值操作 缓存、计数器、分布式锁 存储复杂对象
Hash 字段独立,节省内存 购物车、用户属性、配置项 需要单独过期字段
List 有序,支持阻塞操作 消息队列、最新列表、历史记录 随机访问中间元素
Set 无序,去重,支持集合运算 标签、好友关系、抽奖 需要有序或重复元素
ZSet 有序,带权重 排行榜、延迟队列、优先级队列 不需要排序的场景
Bitmap 极省空间,位操作 签到、用户在线状态、布隆过滤器 非布尔值统计
HyperLogLog 极省内存,基数估计 UV统计、独立IP计数 需要精确计数
Geo 地理位置存储查询 附近的人、位置服务 非地理位置数据
Stream 消息队列,消费组 事件流、日志收集 简单KV存储

六、性能优化建议

6.1 内存优化

  1. 使用合适的数据结构:Hash存储对象比String更省内存

  2. 控制Key长度 :使用缩写,如u:1代替user:1

  3. 启用压缩 :在redis.conf中配置hash-max-ziplist-entries等参数

6.2 命令优化

  1. 批量操作 :使用MSETHMGET代替多次单操作

  2. 管道技术:减少网络往返

  3. 避免大Key:单个String不超过10KB,集合元素不超过5000

6.3 监控与维护

复制代码
# 查看内存使用
redis-cli info memory

# 查看慢查询
redis-cli slowlog get 10

# 大Key扫描
redis-cli --bigkeys

七、总结

Redis的强大之处在于其丰富的数据结构,每种结构都有其独特的应用场景:

  • 缓存和会话 → String/Hash

  • 社交功能 → Set/ZSet

  • 消息系统 → List/Stream

  • 统计计数 → Bitmap/HyperLogLog

  • 位置服务 → Geo

掌握这些数据结构及其应用场景,能够让你在系统设计中游刃有余。记住:没有最好的数据结构,只有最适合场景的数据结构

相关推荐
ALex_zry10 小时前
Redis Cluster 分布式缓存架构设计与实践
redis·分布式·缓存
Mr Xu_11 小时前
告别硬编码:前端项目中配置驱动的实战优化指南
前端·javascript·数据结构
czxyvX11 小时前
017-AVL树(C++实现)
开发语言·数据结构·c++
数智工坊12 小时前
【数据结构-队列】3.2 队列的顺序-链式实现-双端队列
数据结构
elseif12312 小时前
【C++】并查集&家谱树
开发语言·数据结构·c++·算法·图论
徐小夕@趣谈前端12 小时前
Web文档的“Office时刻“:jitword共建版2.0发布!让浏览器变成本地生产力
前端·数据结构·vue.js·算法·开源·编辑器·es6
Nebula_g13 小时前
线程进阶: 无人机自动防空平台开发教程(更新)
java·开发语言·数据结构·学习·算法·无人机
乔江seven13 小时前
【Flask 进阶】3 从同步到异步:基于 Redis 任务队列解决 API 高并发与长耗时任务阻塞
redis·python·flask
xuxie9914 小时前
day 23 树
数据结构
这周也會开心14 小时前
Redis与MySQL回写中的数据类型存储设计
数据库·redis·mysql