您的位置:首页 > 数据库 > Memcache

Memcached剖析

2015-08-22 09:11 676 查看
Memcached

简介

Memcached是一个高性能的分布式内存对象缓存系统,通过在内存中缓存数据减少读取数据库的次数。Memcached基于存储键/值对存储。其守护进程是用C写的,但是客户端已经有很多语言来实现,并通过memcached协议与守护进程通信。



特征

· 协议简单

memcached有两种协议,文本协议和二进制协议。文本协议以换行表示一个请求结束。请求内部参数以空格分隔。为了二进制安全,在二进制数据前要加上长度这个参数,如set x 3 abc\r\n。二进制协议的优点就是高效,因为所有信息都以最少的数据量来表达,且server解析请求时做的是数字比较而不是字符串比较,效率高很多。

· 基于libevent的事件处理

libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。

· 内置内存存储方式

· memcached不互相通信的分布式

注意问题

1.memcached缺乏认证以及安全管制,应该将memcached服务器放置在防火墙后。
2.memcached使用32位元的循环冗余校验(CRC-32)计算键值后,将数据分散在不同的节点上。如果节点存储满了,按照LRU机制替换。
3.由于memcached通常只是当作快取系统使用,所以使用memcached的应用在写回较慢的系统(比如数据库)时,需要额外的工作更新memcached内的数据。
4.memcached中保存的数据都在memcached内置的内存存储空间中,服务器停止运行或崩溃了,这些数据会丢失。

memcached的安装

安装

memcached依赖libevent库。Fedora 8中有现成的rpm包, 通过yum命令安装即可。
$ sudoyum install libevent libevent-devel
memcached的源代码可以从memcached网站上下载。memcached安装与一般应用程序相同,configure、make、make install就行了。
$ wgethttp://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz

$ tar zxf memcached-1.2.5.tar.gz

$ cd memcached-1.2.5

$ ./configure

$ make

$ sudo make install
默认情况下memcached安装到/usr/local/bin下。

启动

从终端输入以下命令,启动memcached。
$/usr/local/bin/memcached -p 11211 -m 64m -vv

slab class 1: chunk size 88 perslab 11915

slab class 2: chunk size 112 perslab 9362

slab class 3: chunk size 144 perslab 7281

中间省略

slab class 38: chunk size 391224perslab 2

slab class 39: chunk size 489032perslab 2

<23 server listening

<24 send buffer was 110592, now 268435456

<24 server listening (udp)

<24 server listening (udp)

<24 server listening (udp)

<24 server listening (udp)
这里显示了调试信息。这样就在前台启动了memcached,监听TCP端口11211 最大内存使用量为64M。
作为daemon后台启动时,只需
$/usr/local/bin/memcached -p 11211 -m 64m -d
这里使用的memcached启动选项的内容如下。
选项
说明
-p
使用的TCP端口。默认为11211
-m
最大内存大小。默认为64M
-vv
用very vrebose模式启动,调试信息和错误输出到控制台
-d
作为daemon在后台启动
上面四个是常用的启动选项,其他还有很多,通过
$/usr/local/bin/memcached -h
命令可以显示。

客户端连接

许多语言都实现了连接memcached的客户端。比如Perl,PHP,Python,Ruby,C#,C/C++,Lua等等。
memcached客户端API:http://www.danga.com/memcached/apis.bml
这里介绍通过mixi正在使用的Perl库链接memcached的方法。
使用Cache::Memcached
Perl的memcached客户端有
Cache::Memcached
Cache::Memcached::Fast
Cache::Memcached::libmemcached
等几个CPAN模块。这里介绍的Cache::Memcached。使用Cache::Memcached连接memcached。
下面的源代码为通过Cache::Memcached连接刚才启动的memcached的例子。
#!/usr/bin/perl

use strict;

use warnings;

use Cache::Memcached;

my $key = "foo";

my $value = "bar";

my $expires = 3600; # 1 hour

my $memcached = Cache::Memcached->new({

servers =>["127.0.0.1:11211"],

compress_threshold => 10_000

});

$memcached->add($key, $value, $expires);

my $ret = $memcached->get($key);

print "$ret\n";
在这里,为Cache::Memcached指定了memcached服务器的IP地址和一个选项,以生成实例。 Cache::Memcached常用的选项如下所示。
选项
说明
servers
用数组指定memcached服务器和端口
compress_threshold
数据压缩时使用的值
namespace
指定添加到键的前缀
另外,Cache::Memcached通过Storable模块可以将Perl的复杂数据序列化之后再保存, 因此散列、数组、对象等都可以直接保存到memcached中。

Memcached的使用

保存数据

向memcached保存数据的方法有add,replace,set
它们的使用方法都相同:
my $add= $memcached->add( '键', '值', '期限' );

my $replace = $memcached->replace( '键', '值', '期限' );

my $set = $memcached->set( '键', '值', '期限' );
向memcached保存数据时可以指定期限(秒)。不指定期限时,memcached按照LRU算法保存数据。 这三个方法的区别如下:
选项
说明
add
仅当存储空间中不存在键相同的数据时才保存
replace
仅当存储空间中存在键相同的数据时才保存
set
与add和replace不同,无论何时都保存

获取数据

获取数据可以使用get和get_multi方法。
my $val= $memcached->get('键');

my $val = $memcached->get_multi('键1', '键2', '键3', '键4', '键5');
get_multi可以非同步地同时取得多个键值, 其速度要比循环调用get快数十倍。

删除数据

删除数据使用delete方法,不过它有个独特的功能。
$memcached->delete('键', '阻塞时间(秒)');
删除第一个参数指定的键的数据。第二个参数指定一个时间值,可以禁止使用同样的键保存新数据。此功能可以用于防止缓存数据的不完整。但是要注意,set函数忽视该阻塞,照常保存数据

增一和减一操作

可以将memcached上特定的键值作为计数器使用。
my $ret= $memcached->incr('键');

$memcached->add('键', 0) unless defined $ret;
增一和减一是原子操作,但未设置初始值时,不会自动赋成0。因此,应当进行错误检查,必要时加入初始化操作。而且,服务器端也不会对超过232时的行为进行检查。

内存分配

Slab Allocation机制

功能有整理内存,重复使用。
memcached使用名为SlabAllocator的机制分配、管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。

原理

SlabAllocation的原理相当简单。将分配的内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk的集合)。
分配到的内存不会释放,而是重复利用。
SlabAllocation的主要术语
Page:分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。
Chunk:用于缓存记录的内存空间。
SlabClass:特定大小的chunk的组。



说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。
memcached根据收到的数据的大小,选择最适合数据大小slab。memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。



缺点

由于分配的是特定长度的内存,因此无法有效利用分配的内存。例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了。



对于该问题目前还没有完美的解决方案。

LazyExpiration

memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期;memcached不会在过期监视上耗费CPU时间。

memcached集群管理

存储方法

下面假设memcached服务器有node1~node3三台, 应用程序要保存键名为“tokyo”“kanagawa”“chiba”“saitama”“gunma”的数据。



首先向memcached中添加“tokyo”。将“tokyo”传给客户端程序库后,客户端实现的算法就会根据“键”来决定保存数据的memcached服务器。服务器选定后,即命令它保存“tokyo”及其值。



同样,“kanagawa”“chiba”“saitama”“gunma”都是先选择服务器再保存。
接下来获取保存的数据。获取时也要将要获取的键“tokyo”传递给函数库。函数库通过与数据保存时相同的算法,根据“键”选择服务器。使用的算法相同,就能选中与保存时相同的服务器,然后发送get命令。只要数据没有因为某些原因被删除,就能获得保存的值。



这样,将不同的键保存到不同的服务器上,就实现了memcached的分布式。memcached服务器增多后,键就会分散,即使一台memcached服务器发生故障无法连接,也不会影响其他的缓存,系统依然能继续运行。

分布式算法

有两种分布式算法,余数计数法和consistent hashing算法。

余数计数法

Hash%numOfNodeà Node of Memcached
余数计算的方法简单,数据的分散性也相当优秀,但也有其缺点。那就是当添加或移除服务器时,缓存重组的代价相当巨大。添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而影响缓存的命中率。

Consistent Hashing

ConsistentHashing如下所示:首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。



添加服务器后,hash变化如下:



从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但Consistent Hashing中,只有在continuum上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响。

mixi案例研究

随着网站访问量的急剧增加,单纯为数据库添加slave已无法满足需要,因此引入了memcached。

架构

mixi使用了许许多多服务器,如数据库服务器、应用服务器、图片服务器、反向代理服务器等。单单memcached就有将近200台服务器在运行。 memcached服务器的典型配置如下:



这些服务器以前曾用于数据库服务器等。随着CPU性能提升、内存价格下降,对旧的数据库进行了替换,替下来的服务器就成为了memcached服务器。这样,可以抑制mixi整体使用的服务器数量的急剧增加,降低管理成本。由于memcached服务器几乎不占用CPU,就将换下来的服务器用作memcached服务器了。

memcached进程

每台memcached服务器仅启动一个memcached进程。分配给memcached的内存为3GB, 启动参数如下:
/usr/bin/memcached-p 11211 -u nobody -m 3000 -c 30720
由于使用了x86_64的操作系统,因此能分配2GB以上的内存。32位操作系统中,每个进程最多只能使用2GB内存。也曾经考虑过启动多个分配2GB以下内存的进程,但这样一台服务器上的TCP连接数就会成倍增加,管理上也变得复杂,所以mixi就统一使用了64位操作系统。
另外,虽然服务器的内存为4GB,却仅分配了3GB,是因为内存分配量超过这个值,就有可能导致内存交换(swap)。memcached启动时,指定的内存分配量是memcached用于保存数据的量,没有包括“slab allocator”本身占用的内存、以及为了保存数据而设置的管理空间。因此,memcached进程的实际内存分配量要比指定的容量要大。
mixi保存在memcached中的数据大部分都比较小。这样,进程的大小要比指定的容量大很多。因此,我们反复改变内存分配量进行验证,确认了3GB的大小不会引发swap,这就是现在应用的数值。

memcached使用方法和客户端

现在,mixi的服务将200台左右的memcached服务器作为一个pool使用。每台服务器的容量为3GB,那么全体就有了将近600GB的巨大的内存数据库。客户端程序库使用Cache::Memcached::Fast,与服务器进行交互;缓存的分布式算法使用Consistent Hashing算法。
应用层上memcached的使用方法由开发应用程序的工程师自行决定并实现。但是,为了防止车轮再造、防止Cache::Memcached::Fast上的教训再次发生,我们提供了Cache::Memcached::Fast的wrap模块并使用。通过Cache::Memcached::Fast维持连接。
Cache::Memcached的情况下,与memcached的连接(文件句柄)保存在Cache::Memcached包内的类变量中。在mod_perl和FastCGI等环境下,包内的变量不会像CGI那样随时重新启动,而是在进程中一直保持。其结果就是不会断开与memcached的连接,减少了TCP连接建立时的开销,同时也能防止短时间内反复进行TCP连接、断开而导致的TCP端口资源枯竭。但是,Cache::Memcached::Fast没有这个功能,所以需要在模块之外将Cache::Memcached::Fast对象保持在类变量中,以保证持久连接。
packageGihyo::Memcached;

use strict;

use warnings;

use Cache::Memcached::Fast;

my @server_list = qw/192.168.1.1:11211 192.168.1.1:11211/;

my $fast; ## 用于保持对象

sub new {

my $self = bless {}, shift;

if ( !$fast ) {

$fast =Cache::Memcached::Fast->new({ servers => \@server_list });

}

$self->{_fast} = $fast;

return $self;

}

sub get {

my $self = shift;

$self->{_fast}->get(@_);

}
上面的例子中,Cache::Memcached::Fast对象保存到类变量$fast中。
公共数据的处理和rehash诸如mixi的主页上的新闻这样的所有用户共享的缓存数据、设置信息等数据,会占用许多页,访问次数也非常多。在这种条件下,访问很容易集中到某台memcached服务器上。访问集中本身并不是问题,但是一旦访问集中的那台服务器发生故障导致memcached无法连接,就会很有问题。Cache::Memcached拥有rehash功能,即在无法连接保存数据的服务器的情况下,会再次计算hash值,连接其他的服务器。但是,Cache::Memcached::Fast没有这个功能。不过,它能够在连接服务器失败时,短时间内不再连接该服务器的功能。
my $fast= Cache::Memcached::Fast->new({

max_failures => 3,

failure_timeout => 1

});
在failure_timeout秒内发生max_failures以上次连接失败,就不再连接该memcached服务器。我们的设置是1秒钟3次以上。
此外,mixi还为所有用户共享的缓存数据的键名设置命名规则,符合命名规则的数据会自动保存到多台memcached服务器中,取得时从中仅选取一台服务器。创建该函数库后,就可以使memcached服务器故障不再产生其他影响。

memcached应用经验

到此为止介绍了memcached内部构造和函数库,接下来介绍一些其他的应用经验。
通过daemontools启动
架构上保证了即使有几台memcached故障不会影响服务,不过对于memcached进程死掉的服务器,只要重新启动memcached,就可以正常运行,所以采用了监视memcached进程并自动启动的方法。于是使用了daemontools。
daemontools是qmail的作者DJB开发的UNIX服务管理工具集, 其中名为supervise的程序可用于服务启动、停止的服务重启等。mixi使用了以下的run脚本来启动memcached。
#!/bin/sh

if [ -f /etc/sysconfig/memcached ];then

. /etc/sysconfig/memcached

fi

exec 2>&1

exec /usr/bin/memcached -p $PORT -u $USER -m $CACHESIZE -c $MAXCONN $OPTIONS
监视mixi使用了名为“nagios”的开源监视软件来监视memcached。
在nagios中可以简单地开发插件,可以详细地监视memcached的get、add等动作。不过mixi仅通过stats命令来确认memcached的运行状态。
definecommand {

command_name check_memcached

command_line $USER1$/check_tcp -H $HOSTADDRESS$ -p 11211 -t 5 -E -s'stats\r\nquit\r\n' -e 'uptime' -M crit

}
此外,mixi将stats目录的结果通过rrdtool转化成图形,进行性能监视,并将每天的内存使用量做成报表,通过邮件与开发者共享。

memcached的性能

memcached的性能十分优秀。看看mixi的实际案例。这里介绍的图表是服务所使用的访问最为集中的memcached服务器。



请求数



流量



TCP连接数
从上至下依次为请求数、流量和TCP连接数。请求数最大为15000qps, 流量达到400Mbps,这时的连接数已超过了10000个。 该服务器没有特别的硬件,就是开头介绍的普通的memcached服务器。此时的CPU利用率为:



CPU利用率
可见,仍然有idle的部分。因此,memcached的性能非常高,可以作为Web应用程序开发者放心地保存临时数据或缓存数据的地方。
兼容应用程序
memcached的实现和协议都十分简单,因此有很多与memcached兼容的实现。一些功能强大的扩展可以将memcached的内存数据写到磁盘上,实现数据的持久性和冗余。以后的memcached的存储层将变成可扩展的(pluggable),逐渐支持这些功能。

几个与memcached兼容的应用程序

repcached为memcached提供复制(replication)功能的patch。
Flared存储到QDBM。同时实现了异步复制和failover等功能。
memcachedb存储到BerkleyDB,还实现了messagequeue。

引用: http://kb.cnblogs.com/page/42731/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: