分布式缓存之Redis与Memcached的比较

一、性能

由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis存储小数据时比Memcached性能更高。而在100k以上的数据时,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。

二、内存使用效率

使用简单的key/value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key/value存储,由于其组合方式的押送,其内存利用率会高于Memcached。

三、Redis支持服务器端的数据操作

Redis相比Memcached来说,拥有更多的数据结构并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去,序列化再反序列化,这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的get/set一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么在Redis会是不错的选择。
与Memcached仅支持简单的key/value结构的数据记录不同,Redis支持的数据类型要丰富得多。最为常用得数据类型主要有5中:String、Hash、List、Set、Zset。Redis内部使用一个redisObject对象来表示所有得key和value。

四、数据备份恢复

memcached挂掉后,数据不可恢复;redis数据丢失之后可以通过aof模式备份进行恢复,Redis支持数据得备份,即master-slave主从模式得数据备份。

五、数据存储

Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过Memcache除了简单得key/value数据类型,还可以存放启动东西,比如图片、视频等。memcache把数据全部都存放在内存中,断电之后会挂掉,数据不能超过内存大小;Redis有部分存放在硬盘上,这样能保证数据得持久性,支持数据得持久化(RDB、AOF),而Memcache不支持持久化。同时Redis并不是所有数据都一直存储在内存中,当物理内存用完,Redis可以将一些很久没用得value交换到磁盘,但memcache超过内存比例会抹掉前面得数据。

六、内存管理机制

对于像Redis和Memcached这种基于内存得数据库系统来说,内存管理得效率高低是影响系统性能得关键因素。传统C语言中得malloc/free函数是最常用得分配和释放内存得方法,但是这种方法存在很大得缺陷:首先,对于开发人员来说不匹配得malloc和free容易造成内存泄漏;其次,频繁调用会造成大量内存碎片无法回收重新利用,降低内存利用率;最后作为系统调用,其系统开销远远大于一般函数调用。所以,为了提高内存得管理效率,高效得内存管理方案都不会直接使用malloc/free调用。
Mamcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定得大小,将分配得内存分割成特定长度得块以存储相应长度得key/value得数据记录,以完全解决内存碎片问题。Slab Alloction机制指挥存储外部数据而设计,也就是说所有得key/value数据都存储在Slab Allocation系统里,而Memcached的其他内存请求则通过普通的malloc/free来申请,因为这些请求的数量和频率决定了它们不会对整个系统的性能造成影响。
分布式缓存之Redis与Memcached的比较

如图所示,它首先从操作系统申请一大块内存,并将其分割成各种尺寸的块Chunk,并把尺寸相同的块分成组SLab Class。其中,Chunk就是用来存储key/value数据最小单位。每个Slab Class的大小,可以在Memcached启动的时候通过指定Growth Factor来控制。假定图中Growth Factor的取值未,如果第一组Chunk的大小为88个字节,第二组Chunk的大小为112个字节,依次类推。
当Memcached接受到客户端发来的数据时,首先会根据收到数据的大小选择一个合适的Slab Class,然后通过查询Memcached保存着该Slab Class内存空闲Chunk的列表就可以找到一个可用于存储数据的Chunk。当一条数据库过期或者丢弃时,该记录所占有的Chunk就可以回收,重新添加到空闲列表中。
从以上过程我们可以看出Memcached的内存管理效率搞,而且不会造成内存碎片,但是它最大的确定就是会导致空间浪费,因为每个Chunk都分配了特定长度的内存空间,所以变长数据无法充分利用这些空间。比如将100个字节的数据缓存到128个字节的Chunk中,剩余的这28个字节就浪费掉了。Memcached主要的cache机制就是LRU算法+超时算法。
分布式缓存之Redis与Memcached的比较

Redis的内存管理主要通过源码的zmalloc.h和zmalloc.c两个文件来实现的。Redis为了方便内存的管理,在分配一块内存之后,会将这块内存的大小存入内存块的头部。
分布式缓存之Redis与Memcached的比较

如图所示,real_ptr是Redis调用malloc后返回的指针。Redis将内存块的大小Size存入头部,size所占据的内存大小是已知的,为size_t类型的长度,然后返回ret_ptr。当需要释放内存的时候ret_ptr被传给内存管理程序,通过ret_ptr,程序可以很容易的算出real_ptr的值,然后real_ptr传给free释放内存。
Redis通过定义一个数组来记录所有的内存分配情况,这个数组的长度为ZMALLOC_MAX_ALLOC_STAT。数组的每一个元素代表当前程序所分配的内存块的个数,且内存块的大小为该元素的下标。在源码中,这个数组为zmalloc_allocation。zmalloc_allocations[16]代表以及分配的长度为16bytes的内存块的个数。zmalloc.c中有一个静态变量used_memory用来记录当前分配的内存总大小。所以,中的来看,Redis采用的是包装的mallc/free,相较与Memcached的内存管理方法来说,要简单很多。

七、集群、分布式存储

Memcached是全内存的数据缓冲系统,Redis虽然支持数据的持久化,但是圈内存毕竟才是其高性能的本质,作为基于内存的存储系统来说,机器物理内存的大小就是系统能够容纳的最大数据量。如果需要处理的数据量超过了单台机器的物理内存大小,就需要构建分布式集群来扩展存储能力。
Memcached本身不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储,关于分布式一致性哈希算法见总结:分布式一致性hash算法。当客户端向Memcached集群发送数据之前首先会通过内置的分布式算法计算出该条数据的目标节点,然后数据会直接发送到该节点上存储。但客户端查询数据时,同样要计算出查询数据所在的节点,然后直接向该节点发送查询请求以获取数据。
相较于Memcached只能采用客户端实现分布式存储,Redis更偏向于在服务器端构建分布式存储,但没有采用一致性哈希,关于Redis集群分析见总结:分布式缓存Redis之cluster集群。最新版本的Redis一键支持了分布式存储功能。Redis Cluster是一个实现了分布式且允许单点故障的Redis高级版本,它没有中心节点,具有线性可伸缩的功能。为了保证单点故障下的数据可用性,Redis Cluster引入了Master节点和Slave节点。在Redis Cluster中,每个Master节点都会有对应的两个用于冗余的Slave节点。这样在整个集群中,任意两个节点的宕机都不会导致数据得不可用。当Master节点退出后,集群会自动选择一个Slave节点成为新的Master节点。

八、Memcached支持多核多线程,Redis单线程操作