Memcache vs. Redis?

我们正在使用带有Redis服务器的Ruby Web应用程序进行缓存。有必要测试Memcache吗?

什么能给我们带来更好的性能?

需要考虑的要点:

  • 读/写速度。
  • 内存使用。
  • 磁盘I/O转储。
  • 缩放。
601886 次浏览

Memcach擅长作为一个简单的键/值存储,并且擅长执行key=>STRING。这使得它非常适合会话存储。

Redis擅长做key=>SOME_OBJECT。

这真的取决于你要在那里放什么。我的理解是,就性能而言,它们相当均衡。

也祝你好运找到任何客观的基准,如果你确实找到了一些,请把它们寄给我。

如果你不介意粗俗的写作风格,从可用性的角度来看,Systoilet博客上的Redis vs Memcache值得一读,但在得出任何关于性能的结论之前,请务必阅读评论中的来回;有一些方法问题(单线程繁忙循环测试),自文章编写以来,Redis也做了一些改进。

没有一个基准链接是完整的,而不会让事情有点混乱,所以还要检查Dormondo的LiveJournalAntirez博客处的一些冲突的基准。

编辑--正如Antirez指出的那样,Systoilet的分析是相当糟糕的。即使除了单线程的缺点之外,这些基准测试中的大部分性能差异也可以归因于客户端库而不是服务器吞吐量。Antirez博客的基准测试确实呈现了更多的苹果对苹果(同样的嘴)的比较。

摘要(太长别读)

2017年6月3日更新

Redis比memcache更强大、更受欢迎、支持更好。Memcache只能做Redis能做的一小部分事情。即使它们的功能重叠,Redis也更好。

对于任何新事物,请使用Redis。

Memcache与Redis:直接比较

这两个工具都是功能强大、快速的内存数据存储,可作为缓存使用。两者都可以通过缓存数据库结果、超文本标记语言片段或任何可能生成成本高昂的其他内容来帮助加快应用程序的速度。

需要考虑的要点

当用于同一件事时,以下是他们如何使用原始问题的“要考虑的要点”进行比较:

  • 读/写速度:两者都非常快。基准测试因工作负载、版本和许多其他因素而异,但通常显示redis与memcache一样快或几乎一样快。我推荐redis,但不是因为memcache很慢。事实并非如此。
  • 内存使用:Redis更好。
    • memcache:您指定缓存大小,当您插入项目时,守护程序会迅速增长到比此大小多一点。除了重新启动memcache之外,从来没有真正回收任何空间的方法。您所有的密钥都可能过期,您可以刷新数据库,它仍然会使用您配置的全部RAM。
    • redis:设置最大大小由您决定。Redis永远不会使用超过它必须使用的内存,并会将不再使用的内存归还给您。
    • 我将100,000~2KB的随机句子字符串(~200MB)存储在两者中。Memcach RAM的使用量增长到~225MB。Redis RAM的使用量增长到~228MB。在冲洗两者后,redis下降到~29MB,memcache保持在~225MB。它们在存储数据方面同样高效,但只有一个能够回收它。
  • 磁盘I/O转储:redis的明显胜利,因为它默认这样做并且具有非常可配置的持久性。Memcache没有在没有第三方工具的情况下转储到磁盘的机制。
  • 缩放:在您需要多个实例作为缓存之前,两者都为您提供了大量的空间。Redis包括帮助您超越这一点的工具,而memcache没有。

memcache

Memcache是一个简单的易失性缓存服务器。它允许您存储键/值对,其中值限制为最大1MB的字符串。

它很擅长这个,但这就是它所做的一切。您可以以极高的速度通过它们的键访问这些值,通常会使可用的网络甚至内存带宽饱和。

当你重新启动memcacher时,你的数据就消失了。这对缓存来说很好。你不应该在那里存储任何重要的东西。

如果您需要高性能或高可用性,可以使用第三方工具、产品和服务。

redis

Redis可以做与memcache相同的工作,并且可以做得更好。

Redis也可以充当缓存。它也可以存储键/值对。在redis中,它们甚至可以高达512MB。

您可以关闭持久性,它也会在重新启动时很高兴地丢失您的数据。如果您希望您的缓存在重新启动后仍然存在,它也可以让您这样做。事实上,这是默认的。

它也非常快,通常受到网络或内存带宽的限制。

如果一个redis/memcache实例的性能不足以满足你的工作负载,redis是一个明确的选择。redis包括集群支持,并附带高可用性工具(前哨),就在“盒子里”。在过去的几年里,redis也成为第三方工具的明显领导者。Redis Labs、亚马逊等公司提供了许多有用的redis工具和服务。围绕redis的生态系统要大得多。现在大规模部署的数量可能比memcache还要多。

Redis超集

Redis不仅仅是一个缓存。它是一个内存中数据结构服务器。下面你会发现Redis可以做的事情的快速概述,而不仅仅是像memcache这样简单的键/值缓存。redis的大多数特性是memcache不能做的事情。

需求文档

Redis比memcache更好地记录。虽然这可能是主观的,但它似乎越来越真实。

redis.io是一个很棒的易于导航的资源。它让您可以在浏览器中尝试redis,甚至为您提供文档中每个命令的实时交互式示例。

现在redis的stackoverflow结果是memcache的两倍。谷歌结果的两倍。更多语言的更容易访问的示例。更积极的开发。更积极的客户端开发。这些测量可能对个人没有多大意义,但结合起来,它们描绘了一幅清晰的画面,即对redis的支持和留档更大,更及时。

持久性

默认情况下,redis使用称为快照的机制将您的数据持久化到磁盘。如果您有足够的可用RAM,它可以将所有数据写入磁盘,几乎没有性能下降。它几乎是免费的!

在快照模式下,突然崩溃可能会导致少量数据丢失。如果您绝对需要确保没有数据丢失,请不要担心,redis在AOF(仅附加文件)模式下也支持您。在这种持久模式下,数据可以在写入时同步到磁盘。这可以将最大写入吞吐量降低到磁盘可以写入的速度,但仍然应该非常快。

如果需要,有许多配置选项可以微调持久性,但默认值非常合理。这些选项可以轻松将redis设置为存储数据的安全冗余位置。它是一个真正数据库。

多种数据类型

Memcach仅限于字符串,但Redis是一个数据结构服务器,可以提供许多不同的数据类型。它还提供了充分利用这些数据类型所需的命令。

字符串(命令

大小可达512MB的简单文本或二进制值。这是唯一的数据类型redis和memcache共享,尽管memcach字符串限制为1MB。

Redis通过提供按位操作、位级操作、浮点增/减支持、范围查询和多键操作的命令,为您提供了更多利用此数据类型的工具。Memcach不支持这些。

字符串对各种用例都很有用,这就是为什么memcache仅对这种数据类型相当有用的原因。

哈希(命令

哈希有点像键值存储中的键值存储。它们在字符串字段和字符串值之间映射。使用哈希的字段->值映射比使用常规字符串的键->值映射稍微节省空间。

哈希作为命名空间很有用,或者当你想要对许多键进行逻辑分组时。使用哈希,你可以有效地获取所有成员,一起过期所有成员,一起删除所有成员,等等。非常适合有多个键/值对需要分组的任何用例。

使用散列的一个例子是用于在应用程序之间存储用户配置文件。使用用户ID作为键存储的redis散列将允许您根据需要存储尽可能多的用户数据位,同时将它们存储在单个键下。使用散列而不是将配置文件序列化为字符串的优点是,您可以让不同的应用程序读取/写入用户配置文件中的不同字段,而不必担心一个应用程序覆盖其他应用程序所做的更改(如果您序列化陈旧数据可能会发生这种情况)。

列表(命令

Redis列表是字符串的有序集合。它们针对从列表的顶部或底部(又名:左侧或右侧)插入、读取或删除值进行了优化。

Redis为利用列表提供了许多命令,包括推送/弹出项目、列表之间推送/弹出、截断列表、执行范围查询等命令。

列表构成了非常耐用的原子队列。这些非常适合作业队列、日志、缓冲区和许多其他用例。

设置(命令

集合是唯一值的无序集合。它们经过优化,可让您快速检查值是否在集合中,快速添加/删除值,并测量与其他集合的重叠。

这些非常适合权限改造列表、唯一访问者跟踪器和许多其他东西。大多数编程语言都有类似的东西(通常称为Set)。

Redis提供了几个命令来管理集合。存在添加、删除和检查集合等明显的命令。弹出/读取随机项等不太明显的命令以及与其他集合执行联合和交叉的命令也是如此。

排序集(命令

排序集合也是唯一值的集合。顾名思义,这些值是有序的。它们按分数排序,然后按字典顺序排列。

此数据类型针对按分数快速查找进行了优化。获取最高、最低或介于两者之间的任何值范围都非常快。

如果你将用户和他们的高分一起添加到排序集中,你就有了一个完美的排行榜。当新的高分进来时,只需将他们和他们的高分再次添加到集合中,它就会重新排列你的排行榜。也非常适合跟踪用户最后一次访问以及谁在你的应用程序中活跃。

存储具有相同分数的值会使它们按字典顺序排序(按字母顺序思考)。这对于自动完成功能等事情很有用。

许多排序集合命令类似于集合命令,有时带有额外的分数参数。还包括用于管理分数和按分数查询的命令。

Geo

Redis有几个命令用于存储、检索和测量地理数据。这包括半径查询和测量点之间的距离。

从技术上讲,redis中的地理数据存储在排序集中,因此这不是真正独立的数据类型。它更像是排序集之上的扩展。

位图和HyperLogLog

与geo一样,这些不是完全独立的数据类型。这些命令允许您将字符串数据视为位图或超日志。

位图是我在Strings下引用的位级运算符的用途。这种数据类型是reddit最近合作艺术项目的基本构建块:r/Place

HyperLogLog允许您使用恒定的极小量空间以惊人的准确性计算几乎无限的唯一值。仅使用约16KB,您就可以有效地计算网站的唯一访问者数量,即使这个数字是数百万。

交易和原子性

redis中的命令是原子的,这意味着你可以确定一旦你向redis写入一个值,该值对连接到redis的所有客户端都是可见的。无需等待该值传播。从技术上讲,memcache也是原子的,但是随着redis在memcache之外添加所有这些功能,值得注意的是,所有这些额外的数据类型和特性也是原子的,这让人印象深刻。

虽然与关系数据库中的事务不太一样,但redis也有交易使用“乐观锁定”(手表/MULTI/EXEC)。

管道

Redis提供了一个名为“流水线”的功能。如果您有许多要执行的redis命令,您可以使用流水线将它们一次性发送到redis,而不是一次一个。

通常,当你对redis或memcacher执行命令时,每个命令都是一个单独的请求/响应周期。通过流水线,redis可以缓冲多个命令并一次执行它们,从而在单个回复中用对所有命令的所有响应来响应。

这可以让您在批量导入或其他涉及大量命令的操作中实现更大的吞吐量。

酒吧/潜艇

Redis有命令专用于发布/订阅功能,允许redis充当高速消息广播器。这允许单个客户端将消息发布到连接到频道的许多其他客户端。

Redis可以做发布/订阅以及几乎任何工具。像RabbitMQ这样的专用消息代理可能在某些领域具有优势,但事实上,同一服务器也可以为您提供发布/订阅工作负载可能需要的持久持久队列和其他数据结构,Redis通常会被证明是最好和最简单的工作工具。

lua脚本

您可以将lua脚本视为redis自己的SQL或存储过程。它既多又少,但类比大多有效。

也许你想让redis执行复杂的计算。也许你负担不起事务回滚,需要保证复杂流程的每一步都将原子地发生。这些问题以及更多的问题都可以通过lua脚本来解决。

整个脚本是原子执行的,因此如果您可以将逻辑放入lua脚本中,您通常可以避免混淆乐观锁定事务。

缩放

如上所述,redis包括对集群的内置支持,并与自己的高可用性工具redis-sentinel捆绑在一起。

结论

我会毫不犹豫地向任何新项目或尚未使用memcache的现有项目推荐redis而不是memcache。

上面的内容听起来可能像我不喜欢memcache。相反:它是一个强大、简单、稳定、成熟和硬化的工具。甚至在一些用例中它比redis快一点。我喜欢memcache。我只是不认为它对未来的发展有多大意义。

redis做了memcache所做的一切,通常更好。memcache的任何性能优势都是次要的,并且是特定于工作负载的。还有一些工作负载,redis会更快,还有更多的工作负载,redis可以做,memcache根本做不到。面对功能上的巨大鸿沟,以及这两个工具都如此快速高效的事实,微小的性能差异似乎微不足道,它们很可能是你不必担心扩展的最后一块基础设施。

只有一种情况是memcache更有意义的:memcache已经被用作缓存。如果你已经在使用memcache进行缓存,那么如果它满足你的需求,请继续使用它。转移到redis可能不值得努力,如果你只是为了缓存而使用redis,它可能不会提供足够的好处来值得你花时间。如果memcache不能满足你的需求,那么你可能应该转移到redis。无论你需要扩展到memcache之外还是需要额外的功能,这都是正确的。

另一个好处是,它可以非常清楚memache在缓存场景中的行为方式,而redis通常用作持久数据存储,尽管它可以配置为在达到最大容量时像memcache一样执行,也就是驱逐最近最少使用的项目。

我研究过的一些应用程序使用这两种方法只是为了清楚地表明我们打算如何处理数据——在memache中的东西,我们编写代码来处理它不存在的情况——在redis中的东西,我们依赖它的存在。

除此之外,Redis通常被认为优于大多数用例,功能更丰富,因此更灵活。

使用Redis如果

  1. 您需要选择性地删除/过期缓存中的项目。(您需要这个)

  2. 你需要查询特定类型的键的能力。例如。blog1:帖子:*,blog2:类别:xyz:帖子:*。哦,是的!这非常重要。使用它有选择地使某些类型的缓存项无效。您也可以使用它来使片段缓存、页面缓存、仅给定类型的AR对象无效等。

  3. 持久性(你也需要这个,除非你不介意缓存在每次重启后都必须预热。对于很少改变的对象非常重要)

使用memcache如果

  1. Memcache让你头疼!
  2. 嗯…群集?嗯。如果你要走那么远,请使用清漆和Redis来缓存片段和AR对象。

根据我的经验,我使用Redis的稳定性比Memcache好得多

我们认为Redis是我们工作项目的负载起飞。我们认为通过使用nginx中名为HttpRedis2Module或类似的模块,我们会有惊人的速度,但是当用AB测试时,我们被证明是错误的。

也许模块不好或者我们的布局不好,但这是一个非常简单的任务,用php获取数据然后将其填充到MongoDB中更快。我们使用APC作为缓存系统,并使用php和MongoDB。它比nginx Redis模块快得多。

我的建议是自己测试它,这样做会向您展示您的环境的结果。我们决定在我们的项目中使用Redis是不必要的,因为它没有任何意义。

这里没有指出的一个主要区别是Memache在任何时候都有内存上限,而Redis默认情况下没有(但可以配置为)。如果你总是想将一个键/值存储一定的时间(并且永远不会因为内存不足而驱逐它),你想使用Redis。当然,你也有运行内存溢出的问题…

Memcache是多线程且快速的。

Redis有很多功能,速度非常快,但完全限于一个核心,因为它基于事件循环。

我们两者都使用。Memcach用于缓存对象,主要是减少数据库上的读取负载。Redis用于排序集之类的东西,这对于滚动时间序列数据很方便。

这是太长张贴作为一个评论已经接受的答案所以我把它作为一个单独的答案

还需要考虑的一件事是,您是否希望缓存实例有一个硬的内存上限。

由于redis是一个具有大量功能的nosql数据库,而缓存只是它可以使用的一个选项,因此它会根据需要分配内存-您放入的对象越多,它使用的内存就越多。maxmemory选项并没有严格强制执行内存使用上限。当您使用缓存时,键会被逐出并过期;很可能您的键的大小不完全相同,因此会发生内部内存碎片。

默认情况下redis使用jemalloc内存分配器,它尽最大努力保持内存紧凑和快速,但它是一个通用内存分配器,无法跟上大量分配和高速对象清除。正因为如此,在某些负载模式下,redis进程显然会因为内部碎片而泄漏内存。例如,如果你有一个7 Gb RAM的服务器,并且你想使用redis作为非持久性LRU缓存,你可能会发现随着时间的推移maxmemory设置为5 Gb的redis进程会使用越来越多的内存,最终达到总RAM限制,直到内存不足杀手干扰。

memcache分配一大块内存——它需要的一切——然后使用自己实现的平板分配器自行管理这一内存。此外,memcache努力保持内部碎片低,因为当LRU驱逐在考虑对象大小的情况下完成时,它实际上使用每片LRU算法

话虽如此,memcache在内存使用必须强制和/或可预测的环境中仍然具有强大的地位。我们尝试使用最新的稳定redis(2.8.19)作为1015k操作/s的工作负载中的非持久性基于LRU的memcache替换,并且它泄漏了大量内存;由于相同的原因,相同的工作负载在一天左右的时间内崩溃了Amazon的ElastiCache redis实例。

如果我们说redis是(缓存+数据结构)的组合,而memcache只是一个缓存,那就错不了。

我有机会在我工作过的缓存代理中同时使用memcache和redis,让我分享一下我到底在哪里使用了什么和背后的原因……

Redis>

1)用于在集群上索引缓存内容。我有超过十亿个键分布在redis集群上,redis响应时间相当少且稳定。

2)基本上,它是一个键/值存储,所以在你的应用程序中,你有类似的东西,人们可以使用redis来解决很多问题。

3)Redis持久性、故障转移和备份(AOF)将使您的工作更轻松。

内存缓存>

1)是的,一个可以用作缓存的优化内存。我用它来存储缓存内容,这些内容被非常频繁地访问(50次/秒),大小小于1 MB。

2)当我的单个内容大小>1MB时,我只分配了16 GB中的2GB用于memcache。

3)随着内容增长接近极限,偶尔我在统计数据中观察到更高的响应时间(而不是redis的情况)。

如果您要求整体体验,Redis非常绿色,因为它易于配置,非常灵活,具有稳定的强大功能。

此外,在这个链接上有一个基准测试结果,下面是来自同一个的一些亮点,

在此处输入图片描述

在此处输入图片描述

希望有帮助!!!

测试。运行一些简单的基准测试。有很长一段时间,我认为自己是一只老派的犀牛,因为我主要使用memcach,并认为Redis是新来的。

在我目前的公司中,Redis被用作主缓存。当我深入研究一些性能统计数据并开始测试时,Redis在性能方面与MySQL相当或最低限度地

Memcache虽然过于简单,但却让Redis失去了水完全。它的缩放效果要好得多:

  • 对于更大的值(需要改变平板尺寸,但有效)
  • 对于多个并发请求

此外,在我看来,memcache驱逐策略的实施要好得多,导致总体上更稳定的平均响应时间,同时处理的数据比缓存可以处理的更多。

一些基准测试显示,在我们的案例中,Redis表现非常差。我认为这与许多变量有关:

  • 您运行Redis的硬件类型
  • 您存储的数据类型
  • 获取和设置量
  • 您的应用程序的并发程度
  • 是否需要数据结构存储

就个人而言,我不同意Redis作者对并发和多线程的看法。

最大的原因是专业化。

Redis可以做很多不同的事情,其中一个副作用是开发人员可能会开始在同一个实例上使用很多不同的功能集。如果您使用Redis的LRU功能作为非LRU的侧硬数据存储缓存,则完全有可能运行内存溢出。

如果您要设置一个专用的Redis实例仅用作LRU实例以避免这种特定情况,那么在Memcacher上使用Redis并没有任何令人信服的理由。

如果你需要一个可靠的“永不停机”的LRU缓存……Memcacher将符合要求,因为它不可能通过设计运行内存溢出,并且专门的功能阻止开发人员试图使其成为可能危及它的东西。

Redis更好。

Redis的优点是,

  1. 它有很多数据存储选项,如字符串、集合、排序集、哈希、位图
  2. 磁盘记录的持久性
  3. 存储过程(LUA脚本)支持
  4. 可以使用PUB/SUB充当消息代理

Memcache是内存键值缓存类型系统。

  1. 不支持各种数据类型存储,如列表、集合redis。
  2. 主要缺点是Memache没有磁盘持久性。

一个非常简单的测试来设置和获取100k唯一的键和值,它们都在linux VM(CentOS)上运行,我的客户端代码(粘贴在下面)在windows桌面上运行。

Redis

  • 存储100000个值的时间=18954ms

  • 加载100000个值的时间=18328ms

Memcache

  • 存储100000个值的时间=797ms

  • 检索100000个值所需的时间=38984ms


Jedis jed = new Jedis("localhost", 6379);int count = 100000;long startTime = System.currentTimeMillis();for (int i=0; i<count; i++) {jed.set("u112-"+i, "v51"+i);}long endTime = System.currentTimeMillis();System.out.println("Time taken to store "+ count + " values is ="+(endTime-startTime)+"ms");
startTime = System.currentTimeMillis();for (int i=0; i<count; i++) {client.get("u112-"+i);}endTime = System.currentTimeMillis();System.out.println("Time taken to retrieve "+ count + " values is ="+(endTime-startTime)+"ms");

如果您对性能感兴趣,Memcache会更快,即使Redis涉及网络(TCP调用)。在内部Memache也更快。

Redis有更多的功能,因为它被其他答案提到。

这里是亚马逊提供的真正伟大的文章/差异

与memcache相比,Redis是一个明显的赢家。

Memcache只有一个加分它是多线程且快速的。Redis有很多很棒的功能并且非常快,但仅限于一个核心。

关于Redis的要点,Memcache中不支持

  • 快照-用户可以拍摄Redis缓存的快照并持久化任何时间点的辅助存储。
  • 内置支持许多数据结构,如Set、Map、SortedSet、列表,位图等
  • 在redis中支持Lua脚本