新手必踩 Redis 10 个低级坑

📅 2026/6/23 10:17:32
新手必踩 Redis 10 个低级坑
新手必踩 Redis 10 个低级坑过期时间、KEYS 命令、持久化误区Redis 用起来简单踩起坑来要命。很多人以为自己会用 Redis其实只是在会用和会坑自己之间反复横跳。以下这 10 个坑每一个都是生产环境用真金白银换来的教训。一、过期时间看似简单处处是陷阱坑1SET/DEL 会清除过期时间INCR 不会这是最容易混淆的点。bashset mykey hello ex 300 # 过期时间 300s ttl mykey # 294 set mykey world # 覆盖了过期时间被清除 ttl mykey # -1永不过期但用INCR、LPUSH、HSET修改值时过期时间不会被清除bashset counter 1 ex 300 incr counter # 值变了过期时间还在 ttl counter # 依然在倒计时记住这个规则覆盖整个 value 的命令SET/DEL/GETSET/PERSIST会清过期时间局部修改的命令INCR/LPUSH/HSET不会。坑2EXPIRE 设置负数 直接删除bashexpire mykey -1 # key 立即被删除 expireat mykey 10000 # 时间戳是过去的key 也被删除这不是 bug是特性。但很多人在代码里算错了时间戳结果 key 莫名消失排查半天找不到原因。坑3RENAME 会继承过期时间bashset key_a a ex 300 set key_b b ex 600 rename key_a key_b # key_b 现在继承 key_a 的 300s 过期时间老 key 的过期时间会搬家到新 key 上不管新 key 原本有没有过期时间。这个特性很少有人注意但在数据迁移场景下可能引发意外。坑4所有 key 同时过期 → 缓存雪崩系统初始化时批量写入缓存所有 key 的 TTL 都设成 3600 秒。到点集体失效所有请求瞬间穿透到数据库连接池直接打满。解法加随机偏移。pythonimport random ttl 3600 random.randint(-300, 300) # ±5分钟随机 r.set(key, value, exttl)别小看这几分钟的随机值它能把集中爆破变成均匀释放。坑5Key 忘设 TTL → 内存黑洞代码里set user:1001 json写得飞起就是不带EX。半年后 Redis 内存从 2G 涨到 16GINFO keyspace显示 key 数量不断增加但业务说数据量没变——全是没过期的僵尸 key。写缓存时养成习惯SET 必须带 EX。bashSET user:1001 {name:Alice} EX 3600批量排查无 TTL 的 keybashredis-cli --scan --pattern * | while read key; do ttl$(redis-cli TTL $key) [ $ttl -1 ] echo 永不过期: $key done二、KEYS 命令生产环境的定时炸弹坑6KEYS * 能把生产打挂Redis 是单线程的。KEYS *是 O(N) 阻塞命令会从头到尾扫描所有 key期间所有其他请求全部排队。数据量小时感觉不到百万级 key 时一次KEYS *可能阻塞 Redis 数秒。这数秒内支付超时、订单失败、监控报警一连串雪崩。有真实案例运营同学执行KEYS coupon:*800 万 key阻塞 680ms直接导致支付接口全线超时活动被迫暂停。替代方案SCAN。bashSCAN 0 MATCH user:* COUNT 100 # 返回游标 一批 key下次用游标继续非阻塞Python 完整遍历pythonimport redis r redis.Redis(host127.0.0.1, passwordyourpassword, decode_responsesTrue) cursor 0 while True: cursor, keys r.scan(cursorcursor, matchuser:*, count100) for key in keys: print(key) if cursor 0: break危险命令危险原因安全替代KEYS *O(N) 阻塞全量遍历SCANHGETALL大 Hash 全量读取HSCANSMEMBERS大 Set 全量读取SSCANLRANGE key 0 -1大 List 全量读取分页 LRANGEFLUSHALL阻塞删除所有 keyFLUSHALL ASYNCRedis 4.0生产建议在 redis.conf 里直接禁用危险命令。rename-command KEYS rename-command FLUSHALL rename-command FLUSHDB rename-command CONFIG 三、持久化RDB 和 AOF 的经典误区坑7只用 RDB重启丢几分钟数据RDB 是定时快照默认触发规则是save 3600 1 # 1小时内1次写操作 save 300 100 # 5分钟内100次写操作 save 60 10000 # 1分钟内10000次写操作崩溃发生在两次快照之间这段时间的数据直接丢失。而且 RDB 文件可能因磁盘故障损坏没有备份就彻底完蛋。解法必须开 AOF或者用混合持久化Redis 4.0 推荐。bashappendonly yes appendfsync everysec # 推荐每秒同步最多丢1秒 aof-use-rdb-preamble yes # 混合持久化重启更快坑8AOF 文件无限膨胀磁盘写满AOF 记录每一条写命令同一个 key 改 1000 次就记 1000 条。时间一长文件可能几十 GB但实际有效数据只有几百 MB。解法配置自动 rewrite。bashauto-aof-rewrite-percentage 100 # 比上次大100%时触发 auto-aof-rewrite-min-size 64mb # 至少64MB才触发紧急手动触发redis-cli BGREWRITEAOF坑9BGSAVE 失败RDB 静默停止INFO persistence显示rdb_last_bgsave_status:err rdb_last_bgsave_error:Cant save in background: fork: Cannot allocate memoryRedis 执行 BGSAVE 时需要 fork 子进程而 Linux 默认overcommit_memory0内存高时拒绝 fork导致快照失败、持久化静默停止——你以为数据安全其实早就不存了。解法Redis 官方推荐配置。bashecho 1 /proc/sys/vm/overcommit_memory # 临时生效 echo vm.overcommit_memory 1 /etc/sysctl.conf # 永久生效 sysctl -p坑10不设置密码 暴露公网 等着被清库没有密码的 Redis 一旦被扫描到轻则被清空数据库重则被植入挖矿程序。这不是假设是每天都在发生的事。最低配置bashrequirepass yourStrongPassword bind 127.0.0.1 ::1 # 只允许本地访问生产环境还要考虑 SSL/TLS 加密、防火墙白名单、定期更新补丁。总结一张表记住所有坑编号坑一句话解法1SET 清过期时间INCR 不清区分覆盖和局部修改2EXPIRE 负数 删 key算准时间戳3RENAME 继承过期时间迁移时注意4统一 TTL → 雪崩加随机偏移5忘设 TTL → 内存爆炸SET 必带 EX6KEYS * 打挂生产用 SCAN 替代7只用 RDB 丢数据开 AOF 或混合持久化8AOF 无限膨胀配置 auto-rewrite9BGSAVE 静默失败设置 overcommit_memory110无密码 公网暴露必须设密码 绑定内网Redis 不难难的是知道哪些简单操作背后藏着炸弹。把这 10 个坑刻进肌肉记忆里比多背 100 个命令有用得多。