Redis的做者Salvatore Sanfilippo曾经对这两种基于内存的数据存储系统进行过比较:node
具体为何会出现上面的结论,如下为收集到的资料:redis
一、数据类型支持不一样算法
与Memcached仅支持简单的key-value结构的数据记录不一样,Redis支持的数据类型要丰富得多。最为经常使用的数据类型主要由五 种:String、Hash、List、Set和Sorted Set。Redis内部使用一个redisObject对象来表示全部的key和value。redisObject最主要的信息如图所示:数据库
type表明一个value对象具体是何种数据类型,encoding是不一样数据类型在redis内部的存储方式,好比:type=string代 表value存储的是一个普通字符串,那么对应的encoding能够是raw或者是int,若是是int则表明实际redis内部是按数值型类存储和表 示这个字符串的,固然前提是这个字符串自己能够用数值表示,好比:”123″ “456”这样的字符串。只有打开了Redis的虚拟内存功能,vm字段字段才会真正的分配内存,该功能默认是关闭状态的。数组
1)String缓存
2)Hash安全
3)List服务器
4)Set网络
5)Sorted Set数据结构
二、内存管理机制不一样
在Redis中,并非全部的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。当物理内存用完时,Redis能够将一些很 久没用到的value交换到磁盘。Redis只会缓存全部的key的信息,若是Redis发现内存的使用量超过了某一个阀值,将触发swap的操 做,Redis根据“swappability = age*log(size_in_memory)”计算出哪些key对应的value须要swap到磁盘。而后再将这些key对应的value持久化到磁 盘中,同时在内存中清除。这种特性使得Redis能够保持超过其机器自己内存大小的数据。固然,机器自己的内存必需要可以保持全部的key,毕竟这些数据 是不会进行swap操做的。同时因为Redis将内存中的数据swap到磁盘中的时候,提供服务的主线程和进行swap操做的子线程会共享这部份内存,所 以若是更新须要swap的数据,Redis将阻塞这个操做,直到子线程完成swap操做后才能够进行修改。当从Redis中读取数据的时候,若是读取的 key对应的value不在内存中,那么Redis就须要从swap文件中加载相应数据,而后再返回给请求方。 这里就存在一个I/O线程池的问题。在默认的状况下,Redis会出现阻塞,即完成全部的swap文件加载后才会相应。这种策略在客户端的数量较小,进行 批量操做的时候比较合适。可是若是将Redis应用在一个大型的网站应用程序中,这显然是没法知足大并发的状况的。因此Redis运行咱们设置I/O线程 池的大小,对须要从swap文件中加载相应数据的读取请求进行并发操做,减小阻塞的时间。
对于像Redis和Memcached这种基于内存的数据库系统来讲,内存管理的效率高低是影响系统性能的关键因素。传统C语言中的 malloc/free函数是最经常使用的分配和释放内存的方法,可是这种方法存在着很大的缺陷:首先,对于开发人员来讲不匹配的malloc和free容易 形成内存泄露;其次频繁调用会形成大量内存碎片没法回收从新利用,下降内存利用率;最后做为系统调用,其系统开销远远大于通常函数调用。因此,为了提升内 存的管理效率,高效的内存管理方案都不会直接使用malloc/free调用。Redis和Memcached均使用了自身设计的内存管理机制,可是实现 方法存在很大的差别,下面将会对二者的内存管理机制分别进行介绍。
Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完 全解决内存碎片问题。Slab Allocation机制只为存储外部数据而设计,也就是说全部的key-value数据都存储在Slab Allocation系统里,而Memcached的其它内存请求则经过普通的malloc/free来申请,由于这些请求的数量和频率决定了它们不会对 整个系统的性能形成影响Slab Allocation的原理至关简单。 如图所示,它首先从操做系统申请一大块内存,并将其分割成各类尺寸的块Chunk,并把尺寸相同的块分红组Slab Class。其中,Chunk就是用来存储key-value数据的最小单位。每一个Slab Class的大小,能够在Memcached启动的时候经过制定Growth Factor来控制。假定图中Growth Factor的取值为1.25,若是第一组Chunk的大小为88个字节,第二组Chunk的大小就为112个字节,依此类推。
当Memcached接收到客户端发送过来的数据时首先会根据收到数据的大小选择一个最合适的Slab Class,而后经过查询Memcached保存着的该Slab Class内空闲Chunk的列表就能够找到一个可用于存储数据的Chunk。当一条数据库过时或者丢弃时,该记录所占用的Chunk就能够回收,从新添 加到空闲列表中。从以上过程咱们能够看出Memcached的内存管理制效率高,并且不会形成内存碎片,可是它最大的缺点就是会致使空间浪费。由于每一个 Chunk都分配了特定长度的内存空间,因此变长数据没法充分利用这些空间。如图 所示,将100个字节的数据缓存到128个字节的Chunk中,剩余的28个字节就浪费掉了。
Redis的内存管理主要经过源码中zmalloc.h和zmalloc.c两个文件来实现的。Redis为了方便内存的管理,在分配一块内存之 后,会将这块内存的大小存入内存块的头部。如图所示,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_allocations。 zmalloc_allocations[16]表明已经分配的长度为16bytes的内存块的个数。zmalloc.c中有一个静态变量 used_memory用来记录当前分配的内存总大小。因此,总的来看,Redis采用的是包装的mallc/free,相较于Memcached的内存 管理方法来讲,要简单不少。
三、数据持久化支持
Redis虽然是基于内存的存储系统,可是它自己是支持内存数据的持久化的,并且提供两种主要的持久化策略:RDB快照和AOF日志。而memcached是不支持数据持久化操做的。
1)RDB快照
Redis支持将当前数据的快照存成一个数据文件的持久化机制,即RDB快照。可是一个持续写入的数据库如何生成快照呢?Redis借助了fork 命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,而后在子进程中循环全部的数据,将数据写成为RDB文件。咱们能够经过Redis的 save指令来配置RDB快照生成的时机,好比配置10分钟就生成快照,也能够配置有1000次写入就生成快照,也能够多个规则一块儿实施。这些规则的定义 就在Redis的配置文件中,你也能够经过Redis的CONFIG SET命令在Redis运行时设置规则,不须要重启Redis。
Redis的RDB文件不会坏掉,由于其写操做是在一个新进程中进行的,当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临 时文件中,而后经过原子性rename系统调用将临时文件重命名为RDB文件,这样在任什么时候候出现故障,Redis的RDB文件都老是可用的。同 时,Redis的RDB文件也是Redis主从同步内部实现中的一环。RDB有他的不足,就是一旦数据库出现问题,那么咱们的RDB文件中保存的数据并不 是全新的,从上次RDB文件生成到Redis停机这段时间的数据所有丢掉了。在某些业务下,这是能够忍受的。
2)AOF日志
AOF日志的全称是append only file,它是一个追加写入的日志文件。与通常数据库的binlog不一样的是,AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令。只 有那些会致使数据发生修改的命令才会追加到AOF文件。每一条修改数据的命令都生成一条日志,AOF文件会愈来愈大,因此Redis又提供了一个功能,叫 作AOF rewrite。其功能就是从新生成一份AOF文件,新的AOF文件中一条记录的操做只会有一次,而不像一份老文件那样,可能记录了对同一个值的屡次操 做。其生成过程和RDB相似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。在写入新文件的过程当中,全部的写操做日志仍是会写到原来老的 AOF文件中,同时还会记录在内存缓冲区中。当重完操做完成后,会将全部缓冲区中的日志一次性写入到临时文件中。而后调用原子性的rename命令用新的 AOF文件取代老的AOF文件。
AOF是一个写文件操做,其目的是将操做日志写到磁盘上,因此它也一样会遇到咱们上面说的写操做的流程。在Redis中对AOF调用write写入 后,经过appendfsync选项来控制调用fsync将其写到磁盘上的时间,下面appendfsync的三个设置项,安全强度逐渐变强。
对于通常性的业务需求,建议使用RDB的方式进行持久化,缘由是RDB的开销并相比AOF日志要低不少,对于那些没法忍数据丢失的应用,建议使用AOF日志。
四、集群管理的不一样
Memcached是全内存的数据缓冲系统,Redis虽然支持数据的持久化,可是全内存毕竟才是其高性能的本质。做为基于内存的存储系统来讲,机 器物理内存的大小就是系统可以容纳的最大数据量。若是须要处理的数据量超过了单台机器的物理内存大小,就须要构建分布式集群来扩展存储能力。
Memcached自己并不支持分布式,所以只能在客户端经过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。下图给出了 Memcached的分布式存储实现架构。当客户端向Memcached集群发送数据以前,首先会经过内置的分布式算法计算出该条数据的目标节点,而后数 据会直接发送到该节点上存储。但客户端查询数据时,一样要计算出查询数据所在的节点,而后直接向该节点发送查询请求以获取数据。
相较于Memcached只能采用客户端实现分布式存储,Redis更偏向于在服务器端构建分布式存储。最新版本的Redis已经支持了分布式存储 功能。Redis Cluster是一个实现了分布式且容许单点故障的Redis高级版本,它没有中心节点,具备线性可伸缩的功能。下图给出Redis Cluster的分布式存储架构,其中节点与节点之间经过二进制协议进行通讯,节点与客户端之间经过ascii协议进行通讯。在数据的放置策略 上,Redis Cluster将整个key的数值域分红4096个哈希槽,每一个节点上能够存储一个或多个哈希槽,也就是说当前Redis Cluster支持的最大节点数就是4096。Redis Cluster使用的分布式算法也很简单:crc16( key ) % HASH_SLOTS_NUMBER。
为了保证单点故障下的数据可用性,Redis Cluster引入了Master节点和Slave节点。在Redis Cluster中,每一个Master节点都会有对应的两个用于冗余的Slave节点。这样在整个集群中,任意两个节点的宕机都不会致使数据的不可用。当 Master节点退出后,集群会自动选择一个Slave节点成为新的Master节点。