memcache 和 redis 是互联网分层架构中, 最常用的 KV 缓存. 不少同学在选型的时候会纠结, 到底是选择 memcache 还是 redis.
画外音: 不鼓励粗暴的实践, 例如 "memcache 提供的功能是 redis 提供的功能的子集, 不用想太多, 选 redis 准没错".
虽然 redis 比 memcache 更晚出来, 且功能确实也更丰富, 但对于一个技术人, 了解 "所以然" 恐怕比 "选择谁" 更重要一些.
什么时候倾向于选择 redis?
业务需求决定技术选型, 当业务有这样一些特点的时候, 选择 redis 会更加适合.
复杂数据结构
value 是哈希, 列表, 集合, 有序集合这类复杂的数据结构时, 会选择 redis, 因为 mc 无法满足这些需求.
最典型的场景, 用户订单列表, 用户消息, 帖子评论列表等.
持久化
mc 无法满足持久化的需求, 只得选择 redis.
但是, 这里要提醒的是, 真的使用对了 redis 的持久化功能么?
千万不要把 redis 当作数据库用:
redis 的定期快照不能保证数据不丢失
redis 的 AOF 会降低效率, 并且不能支持太大的数据量
不要期望 redis 做固化存储会比 mysql 做得好, 不同的工具做各自擅长的事情, 把 redis 当作数据库用, 这样的设计八成是错误的.
缓存场景, 开启固化功能, 有什么利弊?
如果只是缓存场景, 数据存放在数据库, 缓存在 redis, 此时如果开启固化功能:
优点是, redis 挂了再重启, 内存里能够快速恢复热数据, 不会瞬时将压力压到数据库上, 没有一个 cache 预热的过程.
缺点是, 在 redis 挂了的过程中, 如果数据库中有数据的修改, 可能导致 redis 重启后, 数据库与 redis 的数据不一致.
因此, 只读场景, 或者允许一些不一致的业务场景, 可以尝试开启 redis 的固化功能.
天然高可用
redis 天然支持集群功能, 可以实现主动复制, 读写分离.
redis 官方也提供了 sentinel 集群管理工具, 能够实现主从服务监控, 故障自动转移, 这一切, 对于客户端都是透明的, 无需程序改动, 也无需人工介入.
而 memcache, 要想要实现高可用, 需要进行二次开发, 例如客户端的双读双写, 或者服务端的集群同步.
但是, 这里要提醒的是, 大部分业务场景, 缓存真的需要高可用么?
缓存场景, 很多时候, 是允许 cache miss
缓存挂了, 很多时候可以通过 DB 读取数据
所以, 需要认真剖析业务场景, 高可用, 是否真的是对缓存的主要需求?
画外音: 即时通讯业务中, 用户的在线状态, 就有高可用需求.
存储的内容比较大
memcache 的 value 存储, 最大为 1M, 如果存储的 value 很大, 只能使用 redis.
什么时候倾向于 memcache?
纯 KV, 数据量非常大, 并发量非常大的业务, 使用 memcache 或许更适合.
这要从 mc 与 redis 的底层实现机制差异说起.
内存分配
memcache 使用预分配内存池的方式管理内存, 能够省去内存分配时间.
redis 则是临时申请空间, 可能导致碎片.
从这一点上, mc 会更快一些.
虚拟内存使用
memcache 把所有的数据存储在物理内存里.
redis 有自己的 VM 机制, 理论上能够存储比物理内存更多的数据, 当数据超量时, 会引发 swap, 把冷数据刷到磁盘上.
从这一点上, 数据量大时, mc 会更快一些.
网络模型
memcache 使用非阻塞 IO 复用模型, redis 也是使用非阻塞 IO 复用模型.
但由于 redis 还提供一些非 KV 存储之外的排序, 聚合功能, 在执行这些功能时, 复杂的 CPU 计算, 会阻塞整个 IO 调度.
从这一点上, 由于 redis 提供的功能较多, mc 会更快一些.
线程模型
memcache 使用多线程, 主线程监听, worker 子线程接受请求, 执行读写, 这个过程中, 可能存在锁冲突.
redis 使用单线程, 虽无锁冲突, 但难以利用多核的特性提升整体吞吐量.
从这一点上, mc 会快一些.
最后说两点
1. 代码可读性, 代码质量
看过 mc 和 redis 的代码, 从可读性上说, redis 是我见过代码最清爽的软件, 甚至没有之一, 或许简单是 redis 设计的初衷, 编译 redis 甚至不需要 configure, 不需要依赖第三方库, 一个 make 就搞定了.
而 memcache, 可能是考虑了太多的扩展性, 多系统的兼容性, 代码不清爽, 看起来费劲.
例如网络 IO 的部分, redis 源码 1-2 个文件就搞定了, mc 使用了 libevent, 一个 fd 传过来传过去, 又 pipe 又线程传递的, 特别容易把人绕晕.
画外音: 理论上, mc 只支持 kv, 而 redis 支持了这么多功能, mc 性能应该高非常多非常多, 但实际并非如此, 真的可能和代码质量有关.
2. 水平扩展的支持
不管是 mc 和 redis, 服务端集群没有天然支持水平扩展, 需要在客户端进行分片, 这其实对调用方并不友好. 如果能服务端集群能够支持水平扩展, 会更完美一些.
来源: http://zhuanlan.51cto.com/art/201806/576901.htm