欢迎光临
我们一直在努力

第1章:Redis 术语全景与单线程工作原理

1. 项目背景

1.1 业务场景

公司准备把一个日活百万的传统电商系统改造成"缓存优先"的高并发架构。产品需求文档里列了一长串功能:登录验证码要5分钟过期、商品详情要缓存加速、购物车要支持实时修改、积分排行榜要实时展示、附近门店要按距离排序、订单状态变更要异步通知下游系统。

技术评审会上,每个人对 Redis 的认知都不一样。后端开发小王说:"Redis 就是个带 TTL 的 HashMap,我把对象序列化一下存进去就行。"测试主管张姐问:"那 Redis 挂了数据会丢吗?如果会丢,我们的测试用例怎么验证数据一致性?"运维老李直接摇头:“我们之前线上 Redis 内存满了直接 OOM,重启后发现一部分 key 消失了,你们知道是为什么吗?”

1.2 痛点

这种认知差异会直接带来生产事故:

认知误区可能的后果真实案例
Redis=HashMap 把所有业务数据塞进一个大 key,单次 GET 返回 50MB 某电商大促期间,商品详情接口的 Redis 响应包超过网络带宽限制
Redis=数据库 依赖 Redis 强持久化,不做数据库兜底 Redis 崩溃后依赖 AOF 恢复,发现最后 2 秒数据丢失
单线程一定快 线上写 KEYS * 排查问题,整个 Redis 阻塞 3 秒 某社交 App 用户搜索接口大面积超时
不加过期时间 计数器 key 无限增长,内存从 4GB 涨到 32GB 某内容平台接口调用计数 key 达到 8000 万个
忽视内存成本 所有数据都放 Redis,不做容量规划 某 SaaS 平台 Redis 月度成本超过 RDS 成本

更麻烦的是,一旦线上出现慢请求,团队第一反应是怀疑"Redis 单线程扛不住",却没有真正理解 Redis 的请求处理链路——问题可能是网络延迟、客户端连接池耗尽、慢命令阻塞、fork 子进程导致的写时复制、或者是 Cluster 重定向过多。

1.3 本章目标

本章不是背命令手册,而是建立一张 Redis 的"房屋结构图":知道门在哪里(客户端连接)、客厅怎么走(命令执行)、每个房间放什么(数据结构)、水电气怎么通(持久化、复制、集群)。只有先知道每个术语在系统里所处的位置,后续学习 String、Hash、Stream、Cluster、源码时才不会碎片化。

另一个重要目标是让读者直观感受 Redis 的请求处理链路。我们会用 Docker 启动一个 Redis 8.6,执行一组基础命令,用调试工具追踪一次请求的生命周期,最后用一张文本架构图把"一条命令从客户端到 Redis 再返回"的过程串起来。


2. 项目设计

2.1 第一幕:Redis 到底是个什么东西?

小胖(嘴里还嚼着薯片):“我就不明白了,Redis 不就是个会过期的 Map 吗?我 SET user:1 张三,然后 GET user:1,这和 Java 里的 HashMap 到底有啥区别?为什么面试的时候问 Redis,面试官老爱追问什么内存模型、线程模型、数据结构?”

小白(放下手中的技术书,推了推眼镜):“如果只是 HashMap,那它怎么支持网络访问?怎么让几十个客户端同时读写?怎么在你重启电脑后数据还在?怎么让数据在多个服务器之间自动同步?再说,HashMap 里的 Key 可以是任何对象,Redis 的 Key 只能是字符串——这本身就是设计取舍。”

小胖:“那你说它到底是什么?缓存?数据库?消息队列?网上有人把它当缓信用,有人把它当临时数据库用,还有人用它做分布式锁和限流器,它到底是干啥的?”

大师(把白板推到中间,拿起记号笔):“好问题。你们先放下技术偏见。我把 Redis 画成四层楼,你们就清楚了。”

┌─────────────────────────────────────────────┐
│ 客户端层 (Client Layer) │
│ redis-cli / Jedis / Lettuce / Redis-py │
│ redis-benchmark / RESP 协议 │
└──────────────────┬──────────────────────────┘
│ TCP / TLS / Unix Socket

┌─────────────────────────────────────────────┐
│ 网络接入层 (Networking Layer) │
│ 连接管理、RESP 协议解析、IO 多路复用 │
│ readQueryFromClient → processInputBuffer │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ 命令执行层 (Command Execution Layer) │
│ 命令查找表、权限校验(ACL)、Lua 脚本引擎 │
│ processCommand → lookupCommand → call │
│ 慢日志记录、统计信息收集 │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ 数据结构层 (Data Structures Layer) │
│ String / Hash / List / Set / ZSet │
│ Stream / Bitmap / HyperLogLog / GEO │
│ RedisObject (type + encoding + lru + ptr) │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ 生命周期与可靠性层 (Lifecycle & Durability) │
│ 过期删除 (Expire) / 内存淘汰 (Eviction) │
│ RDB 快照 / AOF 日志 / 混合持久化 │
│ 主从复制 / Sentinel 高可用 / Cluster 集群 │
│ 模块扩展 (Module: JSON / Search / Vector) │
└─────────────────────────────────────────────┘

大师:“Redis 是一家高效奶茶店。客户端是顾客,网络层是点单窗口——支持多个窗口同时排队但只有一个操作台。命令执行层就是操作台后的茶饮师,按照配方表(命令表)快速制作。数据结构层是身后的原料架——糖、奶、茶、珍珠,每种原料就是 Redis 的一个数据结构。生命周期层意味着:珍珠放久了会扔掉(过期),糖不够了要计划进货(淘汰),每天打烊后把营业记录写进账本(持久化),分店之间互相同步配方(复制/集群)。”

小胖:“嘶——这么说我就懂了!那为什么大家都说 Redis 是单线程?一个茶饮师能忙过来吗?”

技术映射:Redis 的核心执行引擎是单线程事件循环(Event Loop),所有命令在单个线程内排队执行。每一笔操作都必须在微秒级完成,否则后续所有请求都会等待。

2.2 第二幕:单线程为什么快?

小白:“对啊,这也是我最大的疑问。Java 的 HashMap 是线程不安全的,多个线程同时写会出问题。如果 Redis 是单线程,那它怎么处理高并发?现在服务器都是多核的,只用一个核不是浪费吗?”

大师:“问得好。Redis 快不是因为它无脑单线程,而是因为它做对了几件事。”

他用白板画了第二个图:

┌──────────────────────────────────────────────────┐
│ Redis Event Loop │
│ ┌──────────────────────────────────────────┐ │
│ │ IO 多路复用 (epoll/kqueue) │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │ fd1 │ │ fd2 │ │ fd3 │ │ fd4 │ … │ │
│ │ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │ │
│ └─────┼────────┼────────┼────────┼─────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ 命令队列(按序执行) │ │
│ │ GET user:1 → SET order:1001 → INCR cnt │ │
│ └──────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ 内存数据操作(纳秒~微秒级) │ │
│ │ 哈希表查找 → 编码转换 → 值读写 → 响应 │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

大师:“Redis 的快来自三个核心设计。第一,纯内存操作——数据都在内存里,一次 GET 的内存访问时间大约 100 纳秒,而一次磁盘访问大约 10 毫秒,差了 10 万倍。第二,IO 多路复用——Redis 用 epoll(Linux)或类似的系统调用同时监听成千上万个连接。当一个连接有数据到达,epoll 通知 Redis,Redis 读取并处理,然后立刻处理下一个。这就像茶饮师同时盯着 10 个顾客,哪个顾客举手了就立刻服务他,而不是一个一个轮着问’你要点什么’。第三,数据结构紧凑、编码优化——小 String 用 embstr 编码,小 Hash 用 listpack 编码,小 ZSet 用 listpack 替代跳表,每一项设计都在省内存、省时间。”

小白:“但我还是觉得单线程有风险。如果一个命令执行了 100 毫秒,那这 100 毫秒内所有其他客户端都在干等?”

大师:“完全正确。这就是 Redis 最大的坑——阻塞命令。”

技术映射:Redis 的单线程模型本质是"用极简换取极快",但它要求使用者必须了解每个命令的复杂度,避免在单线程上执行长耗时操作。

2.3 第三幕:Redis 8.x 能力地图

小胖(挠头):“那 Redis 8.x 到底能干什么?除了缓存,还有什么大招?”

大师:“Redis 8.x 已经远不是’缓存中间件’了。我把它的能力地图画出来:”

能力域核心能力典型场景涉及章节
缓存加速 String/Hash 缓存、TTL、淘汰策略、旁路缓存 商品详情、用户会话、配置热加载 第3-4章、第12章
计数器与状态 INCR/DECR、Bitmap 签到、HyperLogLog 去重统计 浏览量、月活、接口 UV 第3章、第8-9章
关系与排序 Set 集合运算、ZSet 排行榜、GEO 地理位置 社交关系、积分榜、附近门店 第6-7章、第10章
队列与消息 List 队列、Stream 消息流、Pub/Sub 广播 异步任务、订单事件、配置推送 第5章、第20-21章
原子操作 Lua 脚本、事务(WATCH/MULTI)、分布式锁 库存扣减、限购、防重提交 第13-14章、第18章
持久化 RDB 快照、AOF 日志、混合持久化 数据恢复、迁移、灾备 第22章
高可用 主从复制、Sentinel 自动故障转移、Cluster 分片 读写分离、自动切主、水平扩容 第23-25章
可观测性 INFO/CLIENT LIST/SLOWLOG/LATENCY/MONITOR 慢查询治理、容量规划、告警 第15章、第27-28章
安全 ACL 用户体系、TLS 加密、命令禁用 多租户隔离、合规审计 第29章
扩展与 AI Module API、JSON/Query/Vector/TimeSeries 语义缓存、RAG、实时索引 第38-39章

小胖(眼睛瞪大):“这么多?!我以为 Redis 就是用来做缓存的……”

大师:“这就是为什么我们第一课要画地图。你不必记住每一条命令,但必须知道’遇到什么问题该去 Redis 地图的哪个区域找答案’。后面 40 章就是带你一步步走完这张地图。”

小白:“那我问一个落地问题——Redis 8.6 相比旧版本有什么值得关注的变化?”

大师:“几个关键点。第一,读写 IO 多线程支持(Redis 6 开始引入,到 8.x 更加成熟),这解决的不是命令执行慢的问题,而是网络读写的大包传输问题,尤其是大规模 Pipeline 和 LRANGE 大返回时。第二,ACL v2 支持更细粒度的 key 前缀权限控制和通道权限,多租户场景不再只能靠命名约定隔离。第三,Functions(Redis Function)作为 Lua 脚本的演进方案,有更好的版本管理能力和库依赖。第四,字段级过期(HEXPIRE/HPEXPIRE)让 Hash 的字段可以独立过期。第五,内存效率的持续优化——listpack 逐步替代 ziplist,降低了内存碎片风险。”

技术映射:Redis 8.x 的核心演进方向是"更安全(ACL/TLS)、更高效(IO 多线程/listpack)、更灵活(Functions/字段过期)、更智能(Vector/Query)"。

2.4 第四幕:一条命令的完整旅程

小胖:“那我就想搞清楚一件事——我敲一个 SET user:1 xiaopang,Redis 内部到底发生了啥?”

大师:“好,这是今天最重要的图。跟着我的笔走:”

Step 1: 客户端发送
┌──────────────┐
│ redis-cli │ 序列化: *3\\r\\n$3\\r\\nSET\\r\\n$6\\r\\nuser:1\\r\\n$8\\r\\nxiaopang\\r\\n
│ (客户端) │────── TCP Socket ──────▶
└──────────────┘

Step 2: 网络层接收
┌──────────────────────────────────────┐
│ Redis Server: networking.c │
│ epoll 通知 fd 可读 │
│ acceptTcpHandler → createClient │
│ readQueryFromClient │
│ → 解析 RESP 协议: argc=3, argv=["SET","user:1","xiaopang"]
└──────────────────────────────────────┘

Step 3: 命令执行
┌──────────────────────────────────────┐
│ Redis Server: server.c │
│ processCommand │
│ → lookupCommand("SET") → setCommand │
│ → 权限校验 (ACL check) │
│ → 参数校验 (arity check) │
│ → call(c, CMD_CALL_FULL) │
└──────────────────────────────────────┘

Step 4: 数据操作
┌──────────────────────────────────────┐
│ Redis Server: t_string.c / db.c │
│ setGenericCommand │
│ → 创建/更新 RedisObject │
│ type: OBJ_STRING │
│ encoding: OBJ_ENCODING_EMBSTR │
│ ptr → "xiaopang" │
│ lru: 当前时间戳 │
│ → dbAdd(c->db, key, val) │
│ → dictAdd(db->dict, key, val) │
│ → 如果设置了 EX/PX,写入 expire 字典 │
└──────────────────────────────────────┘

Step 5: 响应返回
┌──────────────────────────────────────┐
│ Redis Server: networking.c │
│ addReply(c, shared.ok) // "+OK\\r\\n" │
│ → 写入 client->buf 或 reply list │
│ → 注册可写事件 │
│ → sendReplyToClient → write 系统调用 │
└──────────────────────────────────────┘

大师:“整个过程在单线程中一气呵成。从 RESP 解析到哈希表写入到 +OK 返回,没有任何锁竞争、没有上下文切换、没有磁盘 IO。这就是为什么 SET 能跑到 10 万 QPS 以上。”

小白:“那如果我在 SET 的同时有人 GET user:1,会不会读到不完整的数据?”

大师:“不会。因为所有命令都是串行执行的。SET 执行完才轮到下一条 GET,GET 读到的要么是旧值要么是新值,不存在’写了一半’的问题。这是单线程模型最优雅的地方——你用不着考虑数据竞争和加锁。”

技术映射:Redis 的单线程执行模型天然保证了单个命令的原子性,无需开发者在应用层做额外并发控制。


3. 项目实战

3.1 环境准备

启动 Redis 8.6(使用 Docker):

# 拉取镜像
docker pull redis:8.6

# 启动容器(不带密码,方便初学体验)
docker run –name redis-lab-ch01 -p 6379:6379 -d redis:8.6

# 验证容器运行状态
docker ps –filter "name=redis-lab-ch01"

# 进入 Redis CLI
docker exec -it redis-lab-ch01 redis-cli

如果已经运行过其他 Redis 容器,端口可能冲突。换一个端口:

docker run –name redis-lab-ch01 -p 6380:6379 -d redis:8.6
docker exec -it redis-lab-ch01 redis-cli -p 6379

3.2 执行第一组命令:感受 Redis 的响应速度

# 基础连通性测试
127.0.0.1:6379> PING
PONG

# 写入一个 key
127.0.0.1:6379> SET user:1 "xiaopang"
OK

# 读取这个 key
127.0.0.1:6379> GET user:1
"xiaopang"

# 查看 key 是否存在
127.0.0.1:6379> EXISTS user:1
(integer) 1

# 查看 key 的类型
127.0.0.1:6379> TYPE user:1
string

# 查看 key 的内部编码
127.0.0.1:6379> OBJECT ENCODING user:1
"embstr"

# 设置 60 秒过期
127.0.0.1:6379> EXPIRE user:1 60
(integer) 1

# 查看剩余时间
127.0.0.1:6379> TTL user:1
(integer) 56

# 删除 key
127.0.0.1:6379> DEL user:1
(integer) 1
127.0.0.1:6379> GET user:1
(nil)

预期现象与解释:

  • PING 返回 PONG:客户端和 Redis 服务端通信正常,RESP 协议解析正确。
  • OBJECT ENCODING user:1 返回 "embstr":短字符串(小于 44 字节)使用嵌入式字符串编码,对象头和内容分配在同一块内存中,减少内存碎片。
  • TTL 返回的整数值每秒递减,到期后 key 自动删除(惰性删除或定期删除)。

3.3 观察 Redis 运行状态

# 服务端基本信息
127.0.0.1:6379> INFO server
# 重点关注: redis_version, redis_mode, os, process_id, tcp_port

# 客户端连接信息
127.0.0.1:6379> INFO clients
# 重点关注: connected_clients, maxclients

# 内存使用
127.0.0.1:6379> INFO memory
# 重点关注: used_memory_human, maxmemory_human, mem_fragmentation_ratio

# 统计信息
127.0.0.1:6379> INFO stats
# 重点关注: total_commands_processed, instantaneous_ops_per_sec,
# keyspace_hits, keyspace_misses, expired_keys, evicted_keys

# 查看所有连接的客户端
127.0.0.1:6379> CLIENT LIST
# 输出: id=3 addr=127.0.0.1:xxxxx fd=8 name= age=10 idle=0 …

# 查看当前所有 key
127.0.0.1:6379> KEYS *
# 注意: 仅学习环境使用,生产环境应使用 SCAN

3.4 用 redis-benchmark 感受 Redis 吞吐量

redis-benchmark 是 Redis 自带的性能压测工具。退出 redis-cli(按 Ctrl+C),在容器内运行:

# 进入容器 bash
docker exec -it redis-lab-ch01 bash

# 基础压测:10 万请求,50 并发连接,只测 SET 和 GET
redis-benchmark -n 100000 -c 50 -t set,get -q

# 输出示例(实际数值因机器性能而异):
# SET: 85470.09 requests per second, p50=0.271 msec
# GET: 90909.09 requests per second, p50=0.239 msec

参数解释:

  • -n 100000:总请求数 10 万。
  • -c 50:并发 50 个客户端连接。
  • -t set,get:只测试 SET 和 GET 命令。
  • -q:安静模式,只显示 QPS 结果。

加上延迟分位数:

redis-benchmark -n 100000 -c 50 -t set,get –latency

额外测试几个常用命令的性能差异:

# 测试 PING(最快,纯协议响应)
redis-benchmark -n 100000 -c 50 -t ping -q

# 测试 INCR(原子自增)
redis-benchmark -n 100000 -c 50 -t incr -q

# 测试 LPUSH(List 写入)
redis-benchmark -n 100000 -c 50 -t lpush -q

观察到的性能排序通常是:PING > SET/GET > INCR > LPUSH。这是因为不同命令涉及的数据结构操作复杂度不同:PING 几乎不操作数据,SET/GET 只做哈希表查找,INCR 需要读取、自增、写回,LPUSH 需要操作链表节点。

3.5 亲手触发一次"阻塞"

这个实验非常重要——让你直观感受慢命令对 Redis 的影响。

在一个终端执行慢命令(使用 Lua 模拟一个耗时操作):

docker exec -it redis-lab-ch01 redis-cli

先写入一些数据:

127.0.0.1:6379> SET test:1 "hello"
OK
127.0.0.1:6379> SET test:2 "world"
OK

打开第二个终端,执行一个耗时 3 秒的 Lua 脚本(Redis 的 Lua 脚本会阻塞事件循环):

终端 B(先准备好,但不要回车):

docker exec -it redis-lab-ch01 redis-cli EVAL "local start=redis.call('TIME')[1]; while(redis.call('TIME')[1]-start<3) do end; return 'slow done'" 0

终端 A(先执行一个带时间戳的命令来校准):

docker exec -it redis-lab-ch01 redis-cli TIME

然后在终端 B回车执行慢脚本,同时在终端 A立即执行:

docker exec -it redis-lab-ch01 redis-cli PING

你会发现 PING 等了大约 3 秒才返回 PONG。这就是单线程模型的"副作用"——一个慢命令会拖慢所有其他请求。

这个实验的价值在于:它让你亲眼看见了"慢命令阻塞"不是理论,而是真实可复现的。后续学习时,你会对 KEYS *、大 key 删除、复杂 Lua 脚本保持敬畏。

3.6 查看慢查询日志

Redis 内置了慢查询日志功能,可以记录执行时间超过阈值的命令:

# 配置慢查询阈值为 1000 微秒(1 毫秒)
127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 1000

# 配置慢查询日志最多保存 128 条
127.0.0.1:6379> CONFIG SET slowlog-max-len 128

# 查看最近的慢查询
127.0.0.1:6379> SLOWLOG GET 5
# 1) 1) (integer) 3 # 日志 ID
# 2) (integer) 1680000000 # 时间戳
# 3) (integer) 3012541 # 执行耗时(微秒)
# 4) 1) "EVAL" # 命令
# 2) "local start=…" # 参数
# 3) "0"

# 查看当前慢查询数量
127.0.0.1:6379> SLOWLOG LEN
# 重置慢查询日志
127.0.0.1:6379> SLOWLOG RESET

3.7 用 MONITOR 实时观察命令流

MONITOR 可以实时看到 Redis 收到的每一条命令——这是学习和调试的利器(但生产环境慎用,本身有性能开销):

# 终端 A:开启 MONITOR
127.0.0.1:6379> MONITOR
OK

# 终端 B:执行一些命令
docker exec -it redis-lab-ch01 redis-cli SET key1 val1
docker exec -it redis-lab-ch01 redis-cli GET key1
docker exec -it redis-lab-ch01 redis-cli INCR counter

回到终端 A,你会看到实时输出:

1680000000.123456 [0 127.0.0.1:54321] "SET" "key1" "val1"
1680000000.234567 [0 127.0.0.1:54322] "GET" "key1"
1680000000.345678 [0 127.0.0.1:54323] "INCR" "counter"

MONITOR 的每一行包含时间戳、数据库编号、客户端地址和完整命令。

3.8 探索 Redis 配置文件

查看当前运行配置:

# 查看所有配置
127.0.0.1:6379> CONFIG GET *
# (输出会很长,建议按需检索)

# 查看特定配置
127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET save
127.0.0.1:6379> CONFIG GET appendonly
127.0.0.1:6379> CONFIG GET timeout

# 修改配置(运行时生效,重启后丢失)
127.0.0.1:6379> CONFIG SET maxmemory 256mb
OK

3.9 常见坑

坑1:KEYS * 生产事故。KEYS 命令会遍历整个键空间,复杂度 O(N),100 万个 key 时可能阻塞数秒。应使用 SCAN 替代:

# 危险!
127.0.0.1:6379> KEYS user:*

# 安全替代
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 100

坑2:无视 OBJECT ENCODING 的陷阱。同一个 SET 命令,短字符串和长字符串的内部编码完全不同:

127.0.0.1:6379> SET short "hi"
OK
127.0.0.1:6379> OBJECT ENCODING short
"embstr"

127.0.0.1:6379> SET long "这是一段超过44字节的字符串,会触发不同编码…"
OK
127.0.0.1:6379> OBJECT ENCODING long
"raw"

如果你不了解编码差异,在做内存估算时会严重低估或高估。

坑3:INFO 输出误读。used_memory_human 显示的是 Redis 当前使用的内存,但不包括内存碎片。真正需要关注的是 used_memory_rss_human(操作系统视角的物理内存)和 mem_fragmentation_ratio(碎片率,等于 used_memory_rss / used_memory)。


4. 项目总结

4.1 本章核心收获

维度核心认知
Redis 定位 以内存数据结构为核心、基于事件驱动的高性能服务端系统,远不止是"带 TTL 的 HashMap"
单线程模型 核心命令执行路径是单线程串行的,借助 IO 多路复用和纯内存操作达到微秒级延迟
性能关键 快不是魔法——紧凑编码+内存访问+无锁设计+IO 多路复用;慢命令是最大杀手
请求链路 客户端 → RESP 编码 → epoll 通知 → 命令解析 → 权限校验 → 数据操作 → RESP 响应
Redis 8.x 能力 从缓存扩展到消息流、向量检索、字段级过期、ACL v2、Functions、JSON 索引等

4.2 优点与缺点

优点缺点
命令模型简单,学习曲线平缓 单线程执行路径害怕慢命令、大 key、复杂脚本
数据结构贴近业务,建模自然 内存成本高,需要容量和淘汰策略规划
纯内存访问,常规命令延迟微秒级 复杂查询和多表关联不是 Redis 的强项
支持持久化、复制、集群和模块扩展 初学者容易忽视编码差异、key 规范和 TTL 策略
原子命令天然线程安全,无锁编程 单个命令原子 ≠ 多命令事务原子(需要 Lua 或 MULTI/EXEC)

4.3 适用与不适用场景

适用场景:

  • 热点数据缓存(商品详情、用户会话、配置信息)
  • 临时数据存储(验证码、Token、一次性票据)
  • 高性能计数器(浏览量、点赞数、接口调用次数)
  • 排行榜与排序(积分榜、热榜、时间线)
  • 轻量消息队列(异步任务通知、事件传递)
  • 分布式协调(分布式锁、限流器、开关控制)
  • 实时状态存储(在线用户、设备状态、会话管理)
  • 不适用场景:

  • 需要复杂 SQL 查询和关联统计的业务数据——应使用关系数据库
  • 单条记录超大(>10MB)的数据——会导致网络传输和内存碎片问题
  • 需要强事务一致性(ACID)的核心交易数据——Redis 不应替代关系数据库
  • 大量冷数据需要廉价存储——内存成本远高于磁盘
  • 4.4 常见踩坑经验

    案例表象根因解法
    某电商大促 P99 延迟暴涨 商品详情接口从 5ms 涨到 2s 运维用 KEYS * 排查问题,阻塞整个 Redis 3 秒 禁用 KEYS,改用 SCAN;配置 ACL 禁止危险命令
    某社交 App 内存持续上涨 Redis 内存从 8GB 涨到 32GB 大量计数器 key 未设 TTL,永不过期 制定 key 命名规范和 TTL 策略;定期巡检无 TTL key
    某支付系统 Redis “幽灵 key” 某些 key 超 TTL 后仍能读到 主从复制中,从节点用绝对过期时间,删除命令延迟到达 校验数据时增加 TTL 二次判断;用 Lua 读写前先检查 TTL

    4.5 思考题

  • 设计题:如果你需要设计一个"最近 7 天每个用户每天发送短信次数"的计数器系统,你会如何设计 Redis key 和过期策略?写出 key 格式和到期逻辑。

  • 排查题:线上 Redis 的 instantaneous_ops_per_sec 突然从 5 万降到 2 千,但 connected_clients 没有明显变化。请列出至少 5 种可能导致此现象的根因,并说明优先排查哪一项。

  • (答案提示将在下一章末尾给出。)

    下一章预告:第2章——Windows、WSL2 与 Docker 环境搭建。我们将编写一份 Docker Compose 文件,启动带密码、AOF、数据挂载和 RedisInsight 的本地实验环境,为后续所有章节打好基础。

    延伸阅读与资源

    MongoDB 实战进阶与内核修炼 python入门:Rquests从菜鸟脚本到企业级SDK的网络实战圣经 Milvus向量数据库实战修炼:从 0 到 1精通向量检索与生产落地 后端工程师的 AI 转型第一课:Ollama 与私有化大模型实战 10倍开发者的 Dify 魔法书:从零构建全栈 AI 应用 后端工程师转型AI第一课-Ollama 与私有化大模型实战

    大型语言模型(LLM) vLLM 高性能推理落地实战

    Agent开发之LlamaIndex 实战修炼与源码进阶

    大语言模型Transformers 实战修炼与源码剖析

    赞(0)
    未经允许不得转载:171主机测评 » 第1章:Redis 术语全景与单线程工作原理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址