惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs
博客园 - 叶小钗
P
Privacy International News Feed
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
IT之家
IT之家
博客园 - 聂微东
Know Your Adversary
Know Your Adversary
Help Net Security
Help Net Security
罗磊的独立博客
I
Intezer
S
Schneier on Security
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
Y
Y Combinator Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

忘忧的小站

SqlServer高频面试题(持续更新251114) | 忘忧的小站 MySql入门:备份恢复与安全管理 | 忘忧的小站 MySql入门:高可用与架构设计 | 忘忧的小站 MySql入门:SQL编程与高级特性 | 忘忧的小站 MySql入门:性能优化与调优 | 忘忧的小站 MySql入门:MySQL数据类型与表设计 | 忘忧的小站 MySql入门:MySQL核心概念与架构解析 | 忘忧的小站 Redis配置文件及常用命令详解 | 忘忧的小站 Redis入门:总结与展望 | 忘忧的小站 Redis入门:进阶知识与运维管理 | 忘忧的小站 Redis入门:Redis在项目中的实战应用 | 忘忧的小站 Redis入门:Redis的持久化与高可用 | 忘忧的小站 Redis入门:Redis核心概念与快速入门 | 忘忧的小站 Lucene.Net 分布式索引实现方案 | 忘忧的小站 Lucene.Net 入门和简单使用 | 忘忧的小站 JavaScript授权Gps,音频,视频踩坑 | 忘忧的小站 .Net7 针对Utc时区转换问题中间件 | 忘忧的小站 Abp.Zero框架升级 | 忘忧的小站 mysql每日自动备份数据 | 忘忧的小站
Redis入门:玩转Redis五大核心数据结构 | 忘忧的小站
本文作者: 忘忧 · 2025-03-20 · via 忘忧的小站

发表于 | 分类于 NoSql | 0 | 阅读次数 20546

玩转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 许可协议。转载请注明出处!