Learn
Redis/01-introduction

Redis 概览:定位、场景与为什么快

💡🤖 AI 时代,还要学 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 的对比

维度RedisMySQLMemcached
数据位置内存磁盘内存
数据结构9 种以上表/行只有字符串
持久化RDB / AOF完整无
单机 QPS 量级10 万+数千~万10 万+
事务弱事务ACID无
集群Cluster / Sentinel主从/分库分表客户端分片
ℹ️Redis 不是 MySQL 的替代品

Redis 适合存放"热数据、结构简单、允许一定丢失窗口"的数据。核心账务、订单等强一致数据仍然应该以关系数据库为准,Redis 做加速层。

2. 典型应用场景

  1. 缓存:最常见的用法,挡在数据库前面吸收读流量;
  2. 计数器:INCR 原子自增,文章阅读量、点赞数;
  3. 排行榜:ZSet 按 score 排序,天然的 Top-N 结构;
  4. 分布式锁:SET key value NX EX 10,跨进程互斥;
  5. 消息队列:List 的 BLPOP,或更完善的 Stream 消费组;
  6. 会话存储:Session 集中存放,服务无状态化;
  7. 限流:滑动窗口(ZSet)或令牌桶(Lua 脚本);
  8. 去重与统计: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 为什么快:四个原因

  1. 纯内存操作:没有磁盘随机 IO,内存访问是纳秒级;
  2. 高效数据结构:每种类型都针对场景做了编码优化(listpack、skiplist、intset 等);
  3. 单线程无锁:没有上下文切换和锁竞争的开销;
  4. 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 实例执行你的命令:

第一次和 Redis 对话
PING
SET greeting "hello redis"
GET greeting
INCR page:view
INCR page:view
TYPE greeting

PING 返回 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 与通用命令 →
🎯练习
  1. 说出你当前项目中用 Redis 的场景,对照本章 8 个典型场景归类。
  2. 思考:如果 Redis 是多线程执行命令,INCR 还能保证原子性吗?需要付出什么代价?
  3. 查一下你们生产 Redis 的版本(INFO server 中的 redis_version),确认是否在 6.2 以上。