Redis FAQ
核心原因:
- 纯内存操作:访问内存约 100ns,无磁盘延迟
- 单线程 + IO 多路复用:避免上下文切换
- 高效数据结构:dict、skiplist、ziplist、intset、quicklist
- 优化的网络模型:epoll/kqueue + 非阻塞 IO
一句话总结:
Redis 是基于内存 + 单线程 + 高效数据结构的高性能 KV 数据库。
因为 Redis 的瓶颈不是 CPU,而是内存和网络 IO。
单线程好处:
- 无锁竞争,避免死锁
- 代码实现简单稳定
- 性能足够(普通业务难压满一核)
Redis 内部使用多线程(后台线程)处理:
- AOF rewrite
- RDB save
- 清理大 key
- 网络 I/O(6.0+)
| 类型 | 用途 |
|---|---|
| String | 缓存、计数器、Session |
| Hash | 用户信息、对象 |
| List | 队列、日志流 |
| Set | 去重、标签、好友 |
| ZSet | 排行榜、延时队列 |
| Bitmap | 签到、在线统计 |
| HyperLogLog | 大规模 UV 去重 |
| Stream | 消息队列(Kafka-like) |
两种:
- 惰性删除(lazy delete) -> key 到期后访问才删除
- 定期删除(active) -> 每隔一段时间随机抽查 key
最终由 内存淘汰机制 收尾。
| 策略 | 说明 |
|---|---|
| noeviction | 不再写入 |
| volatile-lru | 只从有过期的 key 中删除最近最少使用 |
| allkeys-lru | 所有 key 中删除 LRU |
| volatile-lfu | 有过期 key 中删除最不常用 |
| allkeys-lfu | 所有 key 中删除最不常用 |
| volatile-ttl | TTL 越短越优先删除 |
| volatile-random / allkeys-random | 随机删 |
企业最常用:
allkeys-lru 或 allkeys-lfu
RDB(快照)
- 间隔存储内存快照到磁盘
- 恢复快,但可能丢失几分钟数据
AOF(日志)
- 每次写操作都记录
- 几乎不丢数据(everysec)
- 文件大,需要 rewrite
通常采用:
RDB + AOF 混合持久化
是原子性的,但 Redis 事务特点:
- 命令依次执行
- 不支持回滚
- 支持 WATCH 实现乐观锁
标准写法(原子):
SET lock_key uuid NX EX 10
删除锁(用 Lua 原子判断):
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
策略:
- 缓存空值(null 缓存)
- 布隆过滤器
- 参数校验(ID <= 0 直接拒绝)
多 key 同时失效 或 Redis 宕机。
解决:
- TTL 加随机值(避免同时失效)
- 多级缓存(Redis + 本地缓存)
- Redis 集群高可用
- 热点数据不过期(逻辑过期)
某热点 key 突然失效。
解决:
- 加互斥锁(分布式锁)
- 热点 key “永不过期” + 后台更新
- 逻辑过期方案
大 key = 单体太大
问题:
- 删除慢(阻塞)
- 迁移慢
- 网络传输慢
- 内存膨胀
解决:
- 拆成多个小 key
- 使用 SCAN 遍历 big hash
- 异步删除(unlink)
三种:
- 主从(Replication) -> 不自动故障转移
- 哨兵 Sentinel -> 自动 Failover
- Cluster -> 分片 + 高可用
企业使用:
Redis Cluster 3 主 3 从 + 哨兵模式备份
因为多 DB 会破坏槽位 hash 规则,并产生跨 slot 事务一致性问题。
必须保证:
所有 key 的 hash slot 一致
否则报错:
CROSSSLOT Keys in request don't hash to the same slot
解决:
使用 hash tag:
user:{1}:name
user:{1}:age
两个阶段:
- 全量同步:发送 RDB 文件
- 增量同步:发送 replication buffer
psync2(2.8+)支持断点续传。
不是。
惰性删除 + 定期扫描 -> 不是精确时刻删除
但一定在访问或扫描时最终删除掉。
把多个命令一次发送,减少 RTT(网络往返)
性能提升巨大。
跳表主要用于 ZSet(sorted set)。
为什么不用红黑树?
- 跳表更容易实现
- 支持区间扫描更高效
- 跳表在范围查询、插入性能接近 O(log N)
三部分:
- 惰性删除(访问才删)
- 定期删除(定时任务抽样删除)
- 淘汰策略(内存不足时触发)
优化内存:
- 小 Hash -> ziplist(连续内存、更省空间)
- 大 Hash -> hashtable(性能更好)
Redis 会自动在二者间转换。
为了保持 hash table 的 O(1) 性能。
采用:
渐进式 rehash(incremental rehash)
不是一次性移动,而是每次操作迁移一部分元素,避免阻塞。
Lua:
UNLINK key
异步删除 key,避免主线程阻塞(特别是大 key)。
原因:
主要为了优化 网络 I/O,不是执行命令。
多线程结构:
- I/O 多线程负责读写网络数据
- 命令解析 & 执行仍然单线程
解决网络瓶颈问题。
两个办法:
1. key 做随机散列,例如:
user:123 -> user:123:rand
2. 使用预分片(分桶)
bucket:001:user:123
bucket:002:user:888
哨兵规则:
- TILT 模式
- min-slaves-max-lag
- down-after-milliseconds
严格保证只有一个 MASTER。
原因:
- Redis 内存贵
- 大 key 会导致阻塞
- AOF/RDB 迁移成本高
- 影响网络传输
最佳实践:
Redis 存热点、小对象、结构化数据,不存大文件。
| 特性 | Redis Stream | Kafka |
|---|---|---|
| 存储 | 内存为主,可落盘 | 磁盘 |
| 延迟 | 超低(<1ms) | 较低(<10ms) |
| 消费模型 | 消费组 + ack | 消费组 + offset |
| 规模 | 小规模业务 | 大流量日志/消息队列 |
结论:
Redis Stream 用于轻量消息队列,不替代 Kafka。
核心:
- 多个 hash 函数
- 一个位数组
- 存在小概率误判 false positive(误判存在)
优点:
超省内存,可过滤不存在的数据。
- key 命名规范
- 热点缓存永不过期 + 逻辑过期
- 多级缓存(本地 + Redis)
- 防穿透、防雪崩、防击穿
- Redis Cluster
- 读写隔离
- Lazy Preload(提前预热)
- 大 key 拆分
- 批处理 + pipeline
- 限流 + 分布式锁