Memcached介绍

Memcached介绍

1.memcached是什么?

memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。现在已成为 mixi、 hatena、 Facebook、 Vox、LiveJournal等众多服务中提高Web应用扩展性的重要因素。

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

这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。力,让Memcache作为一个缓存区域,把部分信息保存在内存中,在前端能够迅速的进使用Memcache的网站一般流量都是比较大的,为了缓解数据库的压行存取。

 

2. 协议简单

memcached的服务器客户端通信并不使用复杂的XML等格式,而使用简单的基于文本行的协议。

协议文档位于memcached的源代码内,也可以参考以下的URL。

http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

3. 基于libevent的事件处理

libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。 memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍,可以参考Dan Kegel的The C10K Problem。

libeventhttp://www.monkey.org/~provos/libevent/

The C10K Problemhttp://www.kegel.com/c10k.html

4. 内置内存存储方式

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

5. memcached不互相通信的分布式

memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。

 

图2 memcached的分布式

安装memcached

memcached的安装比较简单,这里稍加说明。

memcached支持许多平台。

l         Linux

l         FreeBSD

l         Solaris (memcached 1.2.5以上版本)

l         Mac OS X

另外也能安装在Windows上。

1. LinuxMemcache服务器端的安装

memcached安装与一般应用程序相同,configure、make、make install就行了。


服务器端主要是安装memcache服务器端,目前的最新版本是 memcached-1.4.8 。
下载:http://www.danga.com/memcached/dist/memcached-1.2.2.tar.gz
另外,Memcache用到了libevent这个库用于Socket的处理,所以还需要安装libevent,libevent的最新版本是libevent-1.4。(如果你的系统已经安装了libevent,可以不用安装)
官网:http://www.monkey.org/~provos/libevent/
下载:http://www.monkey.org/~provos/libevent-1.3.tar.gz

1.分别把memcached和libevent下载回来,放到你指定的目录中,例如: /tmp 目录下:

2.先安装libevent:
# tar zxvf libevent-1.2.tar.gz
# cd libevent-1.2   //进入linux系统中libevent所在目录
# ./configure --prefix=/usr/local/libevent
# make
# make install

3.测试libevent是否安装成功:
# ls -al /usr/lib | grep libevent


lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent-1.2.so.1 -> libevent-1.2.so.1.0.3
-rwxr-xr-x 1 root root 263546 11?? 12 17:38 libevent-1.2.so.1.0.3
-rw-r–r– 1 root root 454156 11?? 12 17:38 libevent.a
-rwxr-xr-x 1 root root 811 11?? 12 17:38 libevent.la
lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent.so -> libevent-1.2.so.1.0.3
还不错,都安装上了。

4.安装memcached,同时需要安装中指定libevent的安装位置:
# cd /tmp
# tar zxvf memcached-1.2.0.tar.gz
# cd memcached-1.2.0       //进入linux系统中memcached所在目录
# ./configure –-prefix=/usr/local/memcached  --with-libevent=/usr/local/libevent
# make
# make install
如果中间出现报错,请仔细检查错误信息,按照错误信息来配置或者增加相应的库或者路径。

5.测试是否成功安装memcached:
# ls -al /usr/local/mem*
-rwxr-xr-x 1 root root 137986 11?? 12 17:39 /usr/local/bin/memcached
-rwxr-xr-x 1 root root 140179 11?? 12 17:39 /usr/local/bin/memcached-debug

6.作软连接,否则运行memcached的时候将找不到libevent模块(注意你安装的libevent 的版本,以下为libevent安装目录中libevent-1.2.so.1)

    # ln -s /usr/local/libevent/lib/libevent-1.2.so.1 /usr/lib

7.启动memcached (/usr/local/memcached为memcached的安装路径,到memcached的bin中启动memcached)

# /usr/local/memcached/bin/memcached -d -m 256 -p 11211 -u root

2. Windows下的Memcache安装


1. 下载memcache的windows稳定版 (在http://www.splinedancer.com/memcached-win32/  下载 memcached 1.2.4 Win32 Beta Binaries),解压放某个盘下面,比如在c:\memcached
2. 在终端(也即cmd命令界面)下输入 ‘c:\memcached\memcached.exe -d install’ 安装
3. 再输入: ‘c:\memcached\memcached.exe -d start’ 启动。NOTE: 以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。

3. Memcached启动、关闭参数

启动参数注释如下:

-p <num>  指定服务TCP端口,默认为11211

-U <num>   指定服务UDP端口  默认11211表示打开,设置0表示关闭

-s <file> 指定unix domain socket的文件名,则关闭端口绑定

-a <mask> 文件属性屏蔽字。8进制,默认0700(和-s配合使用)

-l <ip_addr>  指定监听IP地址(默认对所有网卡IP都生效)

-d   run as a daemon 以精灵程序的方式运行

-r   设置coredump文件SIZE至上限

-u <username> 指定进程用户身份(只有以root运行时才有效,且root身份必须指定用户身份,memcached禁止以进程用户身份为root)

-m <num>  分配给memcached用作缓存的内存大小,单位为MB。默认64MB。

注意32位系统最大可管理的内存上限为3G,预留一些,测试分配2.8G是有效的。不要给memcached分配超过机器内存数,否则会自动使用swap分区,让性能大降,产生超时。(调优参数)

-M  LRU算法开关。缓存满时不使用LRU算法替换旧的数据,返回错误

每个slab内的数据使用LRU,不是全局的。因此,在一些情况下,反而会影响命中率(例如多数KEY-VALUE大小近似,都保存在同一slab的情况下)

-c <num>   最大并发连接数。默认1024。 memcached使用libevent, 可以支持10K个连接。且memcached推荐客户端缓存连接(长连接)(调优参数)

-k   是否调用mlockall()锁定内存页。

     注意:如果锁定了内存页,则若申请内存大小超过锁定值(可以通过ulimit -l查看)时就有可能失败(不建议使用。默认ulimit -l只有64KB)

-v    输出详细信息(在event轮询时输出错误或警告信息,以daemon方式运行无效)

-vv   输出更详细的信息(包括输出客户端的命令及回应信息)

-vvv  输出最详细的信息(包括输出内部的状态信息)

-h   打印版本、帮助信息及退出

-i   打印memcached及libevent的licence

-P <file> 保存进程ID到指定文件。与-d配合使用

-f <factor>   chunk size的增长因子(合理范围1.05~2,默认:1.25)(1.2版本之前默认是2)

(调优参数)

-n <bytes>  每个ITEM保存数据占用的最小空间。最小空间大小=KEY的长度+value的长度+flags的长度(默认为48字节) 而每个ITEM的实际占用空间为 ITEM保存占用数据保存的最小空间 + ITEM结构占用的空间 = 48 + 32 = 80字节。因此chunk size的默认初始值为80字节(调优参数)

-L    尝试使用大容量内存页(如果可能的话)。增加内存页容量可以减少虚存-物理内存映射页表缓冲命中失败(TLB-MISSED)的次数, 并提高性能。但memcached会在启动的时候立即向OS申请-m参数指定的最大缓存块。(调优参数)

-D <char>  指定统计报告时ID同KEY之间的分隔符。默认的分隔符为“:”。 如果指定了此参数,统计采集功能就会自动启动;否则,可以通过发送“stats detail on”命令启动统计采集功能。

-t <num>  默认会创建4个工作线程,主线程用于监听客户建立的连接请求、accpet请求,然后通过管道通知子线程,由子线程处理读写请求。 memcached的多线程主要是通过实例化多个libevent实现的,分别是一个主线程和n个工作(workers)线程,无论是主线程还是workers线程全部通过libevent 管理网络事件, 实际上每个线程都是一个单独的libevent实例。建议不要超过系统CPU的个数。(调优参数)

-R  每次事件触发响应新连接的最大数目。设置此限制是防止其他I/O事件“挨饿”,得不到响应。每个工作线程都单独建立了libevent事件触发。(调优参数,一般不需要调整)

-C    Disable use of CAS 关闭'CAS'指令。意思是说如果这个值我最后一次取的没有被修改的话才存储这个值,比如我先获取一个key为”update_time”的值,然后有其他进程修改了这个值,此时我再调用cas设置这个值时则会返回一个EXSISTS的错误表示修改失败。默认支持cas指令。则每次value的修改,都会记录一个CAS序列号(CAS_UNIQUE)。gets指令会返回CAS_UNIQUE。

-b   指定监听队列长度(listen()的参数)。默认为1024。(调优参数。不需要调整,内核对监听队列长度有个上限)

-B    指定绑定的memcached协议。包括:ascii: 文本协议;binary: 二进制协议;auto: 自动检测(默认选项)

-I    改变slab page的容量大小,以调整ITEM容量的最大值,默认为1MB。设置参数:<number>[k|K|m|M]不能少于1024bytes(即1K),不能大于128MB。memcached不推荐大于1MB,大于1MB增加了最低的内存要求,并会减少记忆效率。调优参数)注:低版本memcached 如memcached-1.2.6不支持该参数,1.4以上可以;测试时,我分配了2M的Page,但是实际上缓存的数据大小仍不能大于1M,其他资料说memcached缓存的item不能大于1M

-S   Turn on Sasl authentication

     启动SSL认证。需要在编译支持SSL。使用SSL认证时,只能使用二进制协议,不能使用文本协议。

Java使用Memcached的例子

许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。仅仅memcached网站上列出的语言就有

l          Perl

l          PHP

l          Python

l          Ruby

l          C#

l          C/C++

l          Lua

l          Java

等等。

1.下载java客户端

可以从https://github.com/gwhalin/Memcached-Java-Client下载java客户端

 

点击Downloads进入下载页面

 

选择要下载的版本下载,一般选择版本稍低的包,版本高的还有些bug,不够稳定。

解压,获取jar包。建立一个基本的Java工程吧。引入jar包。如图示:

 

?

2.Java中如何使用Memcached

package com.memcahce;

import java.io.File;

import java.io.FileWriter;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import java.util.Map;

import com.danga.MemCached.MemCachedClient;

import com.danga.MemCached.SockIOPool;

public class MemCachedManager {

       // 创建全局的唯一实例  

    protected static MemCachedClient mcc = new MemCachedClient();

    protected static MemCachedManager memCachedManager = new MemCachedManager();  

    // 设置与缓存服务器的连接池  

    static {  

        // 服务器列表和其权重

        String[] servers = { "192.168.1.107:11211","127.0.0.1:11211" };  

        Integer[] weights = { 3 };

        // 获取socke连接池的实例对象  

        SockIOPool pool = SockIOPool.getInstance();  

        // 设置服务器信息  

        pool.setServers(servers);  

        pool.setWeights(weights);  

        // 设置初始连接数、最小和最大连接数以及最大处理时间  

        pool.setInitConn(5);  

        pool.setMinConn(5);  

        pool.setMaxConn(250);

              //设置最大空闲时间为6小时

        pool.setMaxIdle(1000 * 60 * 60 * 6);  

        // 设置主线程的睡眠时间  

        pool.setMaintSleep(30);  

        // Tcp的规则就是在发送一个包之前,本地机器会等待远程主机对上一次发送的包的确认信息到来;这个方法就可以关闭套接字的缓存,以至这个包准备好了就发;  

        pool.setNagle(false);  

//连接建立后对超时的控制

        pool.setSocketTO(3000);

//连接建立时对超时的控制

        pool.setSocketConnectTO(0);  

        // initialize the connection pool,初始化一些值并与MemcachedServer段建立连接  

        pool.initialize();  

        // 压缩设置,超过指定大小(单位为K)的数据都会被压缩  

        mcc.setCompressEnable(true);  

        mcc.setCompressThreshold(64 * 1024);  

    }  

    /** 

     * 保护型构造方法,不允许实例化! 

     *  

     */ 

    protected MemCachedManager() {  

    }  

    /** 

     * 获取唯一实例. 

     *  

     * @return 

     */ 

    public static MemCachedManager getInstance() {  

        return memCachedManager;  

    }  

    /** 

     * 添加一个指定的值到缓存中. 

     *  

     * @param key 

     * @param value 

     * @return 

     */ 

    public boolean add(String key, Object value) {  

        return mcc.add(key, value);  

    }  

   

    public boolean add(String key, Object value, Date expiry) {  

        return mcc.add(key, value, expiry);  

    }  

    public boolean set(String key,Object value){

           return mcc.add(key, value);

    }

    public boolean replace(String key, Object value) {  

        return mcc.replace(key, value);  

    }  

    public boolean replace(String key, Object value, Date expiry) {  

        return mcc.replace(key, value, expiry);  

    }

    /**

     * 删除缓存中指定的值

     * @param key

     * @param value

     * @param expiry

     * @return

     */

    public boolean delete(String key, Date expiry){

           return mcc.delete(key, expiry);

    }

   

    /** 

     * 根据指定的关键字获取对象. 

     *  

     * @param key 

     * @return 

     */ 

    public Object get(String key) {

        return mcc.get(key);

    }

    public Map<String,Object> get(String[] keys){

           return mcc.getMulti(keys);

    }

    public static void main(String[] args) {  

        try {

               MemCachedManager cache = MemCachedManager.getInstance();

            List<String> list = new ArrayList<String>();//List集合

           //创建文件实例

            File file = new File("E:\\my.txt");

            if (!file.exists()) {

                            file.createNewFile();

                     }else{

                            file.delete();

                     }

                     //文件写入器实例

            FileWriter fw = new FileWriter(file); 

            //测试文件大于1M时,memcached是否仍缓存数据

            for (int i = 0; i < 40000; i++) {

                    String s = "abcderghijklmn"+i;

                   fw.write(s,0,s.length()); //写入文件

                         list.add(s);  //加入集合

                  }

            fw.flush();

           

            cache.add("hello", 23445,new Date(20000));

            cache.add("list", list,new Date(20000));

//cache.delete("hello", new Date(1000));

//cache.add("hello", 234,new Date(15000));

            System.out.println("get value : " + cache.get("hello"));

            List lst = (List)cache.get("list");

            System.out.println("get Size:" + lst.size());

          

              } catch (Exception e) {

                     e.printStackTrace();

              }

    }  

}

3.保存数据

向memcached保存数据的方法有

1.    add

2.    replace

3.    set

public boolean add(String key, Object value) {  

    return mcc.add(key, value);  

}  

public boolean add(String key, Object value, Date expiry) {  

    return mcc.add(key, value, expiry);  

}  

public boolean set(String key,Object value){

    return mcc.add(key, value);

}

public boolean replace(String key, Object value) {  

    return mcc.replace(key, value);  

}  

public boolean replace(String key, Object value, Date expiry) {  

    return mcc.replace(key, value, expiry);  

}

向memcached保存数据时可以指定期限(秒)。不指定期限时,memcached按照LRU算法保存数据。这三个方法的区别如下:

选项

说明

add

仅当存储空间中不存在键相同的数据时才保存

replace

仅当存储空间中存在键相同的数据时才保存

set

与add和replace不同,无论何时都保存

4.获取数据

获取数据可以使用get和get_multi方法。

public Object get(String key) {

    return mcc.get(key);

}

public Map<String,Object> get(String[] keys){

     return mcc.getMulti(keys);

}

一次取得多条数据时使用get_multi。get_multi可以非同步地同时取得多个键值,其速度要比循环调用get快数十倍。

5.删除数据

删除数据使用delete方法,不过它有个独特的功能。

    public boolean delete(String key, Date expiry){

           return mcc.delete(key, expiry);

    }

删除第一个参数指定的键的数据。第二个参数指定一个时间值,在指定的时间内可以禁止使用同样的键保存新数据。此功能可以用于防止缓存数据的不完整。但是要注意,set函数忽视该阻塞,照常保存数据