忘忧的小站

  • 首页
  • 文章归档
  • 日志
  • 关于页面

  • 搜索
异常处理 AutoWrapper 入门 NoSql 数据库 sqlserver 1 分布式索引 索引 全文搜索 Lucene.Net GPS 音视频 过滤 AOP 时区 升级 ABP.Zero 数据备份 linux 阿里云盘 aliyunpan 面试题 Signalr S 汉字 css html 前端 拼音键盘 在线键盘 uniapp .Net Core XMLRPC Serilog LOKI Nlog 分布式日志 加密 总结 人生 Asp.Net Core Swagger Web Element-plus Quasar 匹配 JavaScript 正则 .Net 后台 架构师 Redis EF CORE MySQL 自考 英语 集群 Jenkins CI/DI 内网穿透 代理 ABP 学习 后端 软考

Redis入门:玩转Redis五大核心数据结构

发表于 2025-03-20 | 分类于 NoSql | 0 | 阅读次数 20148

玩转Redis五大核心数据结构:从计数器到排行榜

在上一篇中,我们知道了Redis为什么这么快,以及如何搭建环境。但Redis真正的威力,来自于它丰富的数据结构。如果说简单的键值对是Redis的"骨架",那么这些数据结构就是它的"肌肉",让Redis能够优雅地解决各种复杂的业务场景。

Redis最吸引人的地方在于,它不仅仅是一个简单的键值存储,而是一个数据结构服务器。今天,我们将深入探索Redis的五大核心数据结构:String(字符串)、Hash(哈希)、List(列表)、Set(集合) 和 Sorted Set(有序集合)。

一、String(字符串):不止是文本

String是Redis最基本的数据类型,一个Key对应一个Value。但别被它的名字骗了——它不仅可以存储文本,还可以存储数字(整数或浮点数)甚至是二进制数据(如图片或序列化对象)。

核心命令与特性

# 1. 基础设置与获取
127.0.0.1:6379> SET username "redis_learner"
OK
127.0.0.1:6379> GET username
"redis_learner"

# 2. 数字操作 - Redis知道你是数字时会允许数学运算
127.0.0.1:6379> SET page_views 100
OK
127.0.0.1:6379> INCR page_views        # 增加1
(integer) 101
127.0.0.1:6379> INCRBY page_views 10   # 增加指定数值
(integer) 111
127.0.0.1:6379> DECR page_views        # 减少1
(integer) 110

# 3. 批量操作 - 提升效率
127.0.0.1:6379> MSET user:1001:name "Alice" user:1001:age 25 user:1001:city "Beijing"
OK
127.0.0.1:6379> MGET user:1001:name user:1001:age user:1001:city
1) "Alice"
2) "25"
3) "Beijing"

# 4. 条件设置 - 实现分布式锁的基础
127.0.0.1:6379> SETNX lock:order_123 "client_1"  # 只有当key不存在时才设置
(integer) 1  # 设置成功
127.0.0.1:6379> SETNX lock:order_123 "client_2"
(integer) 0  # 设置失败,因为key已存在

实战应用场景

  1. 缓存:存储序列化的用户信息、页面片段等
  2. 计数器:文章阅读量、用户点赞数、网站访问量
  3. 分布式锁:通过SETNX实现简单的互斥锁
  4. 会话存储:存储用户Session数据

性能提示:对于多个相关的键值对,使用MSET/MGET比多次SET/GET更高效,因为它减少了网络往返次数。


二、Hash(哈希表):存储对象的最佳选择

如果你需要存储一个对象(如用户信息、商品信息),Hash是你的最佳选择。它类似于编程语言中的字典或Map,适合存储字段-值对的集合。

为什么用Hash而不用多个String?

假设我们要存储用户信息,有两种方案:

  • 方案一(多个String):

    SET user:1001:name "Bob"
    SET user:1001:age 30
    SET user:1001:email "bob@example.com"
    
  • 方案二(一个Hash):

    HSET user:1001 name "Bob" age 30 email "bob@example.com"
    

Hash的优势:

  • 内存效率更高:Redis对Hash有特殊优化,特别是字段较少时
  • 原子性操作:可以一次性获取或修改整个对象
  • 更少的Key:避免键空间膨胀,管理更方便

核心命令详解

# 1. 设置和获取字段
127.0.0.1:6379> HSET product:1001 name "iPhone 15" price 5999 stock 100
(integer) 3  # 返回设置的字段数量

127.0.0.1:6379> HGET product:1001 name
"iPhone 15"

127.0.0.1:6379> HGETALL product:1001  # 获取所有字段和值
1) "name"
2) "iPhone 15"
3) "price"
4) "5999"
5) "stock"
6) "100"

# 2. 批量操作
127.0.0.1:6379> HMGET product:1001 name price  # 获取多个字段
1) "iPhone 15"
2) "5999"

# 3. 数字运算
127.0.0.1:6379> HINCRBY product:1001 stock -1  # 库存减1(售出一件)
(integer) 99

# 4. 检查字段
127.0.0.1:6379> HEXISTS product:1001 name
(integer) 1
127.0.0.1:6379> HKEYS product:1001  # 获取所有字段名
1) "name"
2) "price"
3) "stock"

实战应用场景

  1. 用户信息存储:将用户的所有属性存储在一个Hash中
  2. 商品信息:商品的名称、价格、库存等信息
  3. 配置信息:系统的各种配置参数
  4. 购物车:用户ID作为Key,商品ID和数量作为字段-值对

三、List(列表):实现简单的消息队列

List是一个按插入顺序排序的字符串元素集合,你可以在列表的头部(左边)或尾部(右边)添加元素。Redis的List底层实现是双向链表,这意味着在头部和尾部添加元素的速度极快,但通过索引访问中间元素相对较慢。

核心命令详解

# 1. 从两端添加元素
127.0.0.1:6379> LPUSH tasks "send_email"      # 从左边添加
(integer) 1
127.0.0.1:6379> LPUSH tasks "process_image"
(integer) 2
127.0.0.1:6379> RPUSH tasks "generate_report" # 从右边添加
(integer) 3

# 此时列表:["process_image", "send_email", "generate_report"]
#               头(左)   <--------->   尾(右)

# 2. 从两端弹出元素
127.0.0.1:6379> LPOP tasks  # 从左边弹出
"process_image"
127.0.0.1:6379> RPOP tasks  # 从右边弹出
"generate_report"

# 3. 查看列表范围(不会弹出元素)
127.0.0.1:6379> LRANGE tasks 0 -1  # 查看所有元素,0表示开始,-1表示末尾
1) "send_email"

# 4. 阻塞操作 - 消息队列的核心
# 从一个空列表中阻塞地等待元素,最多等待10秒
127.0.0.1:6379> BLPOP message_queue 10
(nil)  # 10秒内没有元素,返回nil
# 在另一个客户端执行:LPUSH message_queue "new_message"
# 此时BLPOP会立即返回:"message_queue" "new_message"

实战应用场景

  1. 消息队列:使用LPUSH添加任务,BRPOP阻塞获取任务
  2. 最新消息列表:使用LPUSH添加新消息,LRANGE 0 9获取最新的10条
  3. 历史记录:用户浏览历史、搜索历史
  4. 文章评论列表:文章的评论按时间顺序排列

重要特性:List的阻塞操作(BLPOP, BRPOP)使其成为实现简单消息队列的理想选择,消费者可以在队列为空时等待,而不需要轮询。


四、Set(集合):无序与唯一性的力量

Set是String类型的无序集合,它最大的特点是:元素唯一且无序。底层通过哈希表实现,添加、删除、查找的时间复杂度都是O(1)。

核心命令与集合运算

# 1. 基本操作
127.0.0.1:6379> SADD tags "java" "python" "redis" "java"
(integer) 3  # "java"重复,只添加了3个元素

127.0.0.1:6379> SMEMBERS tags  # 获取所有元素(顺序不确定)
1) "redis"
2) "python"
3) "java"

127.0.0.1:6379> SISMEMBER tags "python"  # 检查元素是否存在
(integer) 1

# 2. 集合运算 - Set的精华所在
127.0.0.1:6379> SADD user:1001:follows "user:1002" "user:1003" "user:1004"
(integer) 3
127.0.0.1:6379> SADD user:1002:follows "user:1003" "user:1005"
(integer) 2

# 交集 - 共同关注
127.0.0.1:6379> SINTER user:1001:follows user:1002:follows
1) "user:1003"

# 并集 - 所有的关注
127.0.0.1:6379> SUNION user:1001:follows user:1002:follows
1) "user:1002"
2) "user:1003"
3) "user:1004"
4) "user:1005"

# 差集 - A有但B没有的
127.0.0.1:6379> SDIFF user:1001:follows user:1002:follows
1) "user:1002"
2) "user:1004"

# 3. 随机元素 - 抽奖功能
127.0.0.1:6379> SADD lottery_users "user1" "user2" "user3" "user4" "user5"
(integer) 5
127.0.0.1:6379> SRANDMEMBER lottery_users 2  # 随机返回2个元素,不删除
1) "user3"
2) "user5"
127.0.0.1:6379> SPOP lottery_users 1         # 随机弹出1个元素并删除
1) "user2"

实战应用场景

  1. 标签系统:给文章、用户打标签
  2. 社交关系:共同好友、共同关注
  3. 数据去重:防止重复提交、重复处理
  4. 随机抽奖:从参与用户中随机抽取中奖者
  5. 黑白名单:IP白名单、用户黑名单

五、Sorted Set(有序集合):排行榜的灵魂

Sorted Set是Set的增强版,它在保证元素唯一性的基础上,为每个元素关联了一个分数(Score),元素按照分数进行排序。这是Redis中最复杂但也最强大的数据结构之一。

底层实现:跳表(Skip List)

Sorted Set使用跳表(一种类似链表但有多级索引的数据结构)实现,可以在O(logN)时间内完成插入、删除和按分数范围查找,兼具了链表和二分查找的优点。

核心命令详解

# 1. 添加元素(带分数)
127.0.0.1:6379> ZADD leaderboard 2500 "Alice" 1800 "Bob" 3200 "Charlie" 1500 "David"
(integer) 4

# 2. 按分数范围查询(升序)
127.0.0.1:6379> ZRANGE leaderboard 0 -1 WITHSCORES  # 获取所有元素(分数从低到高)
1) "David"
2) "1500"
3) "Bob"
4) "1800"
5) "Alice"
6) "2500"
7) "Charlie"
8) "3200"

# 3. 按分数范围查询(降序 - 排行榜常用)
127.0.0.1:6379> ZREVRANGE leaderboard 0 2 WITHSCORES  # 获取前三名
1) "Charlie"
2) "3200"
3) "Alice"
4) "2500"
5) "Bob"
6) "1800"

# 4. 按分数范围查询
127.0.0.1:6379> ZRANGEBYSCORE leaderboard 2000 3000 WITHSCORES  # 分数在2000-3000之间的玩家
1) "Alice"
2) "2500"

# 5. 获取排名和分数
127.0.0.1:6379> ZRANK leaderboard "Alice"    # 获取升序排名(从0开始)
(integer) 2
127.0.0.1:6379> ZREVRANK leaderboard "Alice" # 获取降序排名(排行榜名次)
(integer) 1
127.0.0.1:6379> ZSCORE leaderboard "Alice"   # 获取分数
"2500"

# 6. 分数操作
127.0.0.1:6379> ZINCRBY leaderboard 500 "Alice"  # Alice增加500分
"3000"

实战应用场景

  1. 排行榜:游戏积分榜、销量排行榜、热搜榜
  2. 带权重的队列:优先级任务调度
  3. 时间轴:按时间排序的消息列表(时间戳作为Score)
  4. 范围查询:查找分数/价格在某个区间的数据

性能提示:Sorted Set的范围查询(ZRANGEBYSCORE)非常高效,特别适合需要按范围检索数据的场景。


数据结构选择指南

面对具体业务场景时,如何选择合适的数据结构?这里有一个快速参考:

需求场景 推荐数据结构 理由
缓存简单数据 String 简单直接,性能最佳
存储对象 Hash 内存效率高,支持部分更新
消息队列 List 支持阻塞操作,顺序保证
最新N条记录 List LPUSH + LTRIM 实现固定长度列表
去重、标签、共同好友 Set 天然去重,支持集合运算
排行榜、范围查询 Sorted Set 按分数排序,范围查询高效
时间序列数据 Sorted Set 时间戳作为Score,天然排序

总结

通过本篇的学习,我们已经掌握了Redis五大核心数据结构的特性和应用:

  • String:简单但强大,支持数字操作
  • Hash:存储对象的理想选择,内存效率高
  • List:顺序数据结构,适合消息队列和时间线
  • Set:无序唯一集合,强大的集合运算能力
  • Sorted Set:有序唯一集合,排行榜和范围查询的利器

Redis的哲学是:将复杂的数据操作下推到存储层,而不是在应用层处理。理解每种数据结构的特性和适用场景,能够让你在设计系统时做出更优雅、更高效的决策。

现在,当你需要实现计数器时,不会选择在应用层读取-计算-保存,而是直接使用INCR命令;当你需要排行榜时,不会在数据库中排序,而是使用Sorted Set。这就是Redis数据结构的威力所在!


动手练习:尝试用你学到的数据结构实现以下功能:

  1. 使用String实现一个文章阅读量计数器
  2. 使用Hash存储你的个人简历信息
  3. 使用List实现一个简单的待办事项列表
  4. 使用Set找出你和你朋友共同喜欢的电影
  5. 使用Sorted Set创建一个游戏分数排行榜

欢迎在评论区分享你的实现代码和心得体会!

  • 本文作者: 忘忧
  • 本文链接: /archives/2943
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Redis # NoSql
Redis入门:Redis核心概念与快速入门
Redis入门:Redis的持久化与高可用
  • 文章目录
  • 站点概览
忘忧

忘忧

君子藏器于身,待时而动,何不利之有

59 日志
9 分类
67 标签
RSS
Github E-mail StackOverflow
Creative Commons
0%
© 2025 忘忧
由 Halo 强力驱动