Redis 数据结构全解析:类型、底层实现与实际应用场景

很多人第一次接触 Redis 时,都会把它简单理解成一个 Key-Value 缓存工具

但随着使用的深入就会发现,Redis 真正强大的地方,其实是 丰富的数据结构设计

Redis 不仅提供了多种数据结构,而且针对每一种结构都设计了不同的底层实现,使得很多业务场景可以非常高效地完成。

今天我们主要从三个角度整理 Redis 的核心数据结构:

  • Redis 对外提供的数据结构类型

  • 每种数据结构的底层实现方式

  • 实际开发中常见的应用场景

最后也会总结一些面试中经常会被问到的问题,算是对 Redis 数据结构的一次系统复习。


一、为什么 Redis 会设计这么多数据结构?

刚开始学习 Redis 的时候,我其实一直有个疑问:

Redis 不就是一个缓存吗?

直接存 Key-Value 不就够了吗?

后来在看一些实际业务系统的时候才慢慢发现,很多需求其实并不是简单的 KV 存储。

比如这些场景:

  • 记录文章点赞用户

  • 实现排行榜

  • 存储用户对象信息

  • 实现任务队列

  • 统计访问次数

  • 做用户标签系统

如果全部只用字符串来实现,其实会非常麻烦。

Redis 的思路其实很简单:

把常见的数据结构直接内置进去,让开发者可以开箱即用。

所以 Redis 更像是一个:

内存中的高性能数据结构服务器。

很多业务逻辑,如果用数据库实现会比较复杂,但用 Redis 可能几条命令就可以完成。


二、Redis 的主要数据结构类型

Redis 最核心的五种数据结构:

类型 描述
String 字符串
List 列表
Hash 哈希
Set 集合
Sorted Set(ZSet) 有序集合

除此之外 Redis 还提供了一些扩展结构:

  • Bitmap

  • HyperLogLog

  • Geo

  • Stream

不过在绝大多数业务系统中,前五种已经覆盖了绝大多数需求

下面我们逐个来看。


三、String(字符串)

String 是 Redis 中 最基础、使用频率最高 的数据结构。

很多时候我们说 Redis 是 Key-Value 存储,其实大多数 Key 的 Value 就是 String。

Redis 的 String 最大可以存 512MB 的数据。

它不仅可以存字符串,还可以存:

  • 数字

  • JSON

  • 图片二进制数据

  • 序列化对象

常见操作:

复制代码
SET key value
GET key
INCR key
DECR key

1.String 的底层实现

Redis 的 String 底层使用的是 SDS(Simple Dynamic String)

SDS 相比传统 C 语言字符串做了很多优化。

(1)O(1) 获取字符串长度

在 C 语言中,如果想获取字符串长度,需要遍历整个字符串:

复制代码
strlen()

时间复杂度是 O(N)。

而 SDS 在结构体中会直接记录字符串长度,因此获取长度只需要:O(1)


(2)自动扩容避免缓冲区溢出

在传统字符串操作中,如果拼接字符串时空间不够,很容易发生 buffer overflow

SDS 在修改字符串时会自动扩容,因此不会出现这种问题。


(3)支持二进制安全

Redis 的 String 可以存储:

  • 图片

  • protobuf 数据

  • Java 序列化对象

因为它并不会像 C 字符串那样依赖 \0 作为结束符。


2.String 常见使用场景

(1)计数器

String 最常见的用途之一其实就是 计数器

比如一些非常常见的数据统计:

  • 文章阅读量

  • 帖子点赞数

  • 商品库存

  • 网站访问次数

如果这些操作全部通过数据库实现,每次都需要:

复制代码
select
update

在高并发系统中,这种操作会给数据库带来很大压力。

而 Redis 可以直接这样做:

复制代码
INCR article:1001:view

每访问一次文章,就让这个 key 自增一次。

Redis 的操作是 原子性的,所以即使在高并发情况下也不会出现数据问题。


(2)缓存对象

另一个非常常见的用途是 缓存对象

例如用户信息。

传统流程可能是:

复制代码
数据库 -> 查询用户信息

如果接口访问频率很高,每次都查数据库其实挺浪费的。

很多系统会直接把用户信息缓存到 Redis:

复制代码
user:1001 -> JSON

访问流程变成:

复制代码
先查 Redis -> 没有再查数据库

这样数据库压力会小很多。


(3)分布式锁

Redis String 也经常被用来实现 分布式锁

经典写法是:

复制代码
SET lock_key value NX PX 30000

含义:

  • NX:只有 key 不存在时才设置

  • PX:设置过期时间

这个模式在很多分布式系统中都被广泛使用。


3.面试常见问题

Redis String 最大可以存多大?

512MB。


Redis String 底层结构是什么?

SDS(简单动态字符串)。


SDS 相比 C 字符串有哪些优势?

  • O(1) 获取长度

  • 防止缓冲区溢出

  • 支持二进制安全


四、List(列表)

List 可以理解为一个 有序字符串列表

它类似一个 双端队列,支持从两端插入和删除。

常见命令:

复制代码
LPUSH
RPUSH
LPOP
RPOP
LRANGE

1.List 的底层实现

在较新的 Redis 版本中,List 的底层结构是:

quicklist

quicklist 可以理解为:

双向链表 + 压缩列表

每个链表节点内部是一个压缩列表。

这种设计的好处是:

  • 链表保证插入删除效率

  • 压缩列表节省内存

性能和空间之间做了平衡


2.List 常见使用场景

(1)简单消息队列

很多小系统会直接用 Redis List 来实现消息队列。

生产者:

复制代码
LPUSH queue msg

消费者:

复制代码
RPOP queue

这样就形成了一个简单的 FIFO 队列。

不过如果是大型系统,一般还是会使用专业消息队列,例如:

  • Kafka

  • RabbitMQ

因为它们支持:

  • 消息确认

  • 重试机制

  • 持久化


(2)任务队列

List 也很适合用来做任务队列,例如:

  • 异步任务处理

  • 日志处理

  • 批量数据处理

生产者把任务写入队列,消费者不断消费即可。


五、Hash(哈希)

Hash 很适合用来存储 对象结构数据

例如用户信息:

复制代码
user:1001

字段:

复制代码
name -> 张三
age -> 20
city -> 西安

1.常见操作

复制代码
HSET
HGET
HMGET
HGETALL

2.Hash 的底层实现

Redis Hash 的底层结构有两种:

复制代码
ziplist
hashtable

当数据量比较小的时候:

使用 ziplist(压缩列表)

当数据量变大之后:

自动转换为 hashtable

这种设计主要是为了:

在节省内存和保证查询效率之间取得平衡。


3.Hash 常见使用场景

(1)存储对象

例如用户对象:

复制代码
user:1001

字段:

复制代码
name
age
email

如果只修改一个字段,只需要更新对应 field,而不需要重新存整个对象。


(2)购物车

购物车也很适合用 Hash 实现。

例如:

复制代码
cart:user1001

字段:

复制代码
productId -> count

六、Set(集合)

Set 是一个 无序集合

特点:

  • 元素不允许重复

  • 支持集合运算

常见命令:

复制代码
SADD
SMEMBERS
SISMEMBER

1.Set 的底层实现

Set 的底层结构可能是:

复制代码
intset
hashtable

如果集合中的元素都是整数,并且数量较少:

使用 intset

否则使用:

hashtable


2.Set 常见使用场景

(1)标签系统

例如:

复制代码
user:1001:tags

内容可能是:

复制代码
java
redis
backend

(2)好友关系

例如:

复制代码
user:1001:friends

(3)共同好友

Redis 可以直接做集合运算:

复制代码
SINTER

可以快速计算两个集合的交集。


七、Sorted Set(ZSet)

ZSet 可以理解为 带分数的集合

每个元素除了 value,还会有一个 score。

结构类似:

复制代码
member + score

1.ZSet 的底层实现

ZSet 使用两种结构组合实现:

复制代码
skiplist + hashtable

原因是:

  • hashtable:负责快速查找元素

  • skiplist:负责维护有序结构

跳表(skiplist)是一种 多层链表结构

时间复杂度大约是:

复制代码
O(logN)

2.ZSet 常见使用场景

(1)排行榜

这是 ZSet 最经典的使用场景。

例如:

  • 游戏排行榜

  • 点赞排行榜

  • 热度榜

score 可以直接表示排名分数。


(2)延迟队列

可以把时间戳作为 score。

定期扫描最小 score,就可以实现延迟任务。


(3)限流

一些滑动窗口限流算法也会使用 ZSet 实现。


八、总结

Redis 的核心数据结构可以整理成一张表:

类型 底层结构 常见用途
String SDS 缓存、计数器、分布式锁
List quicklist 队列、任务列表
Hash ziplist / hashtable 对象存储
Set intset / hashtable 标签、好友关系
ZSet skiplist + hashtable 排行榜

九、面试高频问题

Redis 有哪些数据结构?

String、List、Hash、Set、ZSet。


Redis String 底层是什么?

SDS。


ZSet 为什么使用跳表?

因为:

  • 实现简单

  • 插入删除效率高

  • 支持范围查询


十、最后

很多人刚开始接触 Redis 时,会把它当作一个简单的缓存工具。

但真正深入之后会发现:

Redis 更像是一套高性能的数据结构工具箱。

很多业务逻辑,如果直接用数据库实现可能会比较复杂,但使用 Redis 的数据结构往往几条命令就能实现。

理解 Redis 的数据结构,其实也是理解 Redis 为什么这么强大的一个过程。

相关推荐
sc_爬坑之路3 小时前
Linux 部署 Redis:一主一从 + Sentinel 完整实战
linux·redis·sentinel
孫治AllenSun3 小时前
【Canal】监听mysql的binlog日志,同步数据到redis和es
redis·mysql·elasticsearch
y = xⁿ3 小时前
【黑马店铺二刷day02】将店铺查询信息添加到Redis中的业务操作
数据库·redis·缓存
一瓢西湖水4 小时前
探究Redis + Caffeine两级缓存架构
redis·缓存·架构
星轨zb4 小时前
非遗AI对话系统架构升级实战
java·人工智能·redis·后端·系统架构
ezreal_pan4 小时前
Redis SCAN 命令使用指南(华为云Redis版)
redis·scan
Mr.朱鹏5 小时前
分布式-redis集群架构
java·redis·分布式·后端·spring·缓存·架构
Volunteer Technology5 小时前
Redis跟HashMap在结构上的区别
数据库·redis·缓存
吾诺5 小时前
GO 快速升级Go版本
开发语言·redis·golang