Blog Email GitHub

05 Jul 2010
memcached初学习

许多web应用都是将数据保存在RDBMS中,web apps从RDMS中读取数据并经过渲染后返回给客户端,在浏览器中显示。但是随着数据访问量的增大,访问的集中,RDBMS的负担加重,响应速度变慢、显示延迟等问题都会出现。

但是如果我们采取以下做法:每次从RDBMS读取的数据存放在内存中,并且更新内容时,不仅更改RDBMS并且同时更改内存中的数据。那么等待用户下次读取数据时,先从内存中获取,如果存在直接返回,如果没有再从RDBMS获取返回。

采取这种做法,响应速度就会得到很大改善。因为磁盘I/O的速度跟内存的读写速度不是一个等级的。

memcached是一个实现以上做法的、高性能、分布式的内存对象缓存系统,目的主要是通过减轻数据库负载来使动态web应用程序提速,任意的数据都是采用key-value形式存储的。

1. memcached特点

1.1 协议简单

采用简单的基于文本行的协议,并没有采用二进制协议或者复杂的XML格式。(目前memcahed开发者们正在策划和实现二进制协议)

1.2 基于libevent的事件处理

使用libevent取代了网络服务器所使用的循环检查架构,从而可以省去对网络的处理,达到不错的性能。(参考 对于此点优势,了解还不是特别深入)

1.3 内置内存存储方式

为了提高性能,memcached都将数据保存在内存中。因此如果重启memcahed、重启操作系统或者断电等操作都会造成数据的丢失。所以说memcached不能永久存储数据。

1.4 memcached不互相通信的分布式

memcached虽然是分布式缓存服务器,但是服务端并没有分布式功能,分布式读和写都由客户端采用特定的算法去实现。另外,服务器端并不会通信以达到共享信息。我的看法是:memcached是个缓存系统,并不需要像NoSQL达到“最终一致性”,所以每个服务器保存的内容都是不一样的,都只是部分数据。另外如果一个服务器出现异常,也不影响正常程序,因为我们丢失的仅仅是“缓存”数据。

2 实现memcached的相关理论和方式

2.1 内存存储方式

在使用内存存储数据的时候,就涉及到如何分配内存。如果采用简单的malloc和free,就会导致内存碎片,反而会加重操作系统管理内存的负担。而memcached采用的内存分配机制Slab Allocation,就是为了解决这种问题。具体的工作原理是:按照预先设定的大小,将内存分割成特定长度的块,并且把长度相同的块分成组;这些内存并不会释放,而是重复利用。memcached根据客户端发来数据的大小去选择合适的块去保存,然后将数据缓存其中。

Slab Allocation解决了内存碎片的问题,但是却带来了另外一个问题:由于分配块的大小是固定大小,而客户端发来缓存的数据大小却是随机的,就会造成内存浪费的问题,比如一个100k的数据保存一个128k大小的块中,造成了28k内存空间的浪费。对于这个问题还没有良好的解决方案。但是如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的情况下,只要使用适合数据大小的组的列表,就可以减小浪费。

2.2 删除缓存数据方式

内存空间不是无限的,当然有个上限或者一个指定值,所以删除缓存数据是必须的。memcached采用的方式是:LRU(Least Recently Used),也就是说:当memcached内存容量达到指定值后,需要空间来缓存新的数据时,去删除那些近期最少使用的数据,用它的内存空间来保存新的缓存数据。

2.3 客户端分布式算法

memcached分服务端和客户端,服务端仅仅负责存储和读取。至于分布式存储,是客户端需要做的事情。分布式存储,需要保证实现一个特性:良好的伸缩性和扩展性,我对此特性的理解就是:在memcached服务器中,如果新添一台服务器,简简单单通过配置就可以,而不影响其他服务器的正常运行,缓存数据的键也会均匀分散到各个服务器;如果一台memcached服务器因为故障无法连接,也不会影响其他缓存,系统依然能继续运行。

下面就简单介绍下实现该特性比较完善的算法:Consistent Hashing,nosql中需要实现分布的数据库也多采用这种算法。其基本原理是:首先计算各个服务器的哈希值,并将其配置到0 ~ 2^32 的圆上(不是线段哦),然后采用同样的方法求出数据的键的哈希值,映射到圆上,沿着映射的位置顺时针查找,将数据保存在最近的一个服务器上。如果超过2^32任然找不到,就保存到第一台服务器上。读取数据时,也是同样的道理,计算读取数据的键的哈希值,根据哈希值找到对应服务器,读取值。

采取此种算法的好处就是,不管添加服务器或者减少服务器或者服务器故障,都不会影响缓存数据,不影响系统的正常运行。并且,如果我们让一个服务器(物理节点)对应n个虚拟节点,随机分布在0 ~ 2^32的圆上,这样就可以解决分布数据不均匀的问题。