分布式缓存技术深度解析:Redis vs Memcached
Executive Summary
核心观点(金字塔原理)
结论先行: 在分布式缓存选型中,Redis因其丰富的数据结构、持久化能力和原生集群支持,更适合作为主流缓存方案;Memcached则在纯KV场景下凭借多线程模型具有更高的吞吐量优势。
支撑论点:
- 架构差异:Redis采用单线程IO复用模型保证原子性,Memcached采用多线程模型提升并发处理能力
- 功能边界:Redis支持5+数据结构、持久化、事务;Memcached仅支持KV且无持久化
- 扩展方案:Redis Cluster原生支持分布式,Memcached依赖客户端一致性哈希
SWOT 分析
| 维度 | Redis | Memcached |
|---|---|---|
| S 优势 | 丰富数据结构、持久化、原生集群、事务支持 | 多线程高吞吐、内存管理无碎片、协议简单 |
| W 劣势 | 单线程CPU计算阻塞、内存碎片问题 | 无持久化、数据结构单一、客户端分布式复杂 |
| O 机会 | 适合复杂业务场景、可作为消息队列/存储使用 | 简单KV缓存场景、Session存储 |
| T 威胁 | 大Key阻塞、全量同步资源消耗大 | 冷启动数据丢失、Slab空间浪费 |
适用场景
- 选择Redis:需要复杂数据结构、数据持久化、分布式锁、排行榜、实时消息等场景
- 选择Memcached:纯KV缓存、极致吞吐量、短期Session存储等简单场景
缓存基础
缓存适用场景
- 性能优化:减少后端计算和IO压力
- 资源占用:降低数据库连接数和查询频率
缓存分类
进程内缓存
- ConcurrentHashMap
- LRUMap
- Ehcache
- GuavaCache
- Caffeine
分布式缓存
- Memcached:吞吐量较大,只支持key-value数据结构,并且不支持持久化
- Redis:支持丰富的数据结构,读写性能很高,但是数据全内存,必须要考虑资源成本,支持持久化
- Tair:支持丰富的数据结构,读写性能较高,部分类型比较慢,理论上容量可以无限扩充
缓存常见问题
| 问题 | 描述 | 解决方案 |
|---|---|---|
| 缓存穿透 | 查询不存在的数据,请求直达DB | 布隆过滤器、空值缓存 |
| 缓存击穿 | 热点Key过期瞬间大量请求 | 互斥锁、永不过期+异步更新 |
| 缓存雪崩 | 大量Key同时过期 | 过期时间随机化、多级缓存 |
| 缓存污染 | 冷数据占用热数据空间 | LRU/LFU淘汰策略 |
| 数据不一致 | 缓存与DB数据不同步 | 更新串行化、延迟双删 |
数据一致性详解:缓存更新与删除的并发可能会出现脏数据,如果能保证更新/删除的一致性,就能解决问题。想要做到缓存更新一致性,只要做到更新串行化即可。通常client路由到指定节点进行操作,保证串行化,比如根据key hash。对同一个缓存key,进行串行化更新。跨机房情况,由更新server同步更新所有机房数据,不依赖replica机制,能避免延迟导致的脏数据。
Redis vs Memcached 深度对比
网络模型
Redis
- 单线程IO复用模型:封装AeEvent事件处理框架,主要实现了epoll、kqueue和select
- 性能特点:单线程保证原子性,但CPU密集型计算(排序、聚合等)会阻塞整体IO调度
Memcached
- 多线程非阻塞IO复用模型:Master主线程 + Worker子线程架构
- 性能特点:更好利用多核CPU,但带来数据一致性和锁同步问题(如stats命令需要加锁)
数据类型
Redis
- String、List、Set、ZSet(Sorted Set)、Hash
- 附加功能:Pub/Sub、Transactions
Memcached
- 仅支持Key-Value形式
- 内存中维护巨大的HashTable,查询时间复杂度O(1)
数据持久化
Redis
支持两种持久化方式:
- RDB(快照):将某一时刻的所有数据写入硬盘
- AOF(只追加文件):执行写命令时,将命令复制到硬盘
Memcached
- 不支持持久化:所有数据以in-memory形式存储
数据一致性
Redis
- 不提供CAS命令,但通过事务保证原子性
- 事务命令:
MULTI、DISCARD、EXEC、WATCH、UNWATCH - 事务特性:满足原子性、一致性、隔离性,不满足持久性
- 注意:Redis不提供事务回滚机制
Memcached
- 提供CAS命令,保证并发访问同一数据的一致性
内存管理
Redis
- 通过
zmalloc.h和zmalloc.c实现内存管理 - 分配内存时将size存入内存块头部,便于释放
- 使用数组
zmalloc_allocations记录各大小内存块分配情况 - Virtual Memory:内存不足时可将冷数据swap到磁盘
- 特点:简单的malloc/free包装,可能产生内存碎片
Memcached
- Slab Allocation机制:
- 预分配大块内存,分割成不同尺寸的Chunk
- 相同尺寸的Chunk组成Slab Class
- Growth Factor控制Chunk大小递增
- 优点:无内存碎片,分配效率高
- 缺点:固定Chunk大小导致空间浪费
集群管理
Redis
- Redis Cluster:原生分布式支持
- 去中心化架构,节点间二进制协议通信
- 16384个哈希槽(非4096),算法:
crc16(key) % 16384 - Master-Slave结构保证高可用
- 自动故障转移:Master宕机时Slave自动提升
Memcached
- 本身不支持分布式
- 依赖客户端实现一致性哈希分布
Redis 高可用架构
主从复制(replication.c)
同步方式
- 全同步(SYNC):首次连接或无法增量同步时
- 部分同步(PSYNC):断线重连后的增量同步
全同步流程
1. 从节点 → SYNC → 主节点
2. 主节点执行 BGSAVE 生成 RDB
3. 主节点发送 RDB 文件流
4. 从节点清空缓存,载入 RDB
5. 主节点发送 Buffer 中的写命令
关键配置参数
| 参数 | 说明 | 建议值 |
|---|---|---|
repl-timeout |
复制超时时间 | 根据RDB大小调整,默认60s |
client-output-buffer-limit replica |
复制缓冲区限制 | 256m 64m 60 |
repl-disable-tcp-nodelay |
TCP延迟控制 | no(低延迟)/ yes(省带宽) |
全同步资源消耗
| 阶段 | 资源消耗 |
|---|---|
| BGSAVE生成RDB | CPU(Fork子进程) |
| Copy-on-Write | 内存(页面复制) |
| RDB持久化 | 磁盘IO |
| RDB传输 | 网络带宽 |
| RDB加载 | 从节点阻塞 |
部分同步条件
- 复制积压缓冲区:FIFO环形队列,默认1MB
- 复制偏移量:主从各自维护
- 主节点runId:标识主节点身份
参考资料
- Redis源码:https://github.com/redis/redis
- Redis 3.0注释版:https://github.com/huangz1990/redis-3.0-annotated
- Memcached LRU机制:http://timyang.net/data/Memcached-lru-evictions/