说到redis就会遐想到memcached,反之亦然。相识过两者的同学有那么个大抵的印象:redis与memcached对比,比仅支持简朴的key-value数据范例,同时还提供list,set,zset,hash等数据布局的存储;redis支持数据的备份,即master-slave模式的数据备份;redis支持数据的耐久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载举办利用等等,这好像看起来redis比memcached越发牛逼一些,那么事实上是不是这样的呢?存在即公道,我们来按照几个差异点来一一较量一下。
网络IO模子
memcached是多线程,非阻塞IO复用的网络模子,分为监听主线程和worker子线程,监听线程监听网络毗连,接管请求后,将毗连描写字pipe通报给worker线程,举办读写IO,网络层利用libevent封装的事件库,多线程模子可以发挥多核浸染,可是引入了cache coherency和锁的问题,好比:memcached最常用的stats呼吁,实际memcached所有操纵都要对这个全局变量加锁,举办技能等事情,带来了机能损耗。
redis利用单线程的IO复用模子,本身封装了一个简朴的AeEvent事件处理惩罚框架,主要实现了epoll, kqueue和select,对付单存只有IO操纵来说,单线程可以将速度优势发挥到最大,可是redis也提供了一些简朴的计较成果,好比排序、聚合等,对付这些操纵,单线程模子施加会严重影响整体吞吐量,CPU计较进程中,整个IO调治都是被阻塞的。
数据支持范例
memcached利用key-value形式存储和会见数据,在内存中维护一张庞大的HashTable,使得对数据查询的时间巨大度低落到O(1),担保了对数据的高机能会见。
正如开篇所说:redis与memcached对比,比仅支持简朴的key-value数据范例,同时还提供list,set,zset,hash等数据布局的存储;具体可以翻阅《Redis内存利用优化与存储》
内存打点机制
对付像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释放内存。