Redis 常被一句话概括为「内存里的键值数据库」,但真要用好它,需要回答三个问题:该用哪种数据结构存?缓存出问题时系统会怎样?重启后数据还在不在?本文按这个顺序展开。
五种核心结构与适用场景
Redis 的 value 不只是字符串。选对结构,很多看似复杂的逻辑一条命令就能解决:
| 结构 | 特征 | 典型场景 |
|---|---|---|
| String | 最基本,可存文本、数字、序列化数据 | 缓存对象、计数器(INCR)、分布式锁(SET NX PX) |
| Hash | 字段-值对的集合 | 存对象的部分字段,避免整取整存 |
| List | 有序、可两端操作 | 简单消息队列(LPUSH + BRPOP)、最新 N 条记录 |
| Set | 无序去重、支持交并差 | 标签、共同好友(SINTER)、抽奖去重 |
| Sorted Set | 每个成员带分数、按分排序 | 排行榜(ZADD/ZREVRANGE)、延迟队列 |
以排行榜为例,用 MySQL 实现需要 ORDER BY score DESC LIMIT 10 扫索引;用 Sorted Set 只需要:
ZADD rank 2580 "player:1001"ZREVRANGE rank 0 9 WITHSCORES # 前十名缓存三大经典问题
把 Redis 放在数据库前面挡流量,是标准用法;但缓存层本身也会引入新的故障模式。这三个问题名字相似,成因和对策完全不同。
缓存穿透:查一个根本不存在的数据
请求恶意或异常地查询不存在的 key,缓存必然不命中,每次都打到数据库。
对策:
- 缓存空值:查不到也把「空结果」缓存起来,设较短 TTL(如 60 秒)。
- 布隆过滤器:在缓存前加一层概率型过滤器,「一定不存在」的请求直接拦截。代价是极小概率误判(存在的 key 被判不存在)和不支持删除。
缓存击穿:热点 key 刚好过期
某个被百万并发访问的热点 key 过期瞬间,所有请求同时涌向数据库。
对策:
- 互斥重建:第一个发现缓存失效的请求先拿分布式锁(
SET lock NX PX 3000),由它查库回填,其余请求短暂等待后读缓存。 - 逻辑过期:key 不物理过期,value 里存一个逻辑过期时间;发现过期后由后台线程异步更新,前台先返回旧值。
缓存雪崩:大批 key 同时过期
通常是两个原因:大量 key 设了相同 TTL,或 Redis 实例整体宕机。
对策:
- TTL 加随机抖动:
3600 + random(600)秒,打散过期时间点。 - 高可用部署:主从 + Sentinel 或 Cluster,避免单点宕机演变成全量回源。
- 多级缓存与降级:本地缓存(如 Caffeine)挡一层;缓存不可用时对非核心请求直接降级返回默认值,保住数据库。
持久化:RDB 与 AOF
纯内存意味着重启即清空。Redis 提供两种把数据落盘的方式,可以单独用也可以同时开。
RDB(快照):按配置的时间间隔(如 save 900 1 表示 900 秒内至少 1 次修改)fork 子进程把全量数据写成二进制文件 dump.rdb。
- 优点:文件紧凑、恢复速度快,适合做备份和灾难恢复。
- 缺点:两次快照之间的修改在宕机时会丢失;数据量大时 fork 可能带来毫秒级停顿。
AOF(追加日志):把每条写命令追加到 appendonly.aof,配合三种 fsync 策略:
| 策略 | 安全性 | 性能 |
|---|---|---|
appendfsync always | 每命令落盘,几乎不丢 | 最差 |
appendfsync everysec(默认) | 宕机最多丢 1 秒 | 折中,推荐 |
appendfsync no | 交给操作系统,可能丢几十秒 | 最好 |
AOF 文件会持续增长,Redis 通过 AOF rewrite(重写)在后台把它压缩成当前数据的最小命令集;7.0 起改用「主文件 + 增量文件」的多部分 AOF,重写期间更省内存和磁盘。
实际选择:缓存场景可以不持久化;需要数据可靠性时开 AOF everysec,再用 RDB 定时快照做异地备份,两者互补。
使用 Redis 的几条纪律
- 所有缓存 key 都要设 TTL,除非有明确的淘汰策略,否则内存迟早被撑爆。
- 禁止在生产环境用
KEYS *——它 O(N) 全量扫描,会直接阻塞单线程的 Redis。用SCAN分批遍历。 - 大 key 是隐形炸弹:一个几百 MB 的 Hash 会让删除、迁移、RDB 都变慢。拆分成多个小 key。
- 删除用
UNLINK替代DEL(4.0+),把内存释放放到后台线程,避免阻塞。
延伸阅读
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 21 天
部分内容可能已过时