Redis 很快,快到让人产生错觉:装完就能高枕无忧。现实是,Redis 既是高性能引擎,也是对使用方式极度敏感的系统组件。一些看似默认安全的配置,在高并发或大数据量场景下,足以把业务拖入故障。
下面这三个问题,都是真实生产环境中高频且代价高昂的陷阱。
坑一:RDB 快照触发的写入冻结
很多实例在运行一段时间后,突然出现写入失败:
MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk
第一反应通常是查磁盘空间,但多数时候空间并不是真正原因。关键在于这条默认配置:
stop-writes-on-bgsave-error yes
背后的机制
当 Redis 执行 RDB 快照(BGSAVE)失败时,只要该选项为 yes,实例会拒绝所有写操作,进入近似只读状态,以保护数据一致性。
失败原因可能包括:
- fork 子进程开销过大(大内存实例常见)
- 磁盘 I/O 抖动
- 权限或文件系统问题
- 后台保存超时
在纯缓存场景中,这种保护策略往往过于激进——缓存写入被冻结,等价于业务被“软熔断”。
何时需要调整
如果 Redis 仅承担缓存职责,且数据可丢失,可以考虑:
stop-writes-on-bgsave-error no
更进一步,若完全不需要持久化:
save ""
直接关闭 RDB 快照,避免 fork 与 I/O 干扰。
核心判断标准:Redis 是缓存,还是数据库。两者容忍度完全不同。
坑二:maxmemory 与 noeviction 组合的隐性埋雷
一个典型故障链路:
- Redis 内存到达上限
- 写入请求被拒绝
- 应用抛出异常
错误信息非常直接:
OOM command not allowed when used memory > 'maxmemory'
问题根源
许多实例的默认策略是:
maxmemory-policy noeviction
含义很简单:内存满了也不淘汰,直接拒绝写入。
这对缓存系统而言是危险的:缓存的职责是“让路”,而不是“锁门”。
推荐策略
绝大多数缓存型业务,更合理的选择是:
maxmemory-policy allkeys-lru
或:
maxmemory-policy volatile-lru
区别在于:
| 策略 | 行为 |
|---|---|
| allkeys-lru | 所有 key 都参与 LRU 淘汰 |
| volatile-lru | 仅设置了 TTL 的 key 参与淘汰 |
如果 Redis 承担的是通用缓存,allkeys-lru 通常更符合预期。
Redis 内存耗尽本身不是事故,拒绝写入才是事故。
坑三:KEYS * —— 单线程模型下的阻塞
这个问题更偏向使用层,但可以通过配置规避人为风险。
Redis 是单线程执行模型,KEYS 命令时间复杂度为 **O(N)**。 执行期间,实例无法处理其他请求。
在百万级以上 key 数量时:
- 登录延迟陡增
- 请求排队
- 健康检查失败
- 实例被负载均衡摘除
这类“瞬时雪崩”在生产中非常常见。
正确替代方案
使用 SCAN:
SCAN cursor MATCH pattern COUNT n
SCAN 的本质是游标式迭代,分批返回结果,避免长时间阻塞。
从源头杜绝误操作
可在配置中直接禁用高危命令:
rename-command KEYS ""
这样误敲命令时,Redis 会返回:
ERR unknown command
这是生产环境中非常实用的“防呆设计”。
默认配置并非性能最优
Redis 的默认策略更偏向:
- 数据安全
- 持久化可靠性
- 通用兼容性
而非:
- 极限吞吐
- 低延迟缓存场景
因此,高流量系统必须显式审视关键配置,而不是依赖默认值。
建议在生产实例上定期检查:
CONFIG GET stop-writes-on-bgsave-error
CONFIG GET maxmemory-policy
如果看到:
stop-writes-on-bgsave-error yesmaxmemory-policy noeviction
需要结合业务属性重新评估风险。
结语
Redis 的性能问题,很多时候不是“Redis 不够快”,而是行为模型与业务预期不匹配:
- 持久化策略影响 fork 与 I/O
- 内存策略影响系统稳定性
- 命令选择影响整体延迟
真正稳定的 Redis 系统,依赖的不是更大的机器,而是更清醒的配置决策。