Redis知识点
本文最后更新于 2025年11月19日 晚上
数据类型

PS:除上述五个基础数据类型以外,redis还有支持三个特殊的类型
HyperLogLogs(基数统计):
举个例子,A = {1, 2, 3, 4, 5}, B = {3, 5, 6, 7, 9};那么基数(不重复的元素)= 1, 2, 4, 6, 7, 9;这个结构可以非常省内存的去统计各种计数,比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等。
Bitmap (位存储):
即位图数据结构,都是操作二进制位来进行记录,只有0 和 1 两个状态。统计用户信息,活跃,不活跃! 登录,未登录! 打卡,不打卡! 两个状态的,都可以使用 Bitmaps!如果存储一年的打卡状态需要多少内存呢? 365 天 = 365 bit 1字节 = 8bit 46 个字节左右!
geospatial (地理位置):
Redis 3.2 版本就推出了! 这个功能可以推算地理位置的信息: 两地之间的距离, 方圆几里的人。
🧩 一、Redis 基本数据类型(5种)
1️⃣ String(字符串)
📘 含义:
最基本的类型,可以存储 字符串、整数、浮点数。
底层实现是 SDS(Simple Dynamic String)。
📖 常用命令:
1 | |
🧠 存储示意:
1 | |
💡 使用场景:
缓存对象的序列化数据(如 JSON)
分布式锁(SETNX)
计数器(INCR)
热点数据缓存(商品详情、token等)
👨💻 开发实践:
在 Spring Boot 中:
1 | |
2️⃣ List(列表)
📘 含义:
一个 双向链表,每个节点是一个字符串。
📖 常用命令:
1 | |
🧠 存储示意:
1 | |
💡 使用场景:
消息队列(FIFO)
任务列表、评论列表等
时间线(Timeline)
👨💻 开发实践:
1 | |
3️⃣ Set(集合)
📘 含义:
无序、去重的字符串集合。
📖 常用命令:
1 | |
🧠 存储示意:
1 | |
💡 使用场景:
用户标签、去重统计(如活跃用户)
共同好友(交集)
推送系统去重
👨💻 开发实践:
1 | |
4️⃣ Hash(哈希)
📘 含义:
类似于 Map / HashMap,用于存储对象的字段值对。
结构为:key → field → value
📖 常用命令:
1 | |
🧠 存储示意:
1 | |
💡 使用场景:
对象缓存(用户信息、配置)
存储结构化数据
统计类数据(点赞数、评论数)
👨💻 开发实践:
1 | |
5️⃣ ZSet(有序集合)
📘 含义:
带权重(score)的有序集合,元素按分数排序。
📖 常用命令:
1 | |
🧠 存储示意:
1 | |
💡 使用场景:
排行榜(游戏分数、热度)
延时任务(score = 时间戳)
优先级队列
👨💻 开发实践:
1 | |
🧮 二、三种特殊数据类型
1️⃣ HyperLogLog(基数统计)
用途: 统计去重后元素的数量(近似值)。
优点: 内存极省(12KB 可统计上亿数据)。
1 | |
场景: 网站UV统计、独立访客数。
2️⃣ Bitmap(位图)
用途: 用二进制位表示状态(0/1)。
1 | |
场景: 用户签到、活跃状态、布隆过滤器基础。
3️⃣ Geospatial(地理位置)
用途: 存储经纬度,并可计算两点距离、查找范围内位置。
1 | |
场景: 附近的人、附近的商家、地图服务。
RedisObject
redisObject 是 Redis 的“元对象”,负责描述「这是个什么类型、怎么存的、上次用过什么时候、被多少地方引用」,真正的数据存放在 ptr 指向的结构里
1 | |
1️⃣ type —— 对象类型(4 位)
表示这个对象是什么类型的数据结构,对应我们平时用的 String / List / Set 等。
| 值 | 含义 |
|---|---|
| 0 | STRING |
| 1 | LIST |
| 2 | SET |
| 3 | ZSET |
| 4 | HASH |
| 5 | MODULE(模块类型) |
| 👉 Redis 用 4 位表示类型,最多支持 16 种。 |
2️⃣ encoding —— 编码方式(4 位)
同一种类型可以有不同的内部实现方式,比如:
| 类型 | 编码方式 | 说明 |
|---|---|---|
| STRING | int | 纯数字,小整数直接保存 |
| STRING | embstr | 小字符串(≤44字节)嵌入对象 |
| STRING | raw | 普通 SDS 动态字符串 |
| LIST | quicklist | 快速链表(双端压缩) |
| SET | intset | 小整数集合 |
| SET | hashtable | 普通哈希表 |
| HASH | ziplist / listpack | 小哈希对象(紧凑存储) |
| HASH | hashtable | 普通哈希表 |
| ZSET | ziplist / listpack | 小型有序集合 |
| ZSET | skiplist | 跳表实现的有序集合 |
🔸 举个例子:
1 | |
3️⃣ lru —— LRU / LFU 时间戳字段(24 位)
这个字段记录对象的「最近访问信息」,主要用于内存淘汰策略(LRU / LFU)。
💡 Redis 支持两种模式:
LRU 模式(Least Recently Used)
lru 存放对象最后一次被访问的时间戳(相对 lru_clock)
用于 volatile-lru、allkeys-lru 策略。LFU 模式(Least Frequently Used)
lru 被拆成:
8 位:访问频率(freq)
16 位:最后访问时间(ldt)
用于 volatile-lfu、allkeys-lfu 策略。
也就是说,这个字段既可能是一个时间戳,也可能是一个复合的访问计数。
4️⃣ refcount —— 引用计数
表示这个对象被引用的次数。
用途:
防止内存重复释放。
当对象被多个地方共享(比如小整数对象),通过引用计数管理内存生命周期。
常见情况:
| refcount 值 | 含义 |
|---|---|
| 1 | 正常使用,仅被一个地方引用 |
| 2+ | 被多个 key 共享(比如共享整数池) |
| 0 | 引用清零,等待释放(free) |
5️⃣ ptr —— 数据指针
指向实际的数据结构实例,比如:
如果是 STRING 类型,可能指向一个 SDS(简单动态字符串)
如果是 HASH 类型,可能指向 哈希表结构 dict
如果是 ZSET 类型,可能指向 跳表结构 zskiplist
也就是说:
robj.ptr 才是实际存储数据的“内核”。
| 字段 | 位宽 | 含义 | 示例 |
|---|---|---|---|
| type | 4 bit | 数据类型(String、List等) | STRING |
| encoding | 4 bit | 编码方式(int、embstr、ziplist等) | embstr |
| lru | 24 bit | LRU 时间戳或 LFU 信息 | 123456 |
| refcount | int | 引用计数 | 1 |
| ptr | void* | 指向底层数据结构 | SDS、dict、zskiplist等 |
持久化
RDB(记录数据)
RDB全称RedisDatabaseBackupfile(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据


AOF(记录操作命令)
AOF全称为AppendOnly File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。



从持久化中恢复数据
- redis重启时判断是否开启aof,如果开启了aof,那么就优先加载aof文件;
- 如果aof存在,那么就去加载aof文件,加载成功的话redis重启成功,如果aof文件加载失败,那么会打印日志表示启动失败,此时可以去修复aof文件后重新启动;
- 若aof文件不存在,那么redis就会转而去加载rdb文件,如果rdb文件不存在,redis直接启动成功;
- 如果rdb文件存在就会去加载rdb文件恢复数据,如加载失败则打印日志提示启动失败,如加载成功,那么redis重启成功,且使用rdb文件恢复数据;

过期策略
惰性删除
惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放
定期删除
定期删除:每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。
定期清理有两种模式:
●SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数
●FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
Redis的过期删除策略:情惰性删除+定期删除两种策略进行配合使用
数据淘汰策略
数据的淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
集群相关
主从复制(1 主+N从)
在Redis 2.8之前只支持全量复制(比如第一次同步时),在2.8之后新增支持增量复制(只会把主从库网络断连期间主库收到的命令,同步给从库)。



全量复制
- 第一阶段是主从库间建立连接、协商同步的过程,主要是为全量复制做准备。在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了。
- 第二阶段,主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。
- 第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。

增量复制
- repl_backlog_buffer:它是为了从库断开之后,如何找到主从差异数据而设计的环形缓冲区,从而避免全量复制带来的性能开销。如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能乖乖地进行一次全量复制,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量复制的概率。
- replication buffer:每个client连上Redis后会分配一个client buffer,所有数据交互都是通过这个buffer进行的:Redis先把数据写到这个buffer中,然后再把buffer中的数据发到client socket中再通过网络发送出去,这样就完成了数据交互。所以主从在增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从数据一致,我们通常把它叫做replication buffer。

哨兵机制(1 主+N从+N哨兵)




分片集群cluster(N主+N从)


hash tag
可以通过hash tag指定数据存在特定的节点中
Redis 规定了一个特殊规则:如果 key 含有 {},则 只对大括号中的内容 计算 CRC16。
举例:
| key | 参与哈希部分 | 计算结果(slot) |
|---|---|---|
user:{1001}:cart |
1001 |
slot = CRC16(“1001”) % 16384 |
user:{1001}:profile |
1001 |
slot = CRC16(“1001”) % 16384 |
user:{2002}:cart |
2002 |
slot = CRC16(“2002”) % 16384 |
🔹 所以 {} 实际是“告诉 Redis:只对这部分算哈希”。
🔹 这样两个 key {1001}、{2002} 分别落到不同槽上,可能不同节点
Hash Tag 的目的不是让你“自由选择节点”,而是为了解决 Redis Cluster 的限制:
Redis Cluster 不允许跨 slot 执行多 key 操作(如 MGET、MSET、Lua)。
有了 {} 之后:
MGET user:{1001}:profile user:{1001}:cart ✅ 可行(同slot)
MGET user:{1001}:profile user:{2002}:profile ❌ 不行(跨slot)
缓存
缓存穿透
缓存穿透:查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库
解决方案一:缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存
解决方案二:布隆过滤器
缓存击穿
缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮
解决方案一:互斥锁(强一致性、性能差)
解决方案二:逻辑过期(高可用、性能优)
缓存雪崩
缓存雪崩:在同一时段大量的缓存key同时失效或者Redis服务岩机,导致大量请求到达数据库,带来巨大压力
解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性 哨兵模式、集群模式
- 给缓存业务添加降级限流策略 nginx或springcloud gateway 降级可做为系统的保底策略,适用于穿透、击穿、雪崩
- 给业务添加多级缓存 Guava或Caffeine
双写一致性
双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致
读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间
写操作:延迟双删 ,先删缓存,更新数据库后再延时删除一次缓存,延时是因为数据库一般是主从结构,延时时间不确定,还是有脏数据风险
一致性要求高:共享锁+排他锁
允许最终一致性:
- 异步通知mq:数据库更新完成后,发送消息通知缓存删除或更新
- 基于 Binlog 的缓存同步(canal):
1️⃣ MySQL 写入事务提交;
2️⃣ Binlog 记录被 Canal 捕获;
3️⃣ Canal 推送更新事件;
4️⃣ Redis 缓存同步更新或删除;
常见问题
Redis为什么快
Redis之所以能够实现高性能,主要有以下几个方面的原因:
- 内存数据结构设计:Redis 将数据存储在内存中,摆脱了磁盘 I/O 的瓶颈,大大提高了读写速度。 Redis 提供了多种数据结构,如字符串、哈希表、列表、集合等,针对不同场景进行优化设计,能够高效地支持各种数据操作。
- 单线程模型: Redis 使用单线程模型处理客户端请求,避免了线程切换和竞争带来的性能开销。 通过高效的事件循环,Redis 能够快速地响应大量并发请求。
- 高效的网络 I/O 模型:Redis 使用非阻塞的 I/O 模型,配合 epoll/kqueue 等高性能的事件处理机制,能够高效地处理网络 I/O。 Redis 采用了 Redis 协议(RESP)作为通信协议,相比 HTTP 等通用协议,RESP 更加简单高效。
- 内存管理优化:Redis 采用自定义的内存分配器,避免了标准 malloc 分配器的性能损耗。 通过对内存进行预分配和复用,Redis 能够减少内存申请和释放的开销。
- 异步操作:Redis 将一些耗时的操作如 BGSAVE、BGREWRITEAOF 等异步化处理,避免阻塞主线程,提高了响应速度。
- 持久化优化:Redis 的 RDB 和 AOF 两种持久化机制都进行了优化,尽量减少持久化过程对性能的影响。
大key如何删除
在Redis中,删除大key(如大型哈希表、列表、集合或有序集合)时,直接使用DEL命令会导致Redis阻塞,影响性能。
解决方案:
- 使用
UNLINK命令:UNLINK命令从Redis 4.0开始引入,它的工作原理是异步删除key。UNLINK命令会立即将key从数据库中删除,但实际的内存释放工作会在后台线程中进行,不会阻塞主线程。- 示例:
1 | |
- 分批删除:
- 使用
SCAN、HSCAN、SSCAN、ZSCAN等命令分批删除大key中的元素,减少每次操作的负载。 - 示例:
- 使用
1 | |
这个命令会返回100个匹配的key,然后你可以逐个删除这些key。
- 选择在业务低峰期执行删除操作:
- 在业务低峰期执行删除操作,可以减少对正常业务的影响。
- 例如,可以选择在夜间或周末进行大key的删除操作。
- 使用
RENAME命令:- 先将大key重命名,使其不再被业务访问,然后再逐步删除。
- 示例:
1 | |
分布式锁
setnx
1 | |
1 | |
| 问题 | 描述 |
|---|---|
| ❌ 非原子操作 | 早期 SETNX + EXPIRE 是两条命令,可能中途宕机导致死锁。 |
| ❌ 没有自动续期 | 如果业务执行超过 10s,锁过期被别人拿走。 |
| ❌ 没有可重入性 | 同一线程内重入锁会阻塞自己。 |
| ❌ 没有公平性控制 | 谁抢到算谁的,无法控制顺序。 |
| ❌ 不支持 RedLock 多节点冗余 | 单实例 Redis 挂掉锁失效。 |
👉 所以,SETNX 方案 能用但不安全,适合简单场景(如单机测试、非核心任务)。 |
redisson
1 | |
| 特性 | 说明 |
|---|---|
| ✅ 原子性 | 使用 Lua 脚本确保加锁/释放锁原子操作 |
| ✅ 自动续期(看门狗机制) | 业务未结束前自动延长锁租期(默认 30s) |
| ✅ 可重入 | 同一线程可多次获得同一锁 |
| ✅ 公平锁 / 读写锁 | 支持多种锁类型(getFairLock(), getReadWriteLock()) |
| ✅ 分布式多节点支持 | 可用 RedLock 算法跨多个 Redis 实例冗余加锁 |
| ✅ 超时等待机制 | 支持 tryLock(waitTime, leaseTime) 模式 |
| ✅ 可靠解锁 | 只删除当前线程持有的锁,防止误删 |


红锁
普通分布式锁,锁的可靠性依赖于单个 Redis 实例
如果这个 Redis 实例宕机或主从切换(未同步 key),
锁数据可能丢失,从而出现:
- A 持锁的节点宕机;
- Redis 主节点切换后锁丢失;
- B 再次获取锁成功;
- 两个客户端同时操作共享资源 ❌(锁失效)。
注意:Redis Cluster 模式下不能、也不需要使用 RedLock(红锁)算法。



1 | |
两者对比
| 对比项 | SETNX 手写锁 |
Redisson |
|---|---|---|
| 原子性 | ✅(仅 SET NX EX) | ✅ Lua 脚本保证 |
| 自动续期 | ❌ | ✅ 看门狗机制 |
| 可重入 | ❌ | ✅ 支持 |
| 锁类型 | 仅独占锁 | 可重入、公平、读写锁 |
| 超时等待 | 手动轮询 | 内置 tryLock(waitTime, leaseTime) |
| 解锁安全 | 需自行校验 UUID | 自动校验持有线程 |
| 多节点冗余(RedLock) | ❌ | ✅ 支持 |
| 实现复杂度 | ⭐ 简单 | ⭐⭐⭐ 稳定生产可用 |
| 适用场景 | 简单任务、单实例 | 分布式系统、并发高业务 |