type
Post
status
Published
date
Apr 17, 2021 09:03
slug
summary
本文系统梳理 Redis 工程实践核心知识。解释单线程为何高性能(内存+I/O多路复用+数据结构优化);详解五大数据结构应用场景(String计数、Hash用户资料、List队列、Set去重、ZSet排行榜)与 Bitmap 签到统计;对比定时删除与惰性删除的过期策略、近似 LRU 的内存淘汰机制;对比 RDB 快照与 AOF 日志在数据完整性、恢复速度、文件体积上的权衡;介绍 RESP 协议与 Pipeline 减少 RTT 的优化思路;对比单机/主从/哨兵/集群四种架构的适用场景与代价;提供缓存穿透(布隆过滤器)、击穿(互斥锁)、雪崩(随机过期时间)、双写一致性(先更新DB再删缓存)的完整解决方案;对比 Redis vs Memcached 在持久化、数据类型、底层模型上的差异。
tags
Redis
category
Database
icon
password
wordCount
3024
前言
Redis 是工程实践中最常用的高性能中间件之一。它以 Key-Value 形式存取数据,通常以 内存 作为主要承载介质,从而获得极低延迟与高吞吐。同时,Redis 也提供了持久化、复制与集群能力,使其能够在缓存、会话共享、计数、队列与分布式锁等场景中稳定落地。
阅读导航
- 先理解:Redis 的定位与为什么快
- 再掌握:数据结构、过期与淘汰、持久化
- 最后补全:协议、Pipeline、架构与缓存典型问题
本文面向“希望把 Redis 讲清楚并能用起来”的读者,按概念到实践的路径,系统梳理 Redis 的核心能力与常见面试要点,包括:运行模型与性能来源、常用数据结构与适用场景、过期与淘汰策略、RDB/AOF 持久化机制、RESP 协议与 Pipeline、主从/哨兵/集群架构,以及缓存穿透、击穿、雪崩等典型问题的处理思路。

什么是 Redis(以及它到底像什么)
Redis(Remote Dictionary Server)是一个开源的 Key-Value 数据库,常见使用方式是把数据放在内存里,因此读写非常快。同时它也支持把数据落盘做持久化,所以它不是那种“断电就失忆”的选手。
如果把系统比作一家餐厅:
- 关系型数据库像后厨,流程严谨,强调事务一致性(ACID)。
- Redis 更像前台的“热菜保温柜”,用于存放高频、需要快速取用的数据。
ACID、NoSQL、CAP:别怕,先把名词当成“门牌号”
传统关系型数据库遵循 ACID(原子性、一致性、隔离性、持久性)。而 NoSQL 是一大类“不是传统关系型数据库”的集合,通常更偏向分布式场景。
分布式系统里经常会提 CAP 定理:
- C 一致性(Consistency):大家看到的数据一致。
- A 可用性(Availability):请求来了尽量都能得到响应。
- P 分区容错性(Partition Tolerance):网络分区或节点间断联时系统还能继续工作。
CAP 的结论是:分布式系统不可能同时完美满足 C、A、P 三者,只能在具体场景里做权衡。
补充一个“旁听席代表”:ZooKeeper 常被描述为偏 CP 的实现。它在极端情况下可能拒绝部分请求,并且选主期间集群会短暂不可用。
Redis 的应用场景(它最擅长的活)
- 缓存:把热点数据放在 Redis,减轻数据库压力。
- 消息队列(轻量):例如 List 结构实现简单队列。
- 共享 Session:多实例应用共享登录态。
- 分布式锁:在多实例并发里做互斥控制。
一句话总结:Redis 是“速度型选手”,适合用在高频读写、低延迟、热点数据、并发控制的地方。
单线程 Redis 为什么还能这么快
Redis 常被称为“单线程模型”(核心命令执行路径上单线程),但这并不等于“它只有一个 CPU 在干活”。它快主要因为:
- 数据结构设计合理:很多操作是 O(1) 或接近 O(1)。
- 基于内存:内存访问比磁盘快几个数量级。
- I/O 多路复用:一个线程能同时处理大量连接的就绪事件。
- 减少上下文切换:多线程锁竞争和切换成本很高,Redis 通过模型选择避免了不少开销。
把它想象成:
一位效率极高的“窗口工作人员”,不靠加人手,而靠流程设计与工具(多路复用)让队伍转得飞快。
I/O 机制小剧场:阻塞、非阻塞、多路复用
如果你对 I/O 还不熟,可以把它当作“等快递”的不同方式:
1) 阻塞 I/O
- 你打电话问快递到了没。
- 没到就一直挂着电话等,啥也干不了。
2) 非阻塞 I/O
- 你每隔一分钟打一次电话。
- 电话不会占线,但你会被自己烦死。
3) I/O 多路复用(select/poll/epoll)
- 你雇了一个“通知员”,同时盯着很多快递。
- 哪个到了就通知谁来取。
Redis 就偏向第三种:让线程把精力花在“处理到达的数据”上,而不是傻等。
Redis 的数据结构与常见使用场景
Redis 的数据结构可以理解为一个工具箱,每种结构都对应一类高效操作:
- String:缓存、计数器、限流、简单状态。
- Hash:用户资料、商品信息、购物车(一个 key 下多个 field)。
- List:队列、消息流(轻量场景)。
- Set:去重、共同好友、共同兴趣(交集/并集/差集)。
- Sorted Set(ZSet):排行榜、Top N、按权重排序。
- Bitmap:签到统计、布隆过滤器等。
参考:统计某用户登录天数
Redis 的过期策略:定时删除 + 惰性删除
Redis 的 key 过期删除通常可以理解为两种方式配合:
- 定时删除(更准确说是定期抽样)
Redis 会周期性抽取一部分 key 检查是否过期,过期就删除。
优点:能主动清理。
缺点:如果全量扫描会很耗 CPU,所以实际会抽样。
- 惰性删除
当你访问某个 key 时,Redis 顺手检查它是否已过期。
优点:访问路径上保证不会拿到过期数据。
缺点:长期不访问的过期 key 可能会在内存里躺很久。
如果你问:那一直躺着不删怎么办?
答:别急,Redis 还有“清场保安”。
Redis 内存淘汰机制:内存不够时谁先走
当内存不足以容纳新写入数据时,Redis 会按配置策略淘汰 key。常见策略包括:
- 直接报错(默认行为之一,取决于配置)。
- LRU(最近最少使用):优先淘汰最久没被访问的 key。
- 随机淘汰。
- 只在设置了过期时间的 key 中淘汰(LRU 或随机)。
- 快过期的优先淘汰。
Redis 的 LRU 实现(近似 LRU)
传统精确 LRU 维护成本高。Redis 常用近似 LRU:
- key 的元信息里维护 LRU 时间戳。
- 每次淘汰时随机采样若干个 key,从中挑“最不常用”的淘汰。
- 3.0 后通过 pool(如 16 大小)改进采样效果:把候选 key 放入池中排序,淘汰时从池里挑最小 LRU。
用人话说:
Redis 没有把“最近使用顺序”写成一本账本,而是用抽查与候选池,保证大概率赶走“最不受宠的那位”。
Redis 持久化机制:RDB 与 AOF
Redis 为了性能,主要把数据放在内存里;为了可恢复,又需要把数据以某种方式落盘。Redis 提供两条路线:RDB 快照与 AOF 追加日志。
维度 | RDB(快照) | AOF(追加日志) |
核心思路 | 定期把某一时刻的内存数据生成快照文件(dump.rdb) | 把写命令按顺序追加到日志文件(appendonly.aof) |
写盘方式 | 通常 fork 子进程写临时文件,写完再原子替换 | 写命令追加到缓冲区与文件;按策略 fsync 落盘 |
数据完整性 | 可能丢失最后一次快照之后的数据 | 取决于 fsync 策略;常见 everysec,最坏丢 1 秒左右 |
恢复速度 | 快(加载快照即可) | 相对慢(需要重放写命令) |
文件体积 | 通常更小、更紧凑 | 通常更大(同一数据可能对应多条命令) |
适合场景 | 备份、全量恢复、主从全量同步(常见) | 更关注数据安全性、希望更接近实时恢复 |
选择建议(常见工程实践)
- 追求恢复更完整:优先启用 AOF(常见 everysec)。
- 追求恢复更快与备份更省:启用 RDB。
- 两者都开启:重启通常优先加载 AOF(更接近最新写入)。
Redis 的 RESP 协议:它和客户端怎么聊天
RESP 是 Redis 的通信协议。你可以把它理解为 Redis 的“聊天格式”。常见返回类型包括:
- 简单字符串(+ 开头)
- 错误(- 开头)
- 整数(: 开头)
- Bulk String($ 开头)
- 数组(* 开头)
Pipeline:一次发一堆命令,少跑几趟
Pipeline 的思路很朴素:
- 不要每发一个命令就等待一次响应。
- 把多个命令一次性发给 Redis。
- Redis 处理完按顺序返回响应,客户端再解析。
收益主要来自:减少 TCP 往返等待时间(RTT)。
Redis 常见架构(从单机到集群)
把 Redis 架构理解成“从易到强、从省心到更复杂”的升级路线会更清晰:单机解决功能与性能,主从解决读扩展与备份,哨兵解决高可用切换,集群解决水平扩展。
模式 | 解决什么问题 | 优点 | 主要代价 / 风险 |
单机 | 最小可用形态 | 部署简单,成本低 | 单点故障,容量与吞吐受限 |
主从复制 | 读扩展与数据副本 | 从库分担读;有备份副本 | 主库仍是单点;写扩展有限 |
哨兵(Sentinel) | 高可用与自动故障转移 | 自动监控与切主 | 切换窗口;异步复制可能丢少量数据 |
集群(Proxy 分片) | 水平扩展(分片) | 对业务可透明;多种 hash 策略 | 多一层组件要运维;扩缩容与 failover 常需额外设计 |
集群(直连) | 水平扩展 + 去中心化 | slot 分布;节点间 gossip;可自动 failover | 异步复制不保证强一致;运维与排障复杂度更高 |




Redis 常见问题与解决方案(面试官的快乐源泉)
1) Redis 与 MySQL 双写一致性
常见思路:先更新数据库,再删除缓存。
原因:数据库读远快于写,脏数据窗口相对小。
进阶:异步延时二次删除,降低并发下的脏读概率。
2) 并发竞争同一个 key
- 分布式锁 + 时间戳。
- 用消息队列把并行改串行。
3) 缓存穿透
现象:查一个根本不存在的 key,缓存没有,数据库也没有,请求次次打到数据库。
解决:
- 接口层做参数校验。
- 缓存空值(注意过期时间)。
- 布隆过滤器:不存在就直接拦截。
4) 缓存击穿
现象:某个热点 key 过期瞬间,大量请求同时打到数据库。
解决:
- 互斥锁(mutex):缓存失效时先抢锁,只有一个线程去构建缓存。
5) 缓存雪崩
现象:大量 key 在同一时间过期,导致请求集体打到数据库。
解决:
- 过期时间加随机数。
- 热点 key 设为不过期或提前刷新。
- 缓存服务做高可用。
- 监控与告警,提前发现风险。
Redis vs Memcached(老对手对比)
- 存储方式:Memcached 纯内存,断电数据没了;Redis 可落盘。
- 数据类型:Memcached 主要 key-value;Redis 支持多种结构。
- 底层模型与协议:不同实现与通信协议。
- value 大小:Redis 可支持更大 value(视配置与实现细节),Memcached 单条通常更小。
收个尾:你应该怎么把这篇内容用起来
- 写业务系统:优先把 Redis 当“缓存与并发工具箱”,而不是当主存储。
- 面试复习:重点记住数据结构使用场景、过期与淘汰、持久化差异、缓存三连。
- 真上生产:别忘了配监控、限流、降级、以及灾备方案。
参考文档
优先参考官方文档,其次参考权威社区文章。不同版本的行为细节可能略有差异,建议以实际线上版本为准。
- Redis 官方文档(总入口):https://redis.io/docs/
- Persistence(RDB/AOF 等):https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/
- Redis Cluster(集群):https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/
- 本文引用:
- 统计某用户登录天数(Bitmap 示例):https://blog.csdn.net/ctwctw/article/details/105013817
.jpg?table=block&id=30052c4c-a1ae-8171-a179-c26aaca7614c&t=30052c4c-a1ae-8171-a179-c26aaca7614c)