1 Memcached简介


1. Memcached是高性能分布式对象缓存系统,官方主页http://memcached.org/,主页上对它的介绍是:为来自数据库调用、API调用、或者页面渲染的结果的小块任意数据(字符串、对象)的一个内存中的键值存储。

许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。

这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。


2. Memcached的基本工作原理

如下为memcached的工作流程示意图,

wKiom1NDVfiwK4O1AAEWf3sN1Q8388.jpg

首次访问:从RDBMS中取得数据保存在memcached;

第二次后:从memcached中取得数据显示页面。类似squid在web网页缓存中的作用。


3. Memcached的特征

memcached作为高速运行的分布式缓存服务器,具有以下的特点。

  • 协议简单

    memcached的服务器客户端通信并不使用复杂的XML等格式,而使用简单的基于文本行的协议。因此,通过telnet也能在memcached上保存数据、取得数据。


  • 基于libevent的事件处理

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


  • 内置内存存储方式

    为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。


  • memcached互不通信的分布式

    memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。分布式的实现取决于客户端。如下为memcached分布式实现示意图,

wKioL1NDVyjAgZfAAAClK5lQWd4468.jpg



4. 缓存与数据库的同步

比较保险的做法是:查询的时候(读)从缓存中取,add、updae、delete的时候(写)同时操作缓存与DB。

当然你也可以定时同步缓存与DB的数据,不同的业务应该有不同的选择。


2 Memcached安装、启动


2.1 安装前准备

memcached依赖于libevent库,在安装前需保证libevent库已经安装

yum install libevent libevent-devel


2.2 安装

可以从官网下载到最新版本的memcached,直接下载请戳http://www.memcached.org/files/memcached-1.4.17.tar.gz,解压、编译安装:

tar zxvf memcached-1.4.17.tar.gz ; cd memcached-1.4.17

./configure --prefix=/usr/local/memcached

make

make install


2.3 启动

启动前防火墙设置:关闭iptables或者添加以下内容到iptables中:

-A INPUT -m state NEW -m tcp -p tcp --dport 11211 -j ACCEPT

-A INPUT -m state NEW -m udp -p udp --dport 11211 -j ACCEPT


/usr/local/memcached/bin/memcached -u root -p 11211 -m 512m -vv

slab class   1: chunk size        80 perslab   13107
slab class   2: chunk size       104 perslab   10082
slab class   3: chunk size       136 perslab    7710
slab class   4: chunk size       176 perslab    5957
slab class   5: chunk size       224 perslab    4681
slab class   6: chunk size       280 perslab    3744
slab class   7: chunk size       352 perslab    2978
slab class   8: chunk size       440 perslab    2383
slab class   9: chunk size       552 perslab    1899
slab class  10: chunk size       696 perslab    1506
slab class  11: chunk size       872 perslab    1202
slab class  12: chunk size      1096 perslab     956
slab class  13: chunk size      1376 perslab     762
slab class  14: chunk size      1720 perslab     609
slab class  15: chunk size      2152 perslab     487
slab class  16: chunk size      2696 perslab     388
slab class  17: chunk size      3376 perslab     310
slab class  18: chunk size      4224 perslab     248
slab class  19: chunk size      5280 perslab     198
slab class  20: chunk size      6600 perslab     158
slab class  21: chunk size      8256 perslab     127
slab class  22: chunk size     10320 perslab     101
slab class  23: chunk size     12904 perslab      81
slab class  24: chunk size     16136 perslab      64
slab class  25: chunk size     20176 perslab      51
slab class  26: chunk size     25224 perslab      41
slab class  27: chunk size     31536 perslab      33
slab class  28: chunk size     39424 perslab      26
slab class  29: chunk size     49280 perslab      21
slab class  30: chunk size     61600 perslab      17
slab class  31: chunk size     77000 perslab      13
slab class  32: chunk size     96256 perslab      10
slab class  33: chunk size    120320 perslab       8
slab class  34: chunk size    150400 perslab       6
slab class  35: chunk size    188000 perslab       5
slab class  36: chunk size    235000 perslab       4
slab class  37: chunk size    293752 perslab       3
slab class  38: chunk size    367192 perslab       2
slab class  39: chunk size    458992 perslab       2
slab class  40: chunk size    573744 perslab       1
slab class  41: chunk size    717184 perslab       1
slab class  42: chunk size   1048576 perslab       1
<26 server listening (auto-negotiate)
<27 server listening (auto-negotiate)
<28 send buffer was 112640, now 268435456
<29 send buffer was 112640, now 268435456
<28 server listening (udp)
<28 server listening (udp)
<28 server listening (udp)
<29 server listening (udp)
<29 server listening (udp)
<29 server listening (udp)
<28 server listening (udp)
<29 server listening (udp)


memcached常用参数说明:

-d:启动一个守护进程.
-m:分配给Memcache使用的内存数量,单位是MB
-u:运行Memcache的用户
-l:监听的服务器IP地址
-p:设置Memcache监听的端口,默认为11211
-c:最大运行的并发连接数,默认是1024,可按照服务器的负载量来设定.
-P:设置保存Memcache的pid文件

-vv:详细的输出信息显示


2.4 制作memcached启动脚本

cat memcached.sh:

#!/bin/bash
# Description: start|stop|restart the Memcached services.
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
. /etc/rc.d/init.d/functions

memcached="/usr/local/bin/memcached"
[ -e $memcached ] || exit 1

start()
{
    echo "Start memcached:"
    daemon $memcached -d -m 521m -u root -l localhost -p 11211 -c 1500 -P /tmp/memcached.pid
}

stop()
{
    echo "Stop memcached:"
    #kill -9 `ps aux | grep memcached | awk '{print $2}'`
    killproc memcached
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        sleep 3
        start
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart}"
        exit 1
esac
exit $?

添加可执行权限:chmod +x memcached.sh

拷贝到/etc/rc.d/init.d/目录下:cp memcached.sh /etc/rc.d/init.d/memcached

将memcached加入系统启动项:

chkconfig  --add memcached

chkconfig --level 35 memcached on

启动memcached:/etc/init.d/memcached start

Start memcached:
                                                                                                   [  OK  ]

查看memcached是否启动:ps aux | grep memcached


3 Memcached分布式集群实现


由于Memcached本身没有内置分布式功能,多个memcached之间互不通信,导致无法实现使用多台Memcache服务器来存储不同的数据,最大程度的使用相同的资源;无法同步数据,容易造成单点故障。如果需要实现高可用性,需要通过其他的代理或者客户端来实现:


3.1 使用magent来实现Memcached集群


1. magent是一款简单且有效的memcached服务器代理软件,项目主页请戳http://code.google.com/p/memagent/


2. magent的特征

  • 能够保持与memcached服务器的连接

  • 支持以下的memcached命令:

    get gets 
    delete 
    incr decr
    add set replace prepebd append
    cas
  • 基于libevent库的事件驱动机制
  • 支持ketama算法
  • 备份服务器工厂
  • unix域socket


3. magent编译安装

mkdir magent && cd magent/

wget http://memagent.googlecode.com/files/magent-0.6.tar.gz

tar zxvf magent-0.6.tar.gz
/sbin/ldconfig
sed -i "s#LIBS = -levent#LIBS = -levent -lm#g" Makefile
make
cp magent /usr/bin/magent
cd ../


注解:编译的时候可能报错:

gcc -Wall -g -O2 -I/usr/local/include  -c -o magent.o magent.c
magent.c: In function ‘writev_list’:
magent.c:729: error: ‘SSIZE_MAX’ undeclared (first use in this function)
magent.c:729: error: (Each undeclared identifier is reported only once
magent.c:729: error: for each function it appears in.)
make: *** [magent.o] Error 1

出错原因是在Linux系统上,SSIZE_MAX是定义在limits.h文件中,需要加入“#include <limits.h>”到magent.c中


4. magent命令参数说明:

magent  命令参数说明:
-h this message   
-u uid   
-g gid   
-p port, default is 11211. (0 to disable tcp support)   
-s ip:port, set memcached server ip and port   
-b ip:port, set backup memcached server ip and port   
-l ip, local bind ip address, default is 0.0.0.0  
-n number, set max connections, default is 4096  
-D do not go to background   
-k use ketama key allocation algorithm   
-f file, unix socket path to listen on. default is off   
-i number, max keep alive connections for one memcached server, default is20  
-v verbose  


5. 集群实例测试

memcached -m 1 -u root -d -l 127.0.0.1 -p 11211
memcached -m 1 -u root -d -l 127.0.0.1 -p 11212
memcached -m 1 -u root -d -l 127.0.0.1 -p 11213
magent -u root -n 51200 -l 127.0.0.1 -p 12000 -s 127.0.0.1:11211 -s 127.0.0.1:11212 -b 127.0.0.1:11213

注解:

1、分别在11211、11212、11213端口启动3个Memcached进程,在12000端口开启magent代理程序;


2、11211、11212端口为主Memcached,11213端口为备份Memcached;


3、连接上12000的magent,set key1和set key2,根据哈希算法,key1被写入11212和11213端口的Memcached,key2被写入11212和11213端口的Memcached;


4、当11211、11212端口的Memcached死掉,连接到12000端口的magent取数据,数据会从11213端口的Memcached取出;


5、当11211、11212端口的Memcached重启复活,连接到12000端口,magent会从11211或11212端口的Memcached取数据,由于这两台Memcached重启后无数据,因此magent取得的将是空值,尽管11213端口的Memcached还有数据。可采用定时维护服务器,恢复memcached。


6、memcached和magent可以采用混合的方式来实现分布式集群,如下为模型图,

wKioL1NDZOXDR7-NAACaEZJI_iU660.jpg



6. 测试流程

[root@rango ~]# telnet 127.0.0.1 12000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
stats
memcached agent v0.4
matrix 1 -> 127.0.0.1:11211, pool size 0
matrix 2 -> 127.0.0.1:11212, pool size 0
END
set key1 0 0 8
rangochen
STORED
set key2 0 0 8
rangochen
STORED
quit
Connection closed by foreign host.


[root@rango ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key1
END
get key2
VALUE key2 0 8
rangochen
END
quit
Connection closed by foreign host.


[root@rango ~]# telnet 127.0.0.1 11212
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key1
VALUE key1 0 8
rangochen
END
get key2
END
quit
Connection closed by foreign host.


[root@rango ~]# telnet 127.0.0.1 11213
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key1
VALUE key1 0 8
rangochen
END
get key2
VALUE key2 0 8
rangochen
END
quit
Connection closed by foreign host.


模拟11211、11212端口的Memcached死掉
[root@rango ~]# ps -ef | grep memcached
root      6589     1  0 01:25 ?        00:00:00 memcached -m 1 -u root -d -l 127.0.0.1 -p 11211
root      6591     1  0 01:25 ?        00:00:00 memcached -m 1 -u root -d -l 127.0.0.1 -p 11212
root      6593     1  0 01:25 ?        00:00:00 memcached -m 1 -u root -d -l 127.0.0.1 -p 11213
root      6609  6509  0 01:44 pts/0    00:00:00 grep memcached
[root@rango ~]# kill -9 6589
[root@rango ~]# kill -9 6591
[root@rango ~]# telnet 127.0.0.1 12000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key1 
VALUE key1 0 8
rangochen
END
get key2
VALUE key2 0 8
rangochen
END
quit
Connection closed by foreign host.


模拟11211、11212端口的Memcached重启复活
[root@rango ~]# memcached -m 1 -u root -d -l 127.0.0.1 -p 11211
[root@rango ~]# memcached -m 1 -u root -d -l 127.0.0.1 -p 11212
[root@rango ~]# telnet 127.0.0.1 12000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key1
END
get key2
END
quit
Connection closed by foreign host.


3.2 java客户端


3.2.1 XMemcached是一个基于Java nio的memcached客户端。它线程安全,结构简单,支持所有的memcached文本协议和二进制协议,并且有比较优异的性能表现。它还支持一些高级特性,如JMX、动态增删节点、客户端统计以及nio连接池等。


3.2.2 与同是基于java nio的spymemcached相比来说,XMemcached具有以下特点:
1)API模型是同步,异步的API使用比较繁琐,在memcached协议加入noreply后,异步模型的存在价值就更可疑了;      
2)支持设置memcached的节点权重;      
3)支持动态增删节点,可以通过编程或者JMX;      
4)支持nio连接池,并且允许让用户对网络层面有更多的控制,提供更多的性能优化选项;      
5)支持客户端数据统计;      
6)支持Kestrel,Kestrel是一个scala编写的MQ server。


3.2.3 简单实例
MemcachedClient client;
    try {
     client = new XMemcachedClient("localhost",11211);//默认端口
    // store a value for one hour(synchronously).
     String someObject = "缓存这个一个小时可以吗?";
     client.set("key", 3600, someObject);

     // Retrieve a value.(synchronously).
     Object getSomeObject = client.get("key");
     // delete
     client.delete("key");
     System.out.println(getSomeObject.toString());
    } catch (TimeoutException e) {
     e.printStackTrace();
    } catch (InterruptedException e) {
     e.printStackTrace();
    } catch (MemcachedException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
以上代码是没有经过Spring管理的,XMemcachedClient是一个常驻程序。所以可以考虑将它置入到Spring中,配置方式前面已讲过。
使用方式见下: 
private MemcachedClient memcachedClient;

public void setMemcachedClient(MemcachedClient memcachedClient) {
    this.memcachedClient = memcachedClient;
}
注入后直接可使用。


Memcached监控


Memcached的监控方式有多种,对应单机和分布式集群配置:


4.1 使用memcache.php来监控memcached


这是一种非常方便、快捷的memcached监控方式,下载该文件请戳http://livebookmark.net/memcachephp/memcachephp.zip

下载完后将该文件移动到网页存放目录即可,如/var/www/html/(apache)。在使用之前需要设置登录账户、密码等配置信息:vim memcache.php:

......

define('ADMIN_USERNAME','memcache');    // Admin Username
define('ADMIN_PASSWORD','131415');      // Admin Password

......

$MEMCACHE_SERVERS[] = '192.168.56.1:11211'; // 添加要监控的memcached服务器ip及端口
$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; //集群环境下添加多个ip及端口


登录http://ip/memcache.php ,查看相应的监控信息。


4.2 使用Nagios的memcached监控插件来实现


1. 可从以下地址下载该插件,请戳 http://cpan.uwinnipeg.ca/cpan/authors/id/Z/ZI/ZIGOROU/Nagios-Plugins-Memcached-0.02.tar.gz


2. 需要保证系统安装有perl环境:

#rpm -q perl

perl-5.10.1-136.el6.i686

#which perl

/usr/bin/perl


3. 编译安装:

#tar zxvf Nagios-Plugins-Memcached-0.02.tar.gz ; cd Nagios-Plugins-Memcached-0.02

#cat README:

......

INSTALLATION

To install this module, run the following commands:

    perl Makefile.PL
    make
    make test
    make install

......

#perl Makefile.PL

报错:Can't locate CPAN.pm in @INC (@INC contains: inc /usr/local/lib/perl5 /usr/local/share/perl5 /usr/lib/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib/perl5 /usr/share/perl5 .) at inc/Module/AutoInstall.pm line 635, <STDIN> line 1.

原因是没有安装相应的perl的CPAN模块,解决:yum install perl-CPAN


#make

编译。会下载一些编译所需要的文件。


#make install

安装。从输出信息中可以知道,默认会将check_memcached安装到/usr/bin目录下,将该文件拷贝到Nagios的libexec目录下:cp /usr/bin/check_memcached /usr/local/nagios/libexec/


测试check_memcached:/usr/local/nagios/libexec/check_memcached -h

报以下错误:Base class package "Nagios::Plugin" is empty.
    (Perhaps you need to 'use' the module which defines that package first,
    or make that module available in @INC (@INC contains: /usr/local/nagios/libexec/../lib /usr/local/lib/perl5 /usr/local/share/perl5 /usr/lib/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib/perl5 /usr/share/perl5 .).
 at /usr/local/share/perl5/Nagios/Plugins/Memcached.pm line 6
BEGIN failed--compilation aborted at /usr/local/share/perl5/Nagios/Plugins/Memcached.pm line 6.
Compilation failed in require at /usr/local/nagios/libexec/check_memcached line 12.
BEGIN failed--compilation aborted at /usr/local/nagios/libexec/check_memcached line 12.

原因是缺少perl的nagios模块:yum search perl-nagios

Loading mirror speeds from cached hostfile
 * epel: mirrors.hust.edu.cn
 * rpmforge: mirrors.neusoft.edu.cn
======================== N/S Matched: perl-nagios =========================
perl-Nagios-NSCA.noarch : Nagios::NSCA Perl module
perl-Nagios-Object.noarch : Nagios::Object - Nagios object configuration
                          : parsing
perl-Nagios-Plugin.noarch : Family of perl modules to streamline writing
                          : Nagios
perl-Nagios-Plugin-WWW-Mechanize.noarch : Login to a web page as a user and
                                        : get data as a Nagios plugin

  Name and summary matches only, use "search all" for everything.

安装perl-Nagios-Plugin.noarch:yum install perl-Nagios-Plugin.noarch

再执行测试,报以下错误:

Can't locate Carp/Clan.pm in @INC (@INC contains: /usr/local/nagios/libexec/../lib /usr/local/lib/perl5 /usr/local/share/perl5 /usr/lib/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib/perl5 /usr/share/perl5 .) at /usr/local/share/perl5/Nagios/Plugins/Memcached.pm line 8.
BEGIN failed--compilation aborted at /usr/local/share/perl5/Nagios/Plugins/Memcached.pm line 8.
Compilation failed in require at /usr/local/nagios/libexec/check_memcached line 12.
BEGIN failed--compilation aborted at /usr/local/nagios/libexec/check_memcached line 12.

原因是没有安装perl的carp:clan包,安装:yum install perl-Carp-Clan.noarch

最后安装cache:memcached模块:yum install perl-Cache-Memcached.noarch

执行:/usr/local/nagios/libexec/check_memcached -h

check_memcached 0.02 [http://search.cpan.org/dist/Nagios-Plugins-Memcached/bin/check_memcache]

This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

Usage: check_memcached [-H host] [-w warnings] [-c critical] [--size-warnng size-warnng] [--size-critical size-critical] [--hit-warning hit-warning] [--hit-critical hit-critical] [-t timeout] [-v] [-h] [-?] [-V] [--extra-opts section@config_file]

......


4. 配置Nagios:

4.1 修改commands.cfg文件,添加监控命令定义:vim /usr/local/nagios/etc/objects/commands.cfg

#check response time(msec) for memcached
define command {
        command_name    check_memcached_response_11211
        command_line    /usr/local/bin/check_memcached -H 192.168.56.1 -w 300 -c 500
        }
#check cache size ratio(bytes/limit_maxbytes[%]) for memcached
define command {
        command_name    check_memcached_11211
        command_line    $USER1$/check_memcached -H 192.168.56.1:11211 --size-warning 80 --size-critical 90
        }
#check cache hit ratio(get_hits/cmd_get[%]) for memcached
define command {
        command_name    check_memcached_hit

        command_line    /usr/local/bin/check_memcached -H 192.168.56.1 --hit-warning 10 --size-critical 5
        }

注解:以上三个命令分别为:监控memcached是否有响应、监控memcached的内存使用比例 、监控memcached的击中率。


4.2 添加主机和服务:vim /usr/local/nagios/etc/objects/memcached.cfg

define host{
        use             linux-server
        host_name       memcache
        alias           Memcache_server
        address         192.168.56.1
        }

define service{
        use             generic-service
        host_name       memcache
        service_description     Memcached_response
        check_command   check_memcached_response_11211
        }

define service{
        use             generic-service
        host_name       memcache
        service_description     Memcached_size
        check_command   check_memcached_11211
        }
define service{
        use     generic-service
        host_name       memcache

        service_description     Memcached_hit
        check_command   check_memcached_hit
        }


4.3 将memcached.cfg添加到Nagios主配置文件中:

cfg_file=/usr/local/nagios/etc/objects/memcached.cfg 


4.4 检查Nagios配置是否有误:/usr/local/nagios/bin/nagios -v /usr/local/nagios/etc/nagios.cfg


4.5 重启Nagios,并登录Nagios web监控界面查看:

/etc/init.d/nagios restart

登录:http://localhost/nagios


4.3 使用MemAdmin监控管理工具来实现


MemAdmin是一款基于 PHP5 & JQuery、 可视化的Memcached管理与监控工具,使用PHP开发,体积小,操作简单。主页请戳http://www.junopen.com/memadmin/


主要功能:

  • 服务器参数监控:STATS、SETTINGS、ITEMS、SLABS、SIZES实时刷新

  • 服务器性能监控:GET、DELETE、INCR、DECR、CAS等常用操作命中率实时监控

  • 支持数据遍历,方便对存储内容进行监视

  • 支持条件查询,筛选出满足条件的KEY或VALUE

  • 数组、JSON等序列化字符反序列显示

  • 兼容memcache协议的其他服务,如Tokyo Tyrant (遍历功能除外)

  • 支持服务器连接池,多服务器管理切换方便简洁

具体配置安装和使用不再赘述,可自行查看相关文档,lol


5 总结


本文就Memcached原理、安装、分布式安装和监控等一一较为详细的介绍,关于高阶的Memcached内部实现原理,还有待研究和学习。