ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis vs Memcached:缓存选型、持久化与分布式锁实战

Redis vs Memcached:缓存选型、持久化与分布式锁实战 很多人一聊到缓存选型张口就是“Redis功能多Memcached性能好、更简单”。我过去几年在好几个项目里来回切换过这两种组件也被问过无数次“到底选哪个”。说实话这个问题的答案在2025年已经和十年前不太一样了。Redis从一个纯粹的缓存演化成了数据结构服务器而Memcached依然坚持着“极简缓存”的路线两者早就不是一个维度的东西了。这篇内容我会把数据结构、持久化、高可用、内存管理、性能模型、分布式玩法全部摊开来讲顺便把大家搜得最多的分布式锁、缓存治理、序列化、哨兵模式与集群模式这些关联知识点也串进去争取一篇看完你心里就有明确答案。1. 为什么到了现在Redis和Memcached依然会被放在一起比1.1 两个项目的出身和设计目标完全不同先说历史。Memcached诞生于2003年当时是为了解决LiveJournal这类动态网站的数据库压力。它的设计目标极其纯粹做一个分布式的内存键值缓存系统把数据库查询结果、页面片段、Session这类数据短期存放在内存里减少数据库的重复访问。它的核心操作就是set、get、delete存储结构简单到不能再简单——就是一个字符串键值对value是字符串或者二进制数据。Redis在2009年出现最初的设计者Salvatore Sanfilippo网名antirez想做的是一个更高级的缓存和存储系统。它的出发点是除了缓存我还想把数据真正地存起来并且希望操作数据的方式能更接近“数据结构”而不是简单的字符串。于是Redis从一开始就支持list、set、hash、zset这些数据类型后来又加入了持久化、复制、事务、Lua脚本、发布订阅等能力慢慢从“缓存”长成了“数据中间件”。这个出身差异决定了今天所有的争议你要是只把它俩当“缓存”用那Memcached完全够用你要是想用缓存做更多的事情——比如排行榜、计数、分布式锁、消息队列——那Memcached根本给不了你这些能力。1.2 热词里那些高频搜索其实都是Redis的场景顺便看看搜索热度就知道大家真正关心什么redis数据类型、redis分布式锁、redis持久化、redis集群、哨兵模式和集群模式的区别、redis缓存治理、redis序列化、redis Desktop Manager、docker安装redis主从。这些关键词里没有一个和Memcached有直接关系。也就是说绝大多数人在真实项目里遇到的痛点——怎么部署Redis、怎么给Redis做高可用、怎么用Redis实现分布式锁、怎么处理缓存穿透和缓存击穿——都是Redis生态里的问题。这不是说Memcached不行而是它根本没有这些能力所以大家也不会去搜它的对应方案。所以说到底这篇文章虽然是做全面对比但“对比”只是切入点落到实际操作层面绝大部分内容都必须围绕Redis展开。因为它的复杂度和能力边界决定了你需要在部署、使用和维护上花更多心思。2. 数据结构Memcached输掉的第一个回合2.1 存储模型的根本差异字符串 vs 五大数据类型Memcached的存储模型可以总结为一句话全世界的key-value都是字符串。value最大默认1MB1.4.x之后支持二进制协议也可以通过编译调整到更大但本质上它不知道value内部长什么样也不会帮你做任何数据处理。你想存一个列表只能在客户端自己做序列化把整个数组变成一个JSON字符串或者二进制串塞进去。你想给某个字段加一也要先get出来改完再set回去——这个操作连“原子性”都谈不上。Redis则完全不同。它内置了String、Hash、List、Set、ZSet五种基础类型以及后续加入的Bitmap位图、HyperLogLog基数统计、GEO地理坐标、Stream消息流、JSON通过模块支持等扩展类型。每一种类型都有配套的操作命令。举个最直观的例子。排行榜这个需求用Redis的ZSet实现只要一行命令ZADD leaderboard 100 user:001然后取前十名ZREVRANGE leaderboard 0 9 WITHSCORES整个过程是原子的、有序的、可排序的还能O(log n)地取任意排名区间。如果换成Memcached你需要在客户端维护整个排行榜数组每次更新都要把整个序列化后的榜单重新写入缓存并发时还要考虑更新顺序做完这一步你就明白了不是Memcached不能用是它根本没有“数据结构”这层语义所有逻辑都被迫推到应用层。2.2 数据操作的原子性为什么INCR会成为分水岭Memcached其实也提供了一个原子操作incr/decr可以对存储的数字字符串做原子增减。很多人在对比时忽略了这一点但它在计数器场景里很有价值。不过它的限制也很明显你只能对一个value做加减不能把这个加减操作和别的数据操作组合起来。Redis同样有INCR、DECR、INCRBY用法几乎一样INCR counter但Redis更强的是它可以把多个操作打包成一个事务或者一段Lua脚本来执行保证整个过程原子完成。Redis分布式锁最主流的实现方式就是利用SET命令的NX和EX参数SET lock_key unique_value NX EX 10这一条命令同时完成了“不存在才设置”和“设置过期时间”两个动作是原子性的。用Memcached实现分布式锁也有社区方案核心思路是用add命令只有key不存在时才存储成功加上过期时间但Memcached的过期时间到达后是惰性删除还是主动清理在分布式锁这种讲究“绝对可靠”的场景里边界情况会把你折磨死。2.3 序列化问题Redis社区为什么有那么多序列化框架搜索词里有“redis序列化”说明这是很多人在实际使用中才遇见的坑。Redis本身存的就是字节流理论上你直接使用String类型往里面塞JSON就行但如果你想把一个对象同时按照它的字段去查询、更新那字符串就做不到了。比如一个用户对象有姓名、年龄、积分三个字段用String可能存整个JSON但用Hash可以分别存三个fieldHSET user:1001 name 张三 age 28 score 500 HINCRBY user:1001 score 10HINCRBY直接对Hash中的某个字段做原子加10而String类型做不到这种字段级别的更新。这也是为什么Spring Data Redis里会有Jackson2JsonRedisSerializer、GenericJackson2JsonRedisSerializer、Kryo、Protostuff这些序列化方案。选择哪种序列化方案本质上取决于你准备用Redis的哪些数据结构。如果是做缓存用StringJSON就足够如果是做数据结构操作那么序列化方案必须支持对象到Hash、Set、ZSet这些类型的映射序列化器选错了后续业务写起来会非常别扭。这里给大家一个亲测稳定的组合建议纯缓存场景value统一用JSON字符串key按业务前缀冒号业务ID来组织序列化器选择StringRedisSerializer不要图省事用JdkSerializationRedisSerializer——那玩意把对象变成一串带包名类名的二进制占空间不说一旦你升级了实体类的包名或者字段老数据全部反序列化失败。需要操作数据结构的场景把实体类拆成Hash字段然后配合Jackson的GenericJackson2JsonRedisSerializer做value反序列化注意不要对Hash整体做JDK序列化存储。3. 持久化与高可用缓存重启之后数据还在吗3.1 Redis持久化的两种方案RDB和AOF这是Redis和Memcached最本质的区别之一。Memcached完全没有持久化能力数据全部存在内存里重启进程、宕机、断电缓存里的数据全部清空。这在早期缓存场景里是可以接受的——缓存本来就是要容忍丢失的。但电商购物车、Feed流、排行榜、在线状态这类数据如果仅仅因为缓存重启就全部清空后端数据库会被瞬间冲垮这就是所谓的缓存雪崩的一种来源。Redis提供了两种持久化机制。RDBRedis DataBase是快照持久化在默认配置下Redis会fork一个子进程把内存里的数据全量写入一个二进制dump.rdb文件。这种方式恢复速度快、文件紧凑适合做备份和灾难恢复。缺点是两次快照之间的数据可能会丢失。AOFAppend Only File是追加日志持久化每次写命令都会追加到aof文件中重启时通过重放日志恢复数据。AOF提供了三种同步策略always每条命令都同步刷盘最安全但性能最差、everysec每秒刷一次盘最多丢一秒数据性能均衡、no交由操作系统刷盘。生产环境主流方案是everysec配合RDB定期快照做冷备。实战里还有一个常见的运维动作让RDB和AOF同时开启。Redis加载数据时会优先使用AOF因为AOF保存的数据更完整。同时定期把RDB文件备份到远程存储用于极端情况下的恢复——比如AOF文件损坏或者整个机器磁盘故障。3.2 主从复制的基本原理从节点到底在干什么Redis的主从复制replication是它的高可用基础。一个主节点master可以挂多个从节点slave从节点会实时同步主节点上的数据变更。同步过程大致分两个阶段第一阶段是全量同步。从节点发送PSYNC命令给主节点主节点fork子进程生成RDB快照发给从节点从节点加载完RDB之后主节点会把快照生成期间产生的新写命令通过复制积压缓冲区repl_backlog继续发给从节点。第二阶段是增量同步。全量完成之后主节点会持续把新的写命令推送给从节点从节点执行同样的命令来保持数据一致。这个过程通过网络连接完成属于异步复制所以从节点理论上存在短暂的数据滞后这也是Redis实际使用中需要注意的读写分离时刚写入主库的数据立刻去从库读可能读不到。从节点的价值不只是备份它还可以承担读流量。我见过很多团队把缓存流量全部打到主节点上其实如果读多写少完全可以把读请求分流到从节点大幅降低主节点压力。但一定要接受“从节点数据可能有秒级延迟”这个现实不适合对一致性要求极高的场景。3.3 哨兵模式与集群模式的区别这是被搜烂了的问题很多人分不清Sentinel哨兵模式和Cluster集群模式这里我一次性讲清楚。哨兵模式解决的是高可用问题不解决容量扩展问题。架构上是一主多从Sentinel进程监控主从节点的健康状态当主节点挂了Sentinel会从从节点里选择一个晋升为新的主节点完成故障转移。业务上还是通过客户端连接到一个逻辑上的主节点客户端需要配合Sentinel协议去感知主节点变更。哨兵模式对客户端是基本透明的只要配置上Sentinel地址客户端就能动态获取当前主节点。它能承载的数据量上限依然是单机内存的大小因为只有一个主节点承载写入。集群模式解决的是容量扩展问题同时也兼具高可用能力。Redis Cluster把数据按照哈希槽hash slot进行分片固定16384个槽通过CRC16算法计算key的哈希值再对16384取模决定这个key落在哪个节点上。每个节点负责一部分槽槽可以在节点之间迁移实现扩缩容。集群模式下每个主节点可以挂从节点主节点挂了之后从节点自动晋升所以它天然包含了哨兵的功能。从使用角度来说数据量预估在单机内存能扛住的范围比如几十GB以内优先考虑哨兵模式简单可靠。一旦数据量到了上百GB单机内存装不下或者写入并发已经压垮单节点CPU就需要上集群模式。集群模式带来的限制也要提前知道多键操作MGET、MSET、事务在不同的槽上会失败除非这些key都显式地加上相同的hash tag比如用大括号把同一个标识符包进去MGET {user:1001}:orders {user:1001}:cart这种写法可以把多个key映射到同一个槽从而支持批量操作。但代价是这些key的访问会打到同一台机器上热点问题需要自行评估。3.4 Memcached的高可用只能靠客户端自己搞定Memcached本身没有主从、没有哨兵、没有集群。它只有一个简单的“分布式”方式客户端基于一致性哈希consistent hashing把key分散到多台Memcached节点上。某个节点挂了一致性哈希只会影响到部分key的缓存命中但是没有任何自动故障转移——你需要自己实现一个节点状态监测然后在客户端把挂掉的节点摘掉。在Memcached的生态里有一种间接的高可用思路做两套Memcached池读多写少场景下悲观地同时写两份缓存读的时候取一份如果读不到再去另一份。这是很原始的双写策略和Redis的自动主从复制差了一个时代。所以结论很简单你在搜索词里能看到“redis哨兵模式和集群模式的区别”却永远也搜不到“memcached哨兵模式”因为Memcached根本没有这个概念。4. 线程模型与性能Memcached并没有传说中那么快4.1 Memcached的多线程与Redis的单线程性能取决于瓶颈有一个非常流传的误解Memcached是多线程所以比单线程的Redis快。这个说法严格来讲要拆开看。Memcached从1.4.x开始使用了多线程架构主线程接受网络连接worker线程处理请求。多线程的好处在于它能够充分利用多核CPU在请求量大到把CPU打满的场景下Memcached的扩展性确实强。但Memcached的多线程并不是无锁的——它内部仍然有锁竞争比如全局的LRU锁在某些操作下会成为瓶颈。Redis在很长一段时间里以单线程闻名。6.0之前Redis的网络IO和处理都是由单个事件循环线程完成的单线程模型最大的优势是没有锁竞争、没有上下文切换开销加上纯内存操作很快单线程足以跑到10万QPS。单线程的瓶颈其实在网络IO上而不是指令执行上。Redis 6.0引入了多线程IO把socket读写的部分给到多个线程但命令执行还是单线程。这意味着Redis的核心语义没有变所有命令执行是严格串行的不需要担心并发修改数据的问题。4.2 实测场景下的差距小数据Redis占优大数据两者接近落实到实际性能对比我过去在多个项目里做过压测给你一个比较直观的参考数值因机器和安装配置而不同但相对关系比较稳定表格如下对比项RedisMemcached单线程读性能value1KB10万 QPS10万 QPS多核扩展能力受限于单线程命令执行网络IO多线程CPU多的机器优势明显复杂数据结构操作支持Lua脚本可组合原子操作不支持只能客户端处理大数据value100KB以上性能下滑明显序列化开销大性能相对稳定直接存取字节流持久化性能损失AOF everysec约损失5%-20%无持久化零损失内存碎片率相对较高取决于数据结构和分配器使用slab allocation碎片控制更好从这张表可以看出一条基本结论如果你的场景极其简单——就是set一个很大的value然后get它且你只需要纯粹的缓存不需要持久化不需要数据结构那么Memcached在超大value场景下和CPU核数特别多的时候确实有优势。但大部分互联网业务里Redis凭借多种数据结构和原子操作可以在一个组件里省掉大量应用层代码进而节省的是研发和维护成本。4.3 内存管理slab分配 vs 多种内存策略Memcached使用slab allocation机制把内存划分成多个slab class每个class内部包含若干个固定大小的chunk。当你set一个key-value时Memcached会找一个大小刚好能放下它的chunk来存储。这个机制的优点是内存碎片少缺点是内存利用率取决于数据大小分布——如果你的数据大小和chunk大小不匹配会出现浪费。比如value都是1.5KB但slab class里只有2KB的chunk每个value就要浪费0.5KB。所以使用Memcached时合理规划value大小分布非常重要不然内存看似够用实际存不了多少数据。Redis的内存管理则更灵活它使用jemalloc分配器处理多种大小分配效率较好。Redis 4.0之后还引入了内存碎片整理active defrag在后台自动整理碎片减少内存浪费。另外Redis支持为数据设置精确的过期策略过期键有三种清理方式惰性删除访问时发现过期就删除、定期删除后台周期扫描部分过期键、以及内存淘汰策略maxmemory-policy。Redis的淘汰策略有八种常用的有allkeys-lru、volatile-lru、allkeys-lfu等而且可以在不重启的情况下动态调整CONFIG SET maxmemory-policy allkeys-lruMemcached的过期键处理方式和淘汰机制则相对简单通过LRU近似算法在内存不足时淘汰最近最少使用的item。这里有一个特殊情况要提醒Memcached在内存不足时不会拒绝写入而是直接淘汰旧数据所以如果你的Memcached被存满了你可能会发现缓存里的数据频繁失效而且没有日志提示——排查命中率下降时第一个要查的就是是否发生了淘汰。5. 分布式场景缓存治理、分布式锁与缓存一致性5.1 缓存治理穿透、击穿、雪崩以及Redis的应对办法搜索热度里“redis缓存治理”排得很高这说明很多人已经被缓存故障折腾过了。如果你用Redis做业务缓存最常遇到的三个问题是缓存穿透请求查询一个根本不存在的数据缓存里没有数据库里也没有每次请求都会穿过缓存打到数据库。高并发下这会让数据库承受大量无效请求。解决办法有三个缓存空值即使查不到也把空结果缓存几十秒、布隆过滤器Bloom Filter预判key是否存在、以及参数校验把明显非法的请求挡在外面。缓存击穿某个key的过期时间到了但同时有大量请求访问这个key请求全部被打到数据库。解决办法是用分布式锁控制只有一个请求去数据库拉数据其他请求等待。更简单点的做法是使用“逻辑过期”方案value里额外存一个逻辑过期时间主动刷新后台线程过期了也不立即删除先返回旧数据再异步更新。缓存雪崩大量key同时过期或者Redis实例整体不可用导致大量请求直接打到数据库。对策是多管齐下过期时间加随机偏移量让过期时刻分散开Redis高可用靠哨兵或集群保证应用侧做熔断和降级极端情况使用本地缓存兜底。这里我想多说一句以上这些治理手段有一个前提就是你想清楚了哪些数据该进缓存、哪些不该进。缓存不是越多越好把数据库中所有查询结果都塞进Redis只会让一致性维护变得非常痛苦。通常建议缓存那些读多写少、实时性要求不高的数据比如配置类数据、商品详情、用户画像、排行榜。写多的数据库存、余额尽量避免直接缓存直接用数据库事务Redis分布式锁来控制并发。5.2 分布式锁Redis实现的细节与坑分布式锁是Redis应用最广的进阶功能之一。标准实现方式如下加锁SET lock:order:1001 550e8400-e29b-41d4-a716-446655440000 NX PX 30000锁的value要保证唯一通常用UUID这样释放锁的时候才能确认是自己加的锁。释放锁时必须使用Lua脚本来保证“判断删除”的原子性if redis.call(get,KEYS[1]) ARGV[1] then return redis.call(del,KEYS[1]) else return 0 end如果不加这个判断而直接delete可能会出现一个隐患线程A持锁超时后锁自然过期线程B重新获得同一把锁然后A执行完业务直接把B的锁给删了导致临界区并发进入。这个坑非常经典很多新手踩过。再进一步单台Redis的分布式锁在极端情况下可能不满足“绝对可靠”因为Redis的锁是“异步复制主节点挂掉就丢”。为此Redis官方给出了Redlock算法——向N个独立部署的Redis节点依次加锁超过半数成功才算加锁成功。但Redlock在分布式系统领域有争议包括Martin Kleppmann等人都发表过论文批判它的时钟假设和GC停顿问题。实践中的建议是如果你的业务能容忍极端情况下锁失效大部分业务其实可以单节点Redis锁就够用如果要求绝对可靠请直接使用ZooKeeper或etcd不要用Redis。5.3 序列化与连接工具的选型刚才提到序列化再说说客户端和可视化工具。用Redis的日常工作离不开好的连接工具搜索热度里“另一个redis桌面管理器”被反复搜索很多人不知道如何选择。如果你用的是Windows比较省心的选择是使用开源的Another Redis Desktop Manager它打包了Windows可执行文件直接点击安装即可。它支持批量操作、连接多个Redis实例、查看内存分析报告。Mac用户也有对应版本支持macOS M系列芯片。此外Redis官方也有一个免费的RedisInsight不仅能看到key还能做命令行CLI、慢日志分析、内存分析甚至发布订阅调试如果追求官方稳定性优先用RedisInsight。命令行方面无论你装的是Linux版本还是Windows版本redis-cli都是必须熟练掌握的。常用命令redis-cli -h 127.0.0.1 -p 6379 -a yourpassword redis-cli --scan --pattern user:* redis-cli --latency redis-cli --bigkeys--bigkeys能帮你扫描出占用内存最大的key这在排查大key导致阻塞时非常有用。大key的问题在Redis里很常见一个几MB的String或者几十万元的Hash在操作时阻塞事件循环影响所有其他请求。遇到这种问题手段包括拆分大key、用Hash替代String、以及必要时用scan分批处理。5.4 缓存与数据库的一致性先更新数据库再删缓存最后必须聊一个所有用缓存的人都会纠结的问题缓存和数据库的一致性问题。这不是Redis或Memcached特有的但用Redis时大家期望值更高所以矛盾更明显。常用做法有两种先更新数据库再删除缓存先删缓存再更新数据库。实际项目里更推荐“先更新数据库再删除缓存”理由是删缓存这个操作的代价比更新缓存小而且如果更新缓存并发写同一个key容易出现旧数据覆盖新数据的时序问题。删缓存则不存在这个问题因为下一次读时会重新加载数据库数据。但是“先更新数据库再删缓存”也有一个经典问题更新数据库成功之后删除缓存失败怎么办解决办法包括重试机制删除失败放进消息队列异步重试、订阅数据库binlog如Canal同步删除缓存、以及设置较短的过期时间作为最终兜底。不要指望任何一种方案能做到强一致缓存本身面向的是最终一致你要做的只是缩小不一致的时间窗口。6. 选型决策这不该是一个二选一的问题6.1 什么情况下你真的可以考虑Memcached尽管上面说了很多Redis的优势但Memcached依然有它的位置我尽量客观地说。如果你的项目是纯缓存场景且满足以下条件用Memcached完全没有问题甚至更省心第一你只需要get/set/delete不需要任何数据结构操作第二value比较大比如几十KB到1MB主要做静态内容或序列化后的Feed缓存第三团队对运维复杂度敏感不想管理持久化、主从、哨兵、集群这些概念第四数据可以完全容忍丢失重启后直接回源数据库做重建。这个场景在广告系统、静态页面缓存、CDN回源缓存、Session存储里依然存在。另一点是内存效率如果你的value大小分布比较均匀Memcached的slab分配器效率可能更高。而且Memcached是多线程在几十核的机器上纯缓存协议的处理能力有数量级上的优势吞吐量上限比Redis单线程模型高不少。6.2 什么情况闭眼选Redis反过来说只要满足下面任何一个条件就别纠结了直接选Redis需要List、Set、Hash、ZSet等数据结构做业务 需要持久化即使是缓存也希望重启能恢复 需要分布式锁、消息队列、排行榜、限流、UV统计、地理位置等功能 需要主从复制、哨兵、集群做高可用和扩展 技术栈里已经有Spring Data Redis等成熟客户端团队对Redis更熟悉。实际环境里90%以上的团队最终都会选Redis不是因为从众而是因为它的功能密度让你可以在同一个基础设施上解决缓存、分布式锁、计数、排行榜、限流、幂等等一堆问题。维护一个组件和散布四五个专用组件运维成本完全不是一个量级。6.3 从部署落地的角度看Redis的安装与运维其实也很简单很多团队不敢选Redis是怕运维复杂。其实日常部署并没有想象中难。Linux系统下最标准的安装流程# 下载源码编译安装 wget https://download.redis.io/releases/redis-7.2.4.tar.gz tar xzf redis-7.2.4.tar.gz cd redis-7.2.4 make make install生产环境建议给Redis设置密码、关闭危险命令、启用AOF并配置好内存上限requirepass your_strong_password rename-command FLUSHALL maxmemory 8gb maxmemory-policy volatile-lru appendonly yes appendfsync everysecDocker方式更简单特别是搭主从的时候写一个docker-compose文件就能把主节点和两个从节点拉起来services: redis-master: image: redis:7.2 command: redis-server --requirepass 123456 --appendonly yes ports: - 6379:6379 redis-slave1: image: redis:7.2 command: redis-server --slaveof redis-master 6379 --masterauth 123456 depends_on: - redis-master ports: - 6380:6379 redis-slave2: image: redis:7.2 command: redis-server --slaveof redis-master 6379 --masterauth 123456 depends_on: - redis-master ports: - 6381:6379Windows上的安装也简单直接下载Redis的Windows压缩包解压后运行redis-server.exe或者注册成Windows服务启动时不需要每次手动点开redis-server --service-install redis.windows.conf --loglevel verbose redis-server --service-start设置密码通过配置文件或命令行都可以配置项还是上面那几个。这些细节如果你踩过坑就会明白真正消耗时间的不是装起来而是后续的高可用和生产环境故障演练。7. 踩坑与经验我换掉Memcached之后遇到的三个真实问题7.1 大key阻塞缓存从没告诉过你这个风险把Memcached换到Redis之后我遇到的第一个深坑是“大key阻塞”。Memcached的value非常大时比如1MB你的get操作本身也是耗时的但它多线程处理一个线程慢最多影响这个请求自己。Redis不同它是单线程执行命令如果你不小心存了一个10MB的value一个get操作就要花几十毫秒而在这几十毫秒里所有其他请求全部排队。后果就是Redis的吞吐量骤降主线程阻塞从节点复制也出现延迟。排查方法很简单刚才提到过用redis-cli --bigkeys定期扫描把大key消灭在萌芽状态。或者用命令查看单个key的大小MEMORY USAGE user:1001这个命令不会把value取出来而是通过内存分配器计算占用空间非常适合排查。修复策略是拆分一个大的Hash拆成多个小Hash或者用List分段存储尽量让单个key的操作时间控制在1毫秒以内。7.2 缓存穿透压垮数据库空值缓存要加短TTL另一个坑是缓存穿透。当时我们有一个根据手机号查用户信息的接口因为接口被脚本刷请求打的全是无手机号对应不存在的用户。数据库一时间只剩几千QPS就被打满了。后来发现是每次请求都穿透到数据库查询而数据库里没数据所以我们也没有缓存任何东西。解决办法很简单凡是数据库查询为空的key也写进Redis设置一个很短的过期时间比如5分钟。这样即使被疯狂刷数据库也只会每个key每5分钟扛一次请求压力降低了好几个数量级。再加上接口层面做了限流和参数校验把非法参数提前拒绝掉现在这套系统再也没被穿透打垮过。7.3 Redis的持久化导致进程启动变慢RDB和AOF的取舍最后说说持久化带来的麻烦。我们有一台Redis节点保存了近20GB的数据重启一次要加载好几GB的RDB文件耗时接近一分钟。新版本Redis支持RDB和AOF混合持久化aof-use-rdb-preamble yesRDB作为基础快照后续增量用AOF记录重启加载速度快数据安全也更好。另外一定要设置合理的save策略避免频繁生成RDB文件导致磁盘IO抖动。高并发写入场景下同时开RDB和AOF必须关注子进程fork带来的内存开销建议给系统预留足够内存否则fork瞬间可能触发OOM。这些经验都是在踩过坑之后才总结出来的文章看得再多不如自己把主从复制、哨兵切换、持久化恢复这些操作完整演练一遍。缓存组件看起来简单实际生产里对上容量规划、故障演练、监控告警每一步都可能成为事故点。8. 一句话建议回到最初的问题——Redis还是Memcached如果你只想要一个非常简单的缓存而且能接受数据全部丢失、不做高可用、不做复杂操作Memcached依然可以胜任。但凡你有一点想用缓存做更多事情的想法Redis都是更合理的选择。而且今天Redis的周边生态、资料、可视化工具、云服务托管都远比Memcached丰富招人和排查问题的成本也低得多。选型不是追新是减少你未来一年运维和开发的麻烦。至少在我负责过的项目里切到Redis之后我没有后悔过一次。
返回列表