Redis 概览:定位、场景与为什么快
必要但概念级即可。命令参数可以让 AI 辅助生成,但数据结构选型(String/Hash/ZSet 怎么选)、缓存穿透/击穿/雪崩、与数据库的一致性,这些用错了会出大事故,AI 不会替你承担线上责任。建议学到「懂原理、会选型」:具体命令不必死背,用时让 AI 写并人工核对;但缓存与一致性这套心智模型必须自己清楚。
如果你只把 Redis 当作"一个用来做缓存的键值存储",那你大概只用到了它 20% 的能力。Redis 是一个内存数据结构服务器(in-memory data structure server)——它提供的不是简单的 key-value,而是 String、Hash、List、Set、ZSet、Stream 等丰富的数据结构,以及围绕这些结构的原子操作。理解这一点,是从"会用 Redis"到"用好 Redis"的第一步。
1. Redis 的定位
1.1 内存优先,磁盘为辅
传统数据库(MySQL、PostgreSQL)的设计前提是"数据放磁盘,内存做缓存";Redis 反过来:全部数据常驻内存,磁盘只用于持久化和重启恢复。这带来两个直接后果:
- 读写都是纯内存操作,延迟在微秒级(通常 100 微秒以内);
- 数据量受内存容量限制,单实例一般建议控制在 10 GB 级别以内。
1.2 与 MySQL/Memcached 的对比
| 维度 | Redis | MySQL | Memcached |
|---|---|---|---|
| 数据位置 | 内存 | 磁盘 | 内存 |
| 数据结构 | 9 种以上 | 表/行 | 只有字符串 |
| 持久化 | RDB / AOF | 完整 | 无 |
| 单机 QPS 量级 | 10 万+ | 数千~万 | 10 万+ |
| 事务 | 弱事务 | ACID | 无 |
| 集群 | Cluster / Sentinel | 主从/分库分表 | 客户端分片 |
Redis 适合存放"热数据、结构简单、允许一定丢失窗口"的数据。核心账务、订单等强一致数据仍然应该以关系数据库为准,Redis 做加速层。
2. 典型应用场景
- 缓存:最常见的用法,挡在数据库前面吸收读流量;
- 计数器:
INCR原子自增,文章阅读量、点赞数; - 排行榜:ZSet 按 score 排序,天然的 Top-N 结构;
- 分布式锁:
SET key value NX EX 10,跨进程互斥; - 消息队列:List 的
BLPOP,或更完善的 Stream 消费组; - 会话存储:Session 集中存放,服务无状态化;
- 限流:滑动窗口(ZSet)或令牌桶(Lua 脚本);
- 去重与统计:Set 去重、HyperLogLog 估算 UV、Bitmap 签到。
后面的章节会逐一展开这些场景的完整实现。
3. 单线程模型:为什么单线程还能这么快
3.1 "单线程"指的是什么
Redis 的命令执行是单线程的:任意时刻只有一个命令在被执行,天然无并发竞争,因此不需要锁。但 Redis 进程本身并不是只有一个线程——持久化的 fork 子进程、异步删除(lazy free)线程、Redis 6 之后的 IO 多路读写线程都是额外线程。
+--------------------------- Redis 进程 ---------------------------+
| |
客户端A --+--> [IO 线程: 读请求/写响应] |
客户端B --+--> [IO 线程: 读请求/写响应] --> [主线程: 顺序执行命令] --> 内存数据 |
客户端C --+--> [IO 线程: 读请求/写响应] |
| |
| [后台线程: lazy free / fsync] [fork 子进程: RDB/AOF 重写] |
+------------------------------------------------------------------+3.2 IO 多路复用
单线程如何同时服务上万个连接?答案是 IO 多路复用(Linux 上是 epoll)。主线程通过 epoll 同时监听所有客户端 socket,哪个 socket 有数据到达就处理哪个,不会阻塞在任何一个连接上:
10000 个客户端连接
|
v
epoll_wait() <-- 一次系统调用返回"哪些连接就绪"
|
v
事件循环: 依次处理就绪连接的命令 -> 写回响应 -> 回到 epoll_wait这和 Nginx、Node.js 的事件驱动模型是同一个思路:线程不等待 IO,只处理就绪事件。
3.3 为什么快:四个原因
- 纯内存操作:没有磁盘随机 IO,内存访问是纳秒级;
- 高效数据结构:每种类型都针对场景做了编码优化(listpack、skiplist、intset 等);
- 单线程无锁:没有上下文切换和锁竞争的开销;
- IO 多路复用:单线程也能撑起海量连接。
命令串行执行意味着:任何一个慢命令都会阻塞所有客户端。KEYS *、对百万成员的 Set 执行 SMEMBERS、大 key 的 DEL,都可能造成几百毫秒甚至秒级卡顿。生产环境要用 SCAN 替代 KEYS,用 UNLINK 替代 DEL 删大 key。
4. Redis 6/7 的多线程 IO
随着网卡带宽越来越大,瓶颈逐渐从内存操作转移到网络包的读取与写回(read/write 系统调用 + 协议解析)。Redis 6 引入了多线程 IO:
- 命令执行仍然是单线程,数据一致性模型不变;
- 多个 IO 线程只负责并行地"读取请求 + 解析协议"和"写回响应";
- 通过
io-threads 4配置线程数,默认关闭(io-threads 1)。
# redis.conf
io-threads 4 # IO 线程数,建议不超过 CPU 核数的一半
io-threads-do-reads yes # 读请求也用多线程(默认只有写回用)只有当单实例 QPS 非常高(10 万以上)且 CPU 网络处理成为瓶颈时才需要开启,多数场景默认配置即可。
5. 快速体验
安装后(下一章详述)用 redis-cli 感受一下。下面的代码块可以直接点「运行」——每次运行都会启动一个全新的空 Redis 实例执行你的命令:
PING
SET greeting "hello redis"
GET greeting
INCR page:view
INCR page:view
TYPE greetingPING 返回 PONG 说明连接正常;INCR 对不存在的 key 会先当作 0 再加一,所以两次调用依次返回 1 和 2。
用 redis-benchmark 直观感受性能(本机测试):
$ redis-benchmark -t set,get -n 100000 -q
SET: 142857.14 requests per second, p50=0.175 msec
GET: 158730.16 requests per second, p50=0.159 msec单机十几万 QPS,p50 延迟不到 0.2 毫秒——这就是内存数据库的量级。
本课程以 Redis 7.x 为基准。Redis 7 带来了 Function(替代 Lua 脚本管理)、分片发布订阅、listpack 全面替换 ziplist、AOF 多文件模式等改进。生产环境建议至少 6.2,新项目直接上 7.x。
小结
- Redis 是内存数据结构服务器,不只是缓存:9 种数据结构 + 原子操作是它的核心价值
- 快的原因:纯内存 + 高效编码 + 单线程无锁 + IO 多路复用
- 命令执行单线程:慢命令会阻塞一切,这是贯穿全课程的红线意识
- Redis 6/7 的多线程只加速网络 IO,命令仍串行执行
- 下一章动手安装并掌握 redis-cli 与通用命令 →
- 说出你当前项目中用 Redis 的场景,对照本章 8 个典型场景归类。
- 思考:如果 Redis 是多线程执行命令,
INCR还能保证原子性吗?需要付出什么代价? - 查一下你们生产 Redis 的版本(
INFO server中的 redis_version),确认是否在 6.2 以上。