Redis 从入门到生产:数据结构、持久化、集群、工程实践与避坑(含 Node.js/Python 示例)
Redis 从入门到生产:数据结构、持久化、集群、工程实践与避坑(含 Node.js/Python 示例)
面向对象:后端/全栈/数据工程开发者
阅读收益:
- 彻底搞清 Redis 的数据结构与复杂度
- 学会缓存设计(击穿/穿透/雪崩防护)、限流、分布式锁、消息/队列等常见方案
- 能按场景选择RDB/AOF、内存淘汰策略,并完成复制/哨兵/集群部署
- 拿走可运行的 Docker、一键命令、Lua/代码模板
文章目录
- Redis 从入门到生产:数据结构、持久化、集群、工程实践与避坑(含 Node.js/Python 示例)
- @[toc]
- 0. Redis 是什么&何时使用/避免
- 1. 五分钟起步(Docker + CLI)
- 2. 数据结构与高频操作(含复杂度)
- 3. 缓存设计:从入门到防雪崩
- 3.1 三种模型
- 3.2 三大故障与防护
- 4. 限流与计数(Lua 原子方案)
- 5. 分布式锁(含可靠解锁)
- 6. 队列与延时任务
- 6.1 可靠队列(List 版)
- 6.2 延时任务(ZSet)
- 7. 持久化:RDB vs AOF(怎么选)
- 8. 内存与淘汰策略
- 9. 复制、哨兵与集群(HA/扩展)
- 9.1 主从复制(Replication)
- 9.2 哨兵(Sentinel,高可用自动故障转移)
- 9.3 集群(Cluster,水平扩展)
- 10. 事务、原子性与 Lua
- 11. Pipeline 与性能
- 12. 安全与 ACL
- 13. 真实落地案例配方
- 13.1 排行榜(ZSet)
- 13.2 会话/验证码(String + TTL)
- 13.3 统计 UV(HyperLogLog)
- 13.4 附近的店(Geo)
- 13.5 订单状态流(Stream + 消费组)
- 14. 客户端示例
- 14.1 Node.js(ioredis)
- 14.2 Python(redis-py)
- 15. 运维排错清单(可直接对照)
- 16. 推荐 key 规范与配置模板
- 17. 常见误区
- 18. 结语
文章目录
- Redis 从入门到生产:数据结构、持久化、集群、工程实践与避坑(含 Node.js/Python 示例)
- @[toc]
- 0. Redis 是什么&何时使用/避免
- 1. 五分钟起步(Docker + CLI)
- 2. 数据结构与高频操作(含复杂度)
- 3. 缓存设计:从入门到防雪崩
- 3.1 三种模型
- 3.2 三大故障与防护
- 4. 限流与计数(Lua 原子方案)
- 5. 分布式锁(含可靠解锁)
- 6. 队列与延时任务
- 6.1 可靠队列(List 版)
- 6.2 延时任务(ZSet)
- 7. 持久化:RDB vs AOF(怎么选)
- 8. 内存与淘汰策略
- 9. 复制、哨兵与集群(HA/扩展)
- 9.1 主从复制(Replication)
- 9.2 哨兵(Sentinel,高可用自动故障转移)
- 9.3 集群(Cluster,水平扩展)
- 10. 事务、原子性与 Lua
- 11. Pipeline 与性能
- 12. 安全与 ACL
- 13. 真实落地案例配方
- 13.1 排行榜(ZSet)
- 13.2 会话/验证码(String + TTL)
- 13.3 统计 UV(HyperLogLog)
- 13.4 附近的店(Geo)
- 13.5 订单状态流(Stream + 消费组)
- 14. 客户端示例
- 14.1 Node.js(ioredis)
- 14.2 Python(redis-py)
- 15. 运维排错清单(可直接对照)
- 16. 推荐 key 规范与配置模板
- 17. 常见误区
- 18. 结语
0. Redis 是什么&何时使用/避免
- 内存型键值数据库,单线程(命令执行)+ 多 I/O 线程,支持持久化。
- 优势:超低延迟(µs 级)、多样数据结构、原子操作、生态广。
- 典型用途:缓存、会话、计数器/限流、排行榜、消息队列、地理/位图、流数据。
- 避免:强一致事务、长查询/大排序、超大值(>几十 MB)、把 Redis 当“持久数据库”的唯一来源(需权衡)。
1. 五分钟起步(Docker + CLI)
docker run -d --name redis -p 6379:6379 redis:7
docker exec -it redis redis-cli
常用命令速览:
SET k v EX 60 NX # 只在不存在时设置,并设置60s过期(缓存/锁常用)
GET k
DEL k
EXPIRE k 3600
TTL k
2. 数据结构与高频操作(含复杂度)
类型 | 典型场景 | 主要命令(复杂度) | 备注 |
---|---|---|---|
String | 缓存、计数器、分布式锁 | GET/SET O(1), INCR O(1), MGET/MSET O(n) | 值最大 512MB |
Hash | 对象/配置 | HSET/HGET/HMGET O(1), HGETALL O(n) | 字段多时考虑 HSCAN |
List | 消息队列、时间线 | LPUSH/RPOP/BRPOP O(1), LRANGE O(n) | BRPOP 支持阻塞 |
Set | 去重集合 | SADD/SISMEMBER O(1), SINTER O(n) | 交并差成本随元素数 |
ZSet | 排行榜/延时队列 | ZADD O(logN), ZRANGE/BYSCORE O(logN+M) | 分数排序 |
Bitmap | 开关/签到、百亿级布尔 | SETBIT/GETBIT/ BITCOUNT | 超省内存 |
HyperLogLog | UV 估算 | PFADD/PFCOUNT | 误差≈0.81% |
Geo | 附近的人/店 | GEOADD/GEORADIUS | 基于 ZSet |
Stream | 日志/流水、消费组 | XADD/XREADGROUP/XACK | 轻量消息队列 |
生产提示:不要在生产用
KEYS *
(阻塞),请用SCAN
系列。
3. 缓存设计:从入门到防雪崩
3.1 三种模型
- Cache-Aside(旁路缓存):应用先读缓存,Miss 则查库并写缓存(最常用)。
- Write-Through:写请求同时写缓存和数据库(延迟更高,数据一致性更好)。
- Write-Behind:先写缓存,再异步写库(风险大,需队列与持久化保障)。
Node.js/ioredis 旁路缓存示例
// pnpm add ioredis
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)
const getUser = async (id) => {const key = `user:${id}`const hit = await redis.get(key)if (hit) return JSON.parse(hit)const row = await db.users.findByPk(id) // 你的DB查询if (row) await redis.set(key, JSON.stringify(row), 'EX', 3600 + Math.floor(Math.random()*600)) // 加抖动防雪崩return row
}
3.2 三大故障与防护
- 缓存穿透(查不存在的 key 打到数据库):
- 负缓存(null 也缓存短 TTL),Bloom Filter(如
redis-bloom
模块)。
- 负缓存(null 也缓存短 TTL),Bloom Filter(如
- 缓存击穿(热点 key 过期瞬间大量并发打到 DB):
- 互斥锁/单飞(set NX 锁)、早到期续约、限流。
- 缓存雪崩(大量 key 同时过期/机器宕机):
- TTL 加抖动、分区部署、多级缓存(如本地 Caffeine + Redis)。
4. 限流与计数(Lua 原子方案)
固定窗口计数器(每分钟 100 次)
-- rate_limit.lua
-- KEYS[1]=key ARGV[1]=limit ARGV[2]=windowSec
local c = redis.call('INCR', KEYS[1])
if c == 1 then redis.call('EXPIRE', KEYS[1], ARGV[2]) end
if c > tonumber(ARGV[1]) then return 0 else return 1 end
调用(Node.js):
const script = fs.readFileSync('rate_limit.lua','utf8')
const sha = await redis.script('load', script)
const ok = await redis.evalsha(sha, 1, `rl:${userId}`, 100, 60)
if (ok === 0) throw new Error('Too Many Requests')
需要平滑限流(滑动窗口/令牌桶),可用 ZSet/Lua 实现,更精确但稍复杂。
5. 分布式锁(含可靠解锁)
核心要点:随机 value + 过期时间;解锁时比对 value;超时自动释放。
Lua 解锁脚本(原子)
-- unlock.lua KEYS[1]=lock_key ARGV[1]=expected_value
if redis.call('GET', KEYS[1]) == ARGV[1] thenreturn redis.call('DEL', KEYS[1])
elsereturn 0
end
Node.js 上锁/解锁
const lockKey = `lock:order:${orderId}`
const token = crypto.randomUUID()
const ttl = 10 // 秒
const locked = await redis.set(lockKey, token, 'NX', 'EX', ttl)
if (!locked) throw new Error('busy')
// ...业务逻辑...
await redis.eval(fs.readFileSync('unlock.lua','utf8'), 1, lockKey, token)
多副本场景可用 Redlock(多节点多数派);请设置合理 TTL 并处理业务超时与重入。
6. 队列与延时任务
6.1 可靠队列(List 版)
- 生产:
LPUSH queue job
- 消费:
BRPOPLPUSH queue processing 5
→ 处理成功后LREM processing job 1
,失败可回滚。
更现代的选择:Streams + 消费组(
XGROUP
/XREADGROUP
/XACK
),支持多消费者与未确认重投。
6.2 延时任务(ZSet)
ZADD delayQ <ts> job
ZRANGEBYSCORE delayQ -inf now LIMIT 0 100 -> 取到期任务
ZREM delayQ job -> 删除即“消费”
7. 持久化:RDB vs AOF(怎么选)
- RDB:快照式,
BGSAVE
;重启恢复快,占用小;可能丢失最近快照后的数据。 - AOF:追加式日志,支持
appendfsync always/everysec/no
;更安全但日志更大,需重写(BGREWRITEAOF
)。 - 组合推荐:
AOF everysec + RDB 定时
(Redis 7 默认 AOF 使用 RDB 前导,恢复更快)。
最小配置片段(redis.conf)
save 900 1
save 300 10
save 60 10000appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
8. 内存与淘汰策略
maxmemory
设置上限,否则 OOM。- 策略(maxmemory-policy):
noeviction
(默认,不驱逐,超限写报错)allkeys-lru/lfu/random
(对所有键)volatile-lru/lfu/random
(仅对有 TTL 的键)
- 排查内存:
INFO memory
、MEMORY STATS
、MEMORY DOCTOR
、MEMORY USAGE key
;
大 key:SCAN
+TYPE
+HLEN/LLEN/SCARD/ZCARD/STRLEN
。
建议缓存服务用
allkeys-lru
或allkeys-lfu
,并统一 TTL + 抖动。
9. 复制、哨兵与集群(HA/扩展)
9.1 主从复制(Replication)
- 从库只读;
replicaof host port
;读多写少可读写分离(注意一致性)。
9.2 哨兵(Sentinel,高可用自动故障转移)
- 至少 3 个哨兵实例;应用通过哨兵发现主库。
docker-compose 最小示例(思路):1 主 1 从 + 3 哨兵,哨兵配置sentinel monitor mymaster 10.0.0.1 6379 2
。
9.3 集群(Cluster,水平扩展)
- 16384 槽位(hash slot),按槽位均衡到节点;客户端需支持集群协议。
- 适合数据量大/吞吐高,但多键事务及
keys
/scan
跨槽限制。
创建:redis-cli --cluster create host1:7000 host2:7001 ... --cluster-replicas 1
10. 事务、原子性与 Lua
- MULTI/EXEC:命令排队再原子执行;不支持回滚。
- WATCH:乐观锁(键被他人改动则
EXEC
失败)。 - Lua:在服务器端原子执行复杂逻辑,避免 RTT 和竞态(不要写长耗时脚本)。
11. Pipeline 与性能
- Pipeline:批量发送减少 RTT(客户端合并收包);
- 批量命令:
MGET/MSET
也能减少往返; - 避免慢命令:
SORT
无 limit、HGETALL
/LRANGE 0 -1
大对象、SUNION
大集合等。 - SLOWLOG:找慢命令;
latency doctor
:诊断延迟峰值。
12. 安全与 ACL
- 不要暴露公网;生产开启 ACL 或
requirepass
,并使用 TLS。 acl setuser app on >strongpass ~cache:* +@read +@write -@dangerous
protected-mode yes
;可rename-command FLUSHALL ""
禁用危险命令。
13. 真实落地案例配方
13.1 排行榜(ZSet)
ZINCRBY rank:game 10 user:123
ZREVRANGE rank:game 0 9 WITHSCORES
ZREVRANK rank:game user:123
13.2 会话/验证码(String + TTL)
SET session:token123 user_json EX 3600
GET session:token123 -> 过期自动清理
13.3 统计 UV(HyperLogLog)
PFADD uv:2025-08 user123
PFCOUNT uv:2025-08
13.4 附近的店(Geo)
GEOADD shop:bj 116.39 39.90 s1 116.40 39.91 s2
GEOSEARCH shop:bj FROMLONLAT 116.39 39.90 BYRADIUS 2 km WITHDIST
13.5 订单状态流(Stream + 消费组)
XADD order:stream * orderId 1001 status CREATED
XGROUP CREATE order:stream g1 $ MKSTREAM
XREADGROUP GROUP g1 c1 BLOCK 0 STREAMS order:stream >
XACK order:stream g1 id-xxx
14. 客户端示例
14.1 Node.js(ioredis)
import Redis from 'ioredis'
const r = new Redis('redis://:password@127.0.0.1:6379')
await r.set('hello','world','EX',60)
console.log(await r.get('hello'))
await r.quit()
14.2 Python(redis-py)
import redis
r = redis.Redis(host='127.0.0.1', port=6379, password=None, decode_responses=True)
r.set('hello','world', ex=60)
print(r.get('hello'))
15. 运维排错清单(可直接对照)
- 连接不上:检查防火墙/密码/ACL,
redis-cli -h -p -a
; - CPU 飙高:大 key/慢命令,
SLOWLOG GET
、MONITOR
(仅临时); - 内存异常增长:
MEMORY STATS/USAGE
、查大 key 与未设置 TTL 的缓存; - 延迟抖动:AOF
everysec
的 fsync 峰值、后台重写/快照、网络队首阻塞; - 主从延迟大:网络/命令量;避免大批量阻塞命令;
- 集群槽位不均衡:
redis-cli --cluster rebalance
; - 意外全量复制:大 RDB 传输卡住,优先保证磁盘/网络,尽量避免频繁重启。
16. 推荐 key 规范与配置模板
- 命名:
<业务>:<实体>:<主键>[:<字段>]
,如user:123
、cart:uid:77
; - 统一 TTL 与抖动:
EX base + rand(0..base*0.2)
; - 配置片段:
# redis.conf 建议
maxmemory 4gb
maxmemory-policy allkeys-lfu
appendonly yes
appendfsync everysec
save 900 1
save 300 10
save 60 10000
17. 常见误区
- 只依赖 Redis 保存关键业务数据而没有备份/持久化策略
- 大量使用
HGETALL/LRANGE 0 -1
读整对象 → 应改为精确字段或分页 - 认为 Redis 是“真正单线程” → 6.0+ 默认 I/O 多线程,命令执行仍单线程
- Pipeline 能“并行执行” → 只是减少 RTT,命令仍顺序执行
- Redlock 等于绝对安全 → 需要合理 TTL / 容错与主从一致性考量
18. 结语
Redis 强在数据结构 + 原子指令带来的工程效率。只要你把握缓存策略、内存与持久化、高可用与避坑清单,它可以优雅地承载从缓存到队列、从统计到限流的大多数高并发场景。
如果你需要,我可以把本文的示例脚本(限流/锁/延时队列)+ Docker Compose + Node/Python Demo打包成一个 CSDN 下载资源,或者按你的业务改写一套“即插即用”的封装库。