miller
发布于

Memcached 与 Redis 对比及其优劣分析

本文由 简悦 SimpRead 转码, 原文地址 www.cnblogs.com

国外讨论

本文主要总结缓存 Redis 和 Memcached 的区别, 总结之前先参考外国知乎上的一篇问答:《Is memcached a dinosaur in comparison to Redis?》, 为了方便今后查阅, 将部分语句翻译记录一下:

  • You should not care too much about performances. Redis is faster per core with small values, but memcached is able to use multiple cores with a single executable and TCP port without help from the client. Also memcached is faster with big values in the order of 100k. Redis recently improved a lot about big values (unstable branch) but still memcached is faster in this use case. The point here is: nor one or the other will likely going to be your bottleneck for the query-per-second they can deliver.

    没有必要过多的关心性能,因为二者的性能都已经足够高了。由于 Redis 只使用单核,而 Memcached 可以使用多核,所以在比较上,平均每一个核上 Redis 在存储小数据时比 Memcached 性能更高。而在 100k 以上的数据中,Memcached 性能要高于 Redis,虽然 Redis 最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。说了这么多,结论是,无论你使用哪一个,每秒处理请求的次数都不会成为瓶颈。(比如瓶颈可能会在网卡)

  • You should care about memory usage. For simple key-value pairs memcached is more memory efficient. If you use Redis hashes, Redis is more memory efficient. Depends on the use case.

    如果要说内存使用效率,使用简单的 key-value 存储的话,Memcached 的内存利用率更高,而如果 Redis 采用 hash 结构来做 key-value 存储,由于其组合式的压缩,其内存利用率会高于 Memcached。当然,这和你的应用场景和数据特性有关。

  • You should care about persistence and replication, two features only available in Redis. Even if your goal is to build a cache it helps that after an upgrade or a reboot your data are still there.

    如果你对数据持久化和数据同步有所要求,那么推荐你选择 Redis,因为这两个特性 Memcached 都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择 Redis 也是明智的。

  • You should care about the kind of operations you need. In Redis there are a lot of complex operations, even just considering the caching use case, you often can do a lot more in a single operation, without requiring data to be processed client side (a lot of I/O is sometimes needed). This operations are often as fast as plain GET and SET. So if you don’t need just GEt/SET but more complex things Redis can help a lot (think at timeline caching).

    当然,最后还得说到你的具体应用需求。Redis 相比 Memcached 来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在 Memcached 里,你需要将数据拿到客户端来进行类似的修改再 set 回去。这大大增加了网络 IO 的次数和数据体积。在 Redis 中,这些复杂的操作通常和一般的 GET/SET 一样高效。所以,如果你需要缓存能够支持更复杂的结构和操作,那么 Redis 会是不错的选择。

下面是具体的各方面对比:

性能

Memcached 是全内存的数据缓冲系统,Redis 虽然支持数据的持久化,但是全内存才是其高性能的本质。作为基于内存的存储系统来说,机器物理内存的大小就是系统能够容纳的最大数据量。如果需要处理的数据量超过了单台机器的物理内存大小,就需要构建分布式集群来扩展存储能力。

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

集群与分布式方面

redis 和 memcached 都支持集群, redis 支持 master-slave 模式, memcache 可以使用一致性 hash 做分布式。简单两张图看出区别:

Memcached 本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现 Memcached 的分布式存储,关于分布式一致性哈希算法见总结:分布式一致性 hash 算法。当客户端向 Memcached 集群发送数据之前,首先会通过内置的分布式算法计算出该条数据的目标节点,然后数据会直接发送到该节点上存储。但客户端查询数据时,同样要计算出查询数据所在的节点,然后直接向该节点发送查询请求以获取数据。

相较于 Memcached 只能采用客户端实现分布式存储,Redis 更偏向于在服务器端构建分布式存储,但没有采用一致性哈希,关于 Redis 集群分析见总结:Redis 集群数据没法拆分时的搭建策略。最新版本的 Redis 已经支持了分布式存储功能。Redis Cluster 是一个实现了分布式且允许单点故障的 Redis 高级版本,它没有中心节点,具有线性可伸缩的功能。为了保证单点故障下的数据可用性,Redis Cluster 引入了 Master 节点和 Slave 节点。在 Redis Cluster 中,每个 Master 节点都会有对应的两个用于冗余的 Slave 节点。这样在整个集群中,任意两个节点的宕机都不会导致数据的不可用。当 Master 节点退出后,集群会自动选择一个 Slave 节点成为新的 Master 节点。

数据类型

Redis 支持的数据类型要比 memcached 丰富得多, Redis 不仅仅支持简单的 K-V 类型的数据,同时还提供 String,List,Set,Hash,Sorted Set,pub/sub,Transactions 数据结构的存储。Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。

memcache 支持 K-V 数据类型,这个 V 没有类型的概念, 也就意味着通过 memcached 取得的数据为 JSON 对象, 并且做一次请求, 会返回所有的 Value 数据, 你需要将数据拿到客户端来进行类似的修改再 set 回去,序列化再反序列化,这大大增加了网络 IO 的次数和数据体积, 这时候 IO 就是瓶颈, 因为并不是所有数据都需要, 同时存以及取数据时需要客户端自己对数据进行处理。

意味着在对数据进行复杂操作方面, Redis 更有优势, 因为 Redis 对每种类型都有独特的操作命令, 对于很多操作提升了效率。这体现了大数据方面计算向数据移动的思维。

持久性

Redis 和 Memcached 都是将数据存放在内存中,都是内存数据库。不过 memcached 还可用于缓存其他东西,例如图片、视频等等。

memcached 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;redis 有部份存在硬盘上,这样能保证数据的持久性,支持数据的持久化(RDB、AOF),而 Memcached 不支持持久化。

同时 Redis 并不是所有的数据都一直存储在内存中的,当物理内存用完时,Redis 可以将一些很久没用到的 value 交换到磁盘,但 memcached 超过内存比例会抹掉前面的数据。redis 支持数据落地持久化存储 (Redis 持久化之 RDB 和 AOF), 可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

memcache 不支持数据持久存储 , 也就是掉电就没了。

value 大小不同

memcached 的简单限制就是键(key)和 item 的限制。最大键长为 250 个字符。可以接受的储存数据不能超过 1MB,因为这是典型 slab(Linux 系统分配内存的一种算法) 的最大值。但是我们可以突破对 key 长度的限制。

修改 memcached 源文件。在 memcached.h 中定义 key 的长度,其代码为:

#define KEY_MAX_LENGTH 250


更换为所需要的长度,比如:1024

#define KEY_MAX_LENGTH 1024


数据一致性方式不同

redis 使用的是单线程模型,保证了数据按顺序提交。

memcache 需要使用 cas 保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于 “乐观锁” 范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作 cpu 利用, redis 单线程模型只能使用一个 cpu,但是可以开启多个 redis 进程。

内存管理机制

对于像 Redis 和 Memcached 这种基于内存的数据库系统来说,内存管理的效率高低是影响系统性能的关键因素。传统 C 语言中的 malloc/free 函数是最常用的分配和释放内存的方法,但是这种方法存在着很大的缺陷:

首先对于开发人员来说不匹配的 malloc 和 free 容易造成内存泄露;其次频繁调用会造成大量内存碎片无法回收重新利用,降低内存利用率;

然后作为系统调用,其系统开销远远大于一般函数调用。所以,为了提高内存的管理效率,高效的内存管理方案都不会直接使用 malloc/free 调用。

Memcached 默认使用 Slab Allocation 机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的 key-value 数据记录,以完全解决内存碎片问题。

Slab Allocation 机制只为存储外部数据而设计,也就是说所有的 key-value 数据都存储在 Slab Allocation 系统里,而 Memcached 的其它内存请求则通过普通的 malloc/free 来申请,因为这些请求的数量和频率决定了它们不会对整个系统的性能造成影响。

Memcacahed 的 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 个字节就浪费掉了。Memcached 主要的 cache 机制是 LRU(最近最少使用 Least Recently Used, 具体操作参考: 常见页面置换算法图解)算法 + 超时失效。

Redis 内存分配机制

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 的内存管理方法来说,要简单很多

EOF

本文作者:等不到的口琴
本文链接:https://www.cnblogs.com/Courage129/p/14331520.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角**【推荐】**一下。您的鼓励是博主的最大动力!

浏览 (1304)
点赞
收藏
评论