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。
libevent: http://www.monkey.org/~provos/libevent/
The C10K Problem: http://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. Linux下Memcache服务器端的安装
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函数忽视该阻塞,照常保存数据