Memcached 是什么?
Memcached 是以 LiveJournal [1] 旗下 Danga Interactive 公司的 Brad Fitzpatric [2] 为首开发的一款软件。现在已成为 Facebook、mixi、hatena、Vox、LiveJournal 等众多服务中提高 Web 应用扩展性的重要技术。许多 Web 应用都将数据保存到关系数据库中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现关系数据库的负担加重、数据库响应恶化、网站显示延迟等重大影响。这时就该 Memcached 大显身手了,一般的使用 Memcached 的目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态 Web 应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站更快的运行速度。Memcached 基于一个存储键/值对的 hashmap。其守护进程(daemon )是用 C 写的,但是客户端可以用任何语言来编写,并通过 memcached 协议与守护进程通信。但是它并不提供冗余(例如,复制其 hashmap 条目);当某个服务器 S 停止运行或崩溃了,所有存放在 S 上的键/值对都将丢失。
(截取自:)
Memcached 能缓存什么?
通过在内存里维护一个统一的巨大的哈希表 ,Memcached 能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。
Memcached 快么?
非常快。Memcached 使用了 libevent(如果可以的话,在 linux 下使用 epoll)来均衡任何数量的打开链接,使用非阻塞的网络 I/O,对内部对象实现引用计数(因此,针对多样的客户端,对象可以处在多样的状态), 使用自己的页块分配器和哈希表, 因此虚拟内存不会产生碎片并且虚拟内存分配的时间复杂度可以保证为O(1).。
Danga Interactive 为提升 Danga Interactive 的速度研发了 Memcached。目前,LiveJournal.com 每天已经在向一百万用户提供多达两千万次的页面访问。而这些,是由一个由 web 服务器和数据库服务器组成的集群完成的。Memcached 几乎完全放弃了任何数据都从数据库读取的方式,同时,它还缩短了用户查看页面的速度、更好的资源分配方式,以及 Memcached 失效时对数据库的访问速度。
(截取自:)
Memcached 特征是什么?
- 协议简单
- 基于 libevent 的事件处理
- 内置内存存储方式
- memcached 不互相通信的分布式
协议简单
memcached 的服务器客户端通信并不使用复杂的 XML 等格式, 而使用简单的基于文本行的协议。因此,通过 telnet 也能在 memcached 上保存数据、取得数据。
基于libevent的事件处理
libevent 是个程序库,它将 Linux 的 epoll、BSD 类操作系统的 kqueue 等事件处理功能 封装成统一的接口。即使对服务器的连接数增加,也能发挥 O(1) 的性能。 memcached 使用这个 libevent 库,因此能在 Linux、BSD、Solaris 等操作系统上发挥其高性能。 关于事件处理这里就不再详细介绍,可以参考 Dan Kegel 的 The C10K Problem。
- libevent:
- The C10K Problem:
内置内存存储方式
为了提高性能,memcached 中保存的数据都存储在 memcached 内置的内存存储空间中。 由于数据仅存在于内存中,因此重启 memcached、重启操作系统会导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU( Least Recently Used ) 算法自动删除不使用的缓存。 memcached 本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。
memcached不互相通信的分布式
memcached 尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。 各个 memcached 不会互相通信以共享信息。那么,怎样进行分布式呢? 这完全取决于客户端的实现。
memcached的分布式
(截取自:)
Memcached 适合什么场合?
在很多时候,memcached 都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有人发贴,类似于“如何提高效率”,回复是“用 memcached ”,至于怎么用,用在哪里,用来干什么一句没有。memcached 不是万能的,它也不是适用在所有场合。
Memcached是 “分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用,memcached 不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源,即使是 UNIX 本地连接也一样。 在我之前的测试数据中显示,memcached 本地读写速度要比直接 PHP 内存数组慢几十倍,而APC、共享内存方式都和直接数组差不多。可见,如果只是本地级缓存,使用 memcached 是非常不划算的。
Memcached 在很多时候都是作为数据库前端 cache 使用的。因为它比数据库少了很多 SQL 解析、磁盘操作等开销,而且它是使用内存来管理数据的,所以它可以提供比直接读取数据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached 可以大大降低数据库压力,使系统执行效率提升。另外,memcached 也经常作为服务器之间数据共享的存储媒介,例如在 SSO 系统中保存系统单点登陆状态的数据就可以保存在 memcached 中,被多个应用共享。
需要注意的是,memcached 使用内存管理数据,所以它是易失的,当服务器重启,或者 memcached 进程中止,数据便会丢失,所以 memcached 不能用来持久保存数据。很多人的错误理解,memcached 的性能非常好,好到了内存和硬盘的对比程度,其实 memcached 使用内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了, memcached 进程本身并不占用多少 CPU 资源的情况。(截取自: )
Memcached 使用原则?
- 优先从 Memcached 获取对象,如果没有命中则查询数据库并将这个对象放到Memcache中。
- 在数据库中更新数据的时候删除 Memcached 中对应的对象。
- Memcached 中不会存放衍生对象,所有缓存的对象都数据库中持久化的数据直接映射。
- 对于热门的缓存 key,使用别名,对应的在数据被更新的时候则要删除所有的别名缓存。
- Memcached 服务器是检测内存的最佳位置,记录删除,命中失败等
(截取自:Memcache@Facebook : )
扩展阅读
Facebook对memcached的提升
如果你翻阅过一些关于大型网站扩展(Scaling)的资料,那么你可能听说过一个叫memcached的 东西。memcached是一个高性能、分布式的内存对象缓存系统。我们Facebook可能是世界上最大的memcached用户了。我们利用 memcached来减轻数据库的负担。memcached确实很快,但是我们还要让他更快、更高效。我们使用了超过800台服务器,提供超过28TB的 内存来服务于用户。在过去的一年里,随着Facebook的用户量直线上升,我们遇到了一系列的扩展问题。日益增长的需求使得我们必须对操作系统和 memcached进行一些修改,以获得足够的性能来为我们的用户提供最好的体验。
因为我们有好几千台机器,每个都运行了几百个Apache进程甚至更多,最终导致到memcached进程的TCP链接有几十万个。这些链接本身并 不是什么大问题,但是memcached为每个TCP链接分配内存的方法却很成问题。memcached为每个链接使用单独的缓存进行数据的读写。当达到 几十万链接的时候,这些累计起来达好几个G——这些内存其实可以更好地用于存储用户数据。为了收复这些内存,我们实现了一个针对TCP和UDP套接字的每 线程共享的链接缓存池。这个改变使每个服务器可以收回几个G的内存。
虽然TCP上我们改进了内存的使用效率,但我们还是转向了UDP,目的是让get(获取)操作能降低网络流量、让multi-get(同时并行地获 取几百个键值)能实现应用程序级别的流量控制。我们发现Linux上到了一定负载之后,UDP的性能下降地很厉害。这是由于,当从多个线程通过单个套接字 传递数据时,在UDP套接字锁上产生的大量锁竞争导致的。要通过分离锁来修复内核恐怕不太容易。所以,我们使用了分离的UDP套接字来传递回复(每个线程 用一个答复套接字)。这样改动之后,我们就可以部署UDP同时后端性能不打折。
另一个Linux中的问题是到了一定负载后,某个核心可能因进行网络软终端处理会饱和而限制了网络IO。在Linux中,网络中断只会总是传递给某 个核心,因此所有的接受软终端的网络处理都发生在该内核上。另外,我们还发现某些网卡有过高的中断频率。我们通过引入网络接口的“投机”轮询解决了这两个 问题。在该模型中,我们组合了中断驱动和轮询驱动的网络IO。一旦进入网络驱动(通常是传输一个数据包时)以及在进程调度器的空闲循环的时候,对网络接口 进行轮询。另外,我们也用到了中断(来控制延迟),不过网络中断用到的数量大大减少(一般通过主动设置中断联结阈值interrupt coalescing thresholds)。由于我们在每个核心上进行网络传输,同时由于在调度器的空闲循环中对网络IO进行轮询,我们将网络处理均匀地分散到每个核心上。
最后,当开始部署8核机器的时候,我们在测试中发现了新的瓶颈。首先,memcached的stat工具集依赖于一个全局锁。这在4核上已经很令人 讨厌了,在8核上,这个锁可以占用20-30%的CPU使用率。我们通过将stats工具集移入每个线程,并且需要的时候将结果聚合起来。其次,我们发现 随着传递UDP数据包的线程数量的增加,性能却在降低。最后在保护每个网络设备的传送队列的锁上发现了严重的争用。数据包是由设备驱动进行入队传输和出 队。该队列由Linux的“netdevice”层来管理,它位于IP和设备驱动之间。每次只能有一个数据包加入或移出队列,这造成了严重的争用。我们当 中的一位工程师修改了出队算法,实现了传输的批量出队,去掉了队列锁,然后批量传送数据包。这个更正将请求锁的开销平摊到了多个数据包,显著地减少了锁争 用,这样我们就能在8核系统上将memcached伸展至8线程。
做了这些修改之后,我们可以将memcached提升到每秒处理20万个UDP请求,平均延迟降低为173微秒。可以达到的总吞吐量为30万UDP 请求/s,不过在这个请求速度上的延迟太高,因此在我们的系统中用处不大。对于普通版本的Linux和memcached上的50,000 UDP请求/s而言,这是个了不起的提升。
我们希望尽快将我们的修改集成到官方的memcached仓库中去,我们决定在这之前,先将我们对 memcached 的修改发布到 上。
注:
1.LiveJournal(经常被简写为LJ)是一个在互联网上提供写作的虚拟社区服务
2.Brad Fitzpatrick: