云上分布式缓存选型与设计:Redis、Memcached还是自建?

去年一个客户半夜被拉起来,数据库CPU跑满,数据库连接数打爆。查了半天不是慢查询,是缓存穿透。某个热点key被恶意攻击,每秒请求几万次,缓存里没有,全怼到数据库。数据库直接崩了。
他们用了Redis,配置简单,命中率也不错。但没有防穿透设计,攻击一来,缓存成了摆设。
这是很多团队对缓存的误解:以为装上缓存就能扛高并发,但不懂策略,装了也白装。
今天聊聊分布式缓存。不是那种“缓存很重要”的废话,而是帮你理清楚:Redis和Memcached怎么选?怎么部署?缓存穿透、击穿、雪崩怎么防?缓存和数据库数据不一致怎么办?
01 Redis vs Memcached:不是谁强,是谁合适
Redis和Memcached都是内存KV存储,但设计哲学不同。
Redis
数据结构丰富:String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog等
支持持久化:RDB快照 + AOF日志,重启不丢数据
单线程模型(6.0后引入多线程IO,但核心执行仍是单线程),避免并发复杂性
支持主从、哨兵、集群,高可用方案成熟
Memcached
简单的KV存储,值只能是字符串
不支持持久化,重启数据全丢
多线程模型,纯KV场景性能极高
集群靠客户端一致性哈希实现,服务端无状态
选型建议:
需要复杂数据结构(排行榜、去重、消息队列)→ Redis
需要持久化或高可用(数据不能丢)→ Redis
纯KV、超大并发、可以丢数据(如会话缓存)→ Memcached
云托管实例 → 两者都有托管版,运维成本低
02 部署形态:单机到集群怎么选
单机:开发测试用,生产不建议。挂了全丢。
主从:读多写少场景。主挂了,从可顶,但需要人工或哨兵干预。
哨兵:主从自动切换,高可用。适合对数据一致性要求不太苛刻的场景。
集群:数据分片,水平扩展。适合数据量大、写入高的场景。客户端需支持集群协议。
云托管:云厂商提供Redis/Memcached托管服务,自动高可用、备份、扩缩容。运维成本最低。
选型建议:生产环境至少主从+哨兵,数据量大直接上集群。预算允许就托管。
03 三大缓存坑:穿透、击穿、雪崩
穿透:查询不存在的数据,缓存没有,数据库也没有。每次请求穿透缓存到数据库,可能被恶意攻击。
解法:
缓存空值(TTL短)
布隆过滤器:在缓存前加一层过滤器,过滤掉不存在的key
击穿:一个热点key过期,大量请求同时打到数据库。
解法:
热点key永不过期(逻辑更新)
互斥锁:第一个请求查数据库后写缓存,其他请求等待
雪崩:大量key同时过期或Redis宕机,请求全到数据库,数据库被压垮。
解法:
TTL加随机偏移量,避免同时过期
集群+主从+哨兵,防止单点故障
限流、熔断、降级
那家客户没设防穿透,攻击者查不存在的用户ID,每次都打数据库。加了布隆过滤器后,不存在的key被拦截,数据库恢复。
04 缓存一致性:数据库和缓存怎么同步
缓存和数据库双写,必然面临一致性问题。
方案一:先更新数据库,再删除缓存(Cache Aside) 最常用。
读:先读缓存,没有就读数据库,写缓存
写:更新数据库,删除缓存
问题:删除缓存失败怎么办?
解法:消息队列重试 + 最终一致性
方案二:先删缓存,再更新数据库
问题:删除缓存后、更新数据库前,有读请求进来,缓存没有,读到旧数据写回缓存。
解法:延迟双删(先删缓存、更新数据库、sleep几毫秒再删一次)。复杂,不推荐。
方案三:订阅数据库变更日志(CDC)
Canal、Debezium监听binlog,异步更新缓存。解耦,延迟低,推荐对一致性要求高的场景。
05 监控什么
缓存出问题往往是突发的,监控要在事前。
命中率:低于80%需要排查。可能缓存容量不够、过期策略不对、大量未命中穿透。
内存使用率:接近上限触发逐出,可能导致命中率下降。
逐出键数:逐出多说明容量不够,或过期策略设太短。
慢查询:Redis单线程,慢查询会阻塞所有请求。KEYS、HGETALL等O(N)命令要避免。
连接数:接近上限说明应用配置问题或连接泄漏。
06 一个真实案例
某电商大促前压测,Redis命中率只有60%。查日志发现大量请求查询“商品详情”,但key的TTL设了1小时,商品数据更新频繁,缓存经常失效。
我们做了几件事:
热点商品TTL从1小时改到24小时,数据变更时主动删除缓存
对不存在的商品ID请求,用布隆过滤器拦截
增加Redis集群节点,内存从16GB扩到64GB
优化查询,把重复的KEYS命令改成SCAN
优化后,命中率从60%升到92%,数据库CPU下降40%,大促平稳度过。
运维负责人说:“以前以为缓存就是装上就能用,现在知道,装对只是开始,用对才是关键。”
写在最后
分布式缓存选型,技术参数容易查,难的是用对策略。
那位客户的运维负责人后来总结:“Redis还是Memcached,问自己两个问题:需要复杂数据结构吗?需要持久化吗?其余的,防穿透、防击穿、防雪崩,一个都不能少。”
命中率不到80%,先排查再扩容。热点key永不过期+逻辑更新。布隆过滤器挡掉恶意穿透。双写一致性用CDC或消息队列。
你的缓存,现在是在帮数据库扛压,还是让它白忙?