赞
赏
memcached 和 redis 都属于内存(memory)键-值(key-value)数据库,在设计和思想上有许多相同之处,功能和应用在很多场合(如分布式缓存服务)也相似。它们都从属于数据库解决方案中的 nosql 家族,由于两者都将数据存储在内存中,自然而然,它们都是非常理想的缓存实现方案。
memcached 最初是由 Brad Fitzpatrick 于 2003 年开发而成。而 redis 则由 Salvatore Sanfilippo 于 2009 年创建,它本身也从 memcached 上吸取借鉴大量宝贵经验教训,被称为 “强化版 memcached”。确实,redis 在功能多样性方面要胜过 memcached,虽然强大且更具灵活性,但复杂程度也比 memcached 更甚。
memcached 采用客户端-服务器(C/S)的架构,客户端和服务器端的通讯使用自定义的协议标准,只要满足协议格式要求,客户端 Library 可以用任何语言实现。
从用户的角度来说,服务器维护了一个键-值关系的数据表,服务器之间相互独立,互相之间不共享数据也不做任何通讯操作。也就是说,memcached 本身不支持分布式扩展。客户端需要知道所有的服务器,并自行负责管理数据在各个服务器间的分配。
在服务器端,内部的数据存储,使用基于 Slab 的内存管理方式,有利于减少内存碎片和频繁分配销毁内存所带来的开销。各个 Slab 按需动态分配一个 page 的内存(和 4K page的概念不同,这里默认 page 为 1M),page 内部按照不同 slab class 的尺寸再划分为内存 chunk 供服务器存储 KV 键值对使用。
memcached 的基本应用模型:
redis 的基本应用模式和上图 memcached 的基本相似,不难发现网上到处都是关于 redis 是否可以完全替代 memcached 使用的问题。
redis 内部的数据结构最终也会落实到 key-value 对应的形式,不过从暴露给用户的数据结构来看,要比 memcached 丰富,除了标准的通常意义的键值对,redis 还支持 List,Set, Hashes,Sorted Set 等数据结构。
memcached 的命令或者说通讯协议非常简单,server 所支持的命令基本就是对特定 key 的添加,删除,替换,原子更新,读取等,具体包括 set, get, add, replace, append, inc/dec 等等。
memcached 的通讯协议包括文本格式和二进制格式,用于满足简单网络客户端工具(如 telnet)和对性能要求更高的客户端的不同需求。
redis 的命令在 kv(string 类型)上提供与 memcached 类似的基本操作,在其它数据结构上也支持基本类似的操作(当然还有这些数据结构所特有的操作,如 set 的 union,list 的 pop 等)而支持更多的数据结构,在一定程度上也就意味着更加广泛的应用场合。
除了多种数据结构的支持,redis 相比 memcached 还提供了许多额外的特性,比如 subscribe/publish 命令,以支持发布/订阅模式这样的通知机制等等,这些额外的特性同样有助于拓展它的应用场景。
redis 的客户端-服务器通讯协议完全采用文本格式(在将来可能的服务器间通讯会采用二进制格式)。
redis 通过 multi / watch / exec 等命令可以支持事务的概念,原子性的执行一批命令。在 2.6 以后的版本中由于添加了对 script 脚本的支持,而脚本固有的是以 transaction 事务的方式执行的,并且更加易于使用,所以不排除将来取消 multi 等命令接口的可能性。
memcached 的应用模式中,除了 increment/decrement 这样的原子操作命令,不存在对事务的支持。
memcached 不保证存储的数据的有效性,slab 内部基于 LRU 也会自动淘汰旧数据,客户端不能假设数据在服务器端的当前状态,这应该说是 memcached 的 feature 设定,用户不必太多关心或者自己管理数据的淘汰更新工作,当然是否适合你的应用,取决于具体的需求,它也可能成为你需要精确自行控制 cache 生命周期的一个障碍。
memcached 也不做数据的持久化工作,但是有许多基于 memcached 协议的项目实现了数据的持久化,例如 memcache DB 使用 BerkeleyDB 进行数据存储,但本质上它已经不是一个 cache server,而只是一个兼容 memcached 的协议 key-valueData Store 了。
redis 可以以 master-slave 的方式配置服务器,slave 节点对数据进行 replica 备份,slave 节点也可以充当 read only 的节点分担数据读取的工作。
redis 内建支持两种持久化方案,snapshot 快照和 AOF 增量 Log 方式。快照顾名思义就是隔一段时间将完整的数据 dump 下来存储在文件中。AOF 增量 Log 则是记录对数据的修改操作(实际上记录的就是每个对数据产生修改的命令本身),两种方案可以并存,也各有优缺点。
以上 Redis 的数据备份持久化方案等,如果不需要,为了提高性能,也完全可以 Disable。
memcached 自身并不主动定期检查和标记哪些数据需要被淘汰,只有当再次读取相关数据时才检查时间戳,或者当内存不够使用需要主动淘汰数据时进一步检查 LRU 数据。
redis 为了减少大量小数据 CMD 操作的网络通讯时间开销 RTT (Round Trip Time),支持 pipeline 和 script 技术。所谓的 pipeline 就是支持在一次通讯中,发送多个命令给服务器批量执行,带来的代价是服务器端需要更多的内存来缓存查询结果。
redis 内嵌了 LUA 解析器,可以执行 lua 脚本,脚本可以通过 eval 等命令直接执行,也可以使用 script load 等方式上传到服务器端的 script cache 中重复使用。这两种方式都可以有效地减少网络通讯开销,增加数据吞吐率。
对于 KV 的操作,memcached 和 redis 都支持 multiple 的 get 和 set 命令(memcached 的 multiple set 命令貌似只在二进制的协议中支持),这同样有利于性能的提升。
实际性能方面,网上有很多测试比较,给出的结果各不相同,这无疑和各种测试的测试用例,测试环境,和测试时具体使用的客户端 Library 实现有关。但是总体看下来,比较靠谱的结论是在 kv 类操作上,两者的性能接近,memcached 的结构更加简单,理论上应该会略微快一些。
memcached 的服务器端互相完全独立,客户端通常通过对键值应用 hash 算法决定数据的分区,为了减少服务器的增减对 hash 结果的影响,导致大面积的缓存失效,多数客户端实现了一致性 hash 算法。
redis 计划在服务器端内建对集群的支持,在此之前,同样可以认为每个 redis 服务器实例相互之间是完全独立的,需要依靠客户端处理分区算法和可用服务器列表管理的工作。
redis 官方推荐的用于 sharding 的客户端程序库是 Twitter 的开源项目 Twemproxy, Twemproxy 同时支持 Memcached 和 Redis 的文本通讯协议。
需要注意的是,redis 的许多命令在集群环境下是不能正确运行的,例如 set 的交集,以及跨节点的事务操作等等,因为目前的 redis 集群设计,根本目标也就是服务器之间互相汇报一下存活状态,以及对数据做荣誉备份平衡负载等而已,本质上对数据的跨节点操作并不提供任何额外支持,所以在数据服务的层面上来说,各个服务器依旧是完全独立的。
这些操作如果一定要实现,当然可以通过客户端代码来实现(效率有多高且不说),类似的问题 memcached 集群当然也会遇上,但是原本 memcached 就不支持复杂的操作和数据类型,许多运算逻辑原本就是由客户端代码或应用程序自己处理的。
redis 的作者 Salvatore Sanfilippo 曾经对这两种基于内存的数据存储系统进行过比较,总体来看还是比较客观的,现总结如下:
由于 redis 只使用单核,而 memcached 可以使用多核,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高。而在 100k 以上的数据中,memcached 性能要高于 redis,虽然 redis 最近也在存储大数据的性能上进行优化,但是比起 memcached,还是稍有逊色。
使用简单的 key-value 存储的话,memcached 的内存利用率更高,而如果 redis 采用 hash 结构来做 key-value 存储,由于其组合式的压缩,其内存利用率会高于 memcached。另外,memcached 使用预分配的内存池的方式,带来一定程度的空间浪费并且在内存仍然有很大空间时,新的数据也可能会被剔除,而 redis 使用现场申请内存的方式来存储数据,不会剔除任何非临时数据 redis 更适合作为存储而不是 cache。
redis 相比 memcached 来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在 memcached 里,你需要将数据拿到客户端来进行类似的修改再 set 回去。这大大增加了网络 IO 的次数和数据体积。在 redis 中,这些复杂的操作通常和一般的 GET/SET 一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么 redis 会是不错的选择。