缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载。缓存是解决这个问题的好办法。
Memcached是什么?
Memcached是由Danga Interactive开发的,高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。
Memcached能缓存什么?
通过在内存里维护一个统一的巨大的hash表,Memcached能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。
Memcached快么?
非常快。Memcached使用了libevent(如果可以的话,在linux下使用epoll)来均衡任何数量的打开链接,使用非阻塞的网络I/O,对内部对象实现引用计数(因此,针对多样的客户端,对象可以处在多样的状态), 使用自己的页块分配器和哈希表, 因此虚拟内存不会产生碎片并且虚拟内存分配的时间复杂度可以保证为O(1).。
Danga Interactive为提升Danga Interactive的速度研发了Memcached。目前,LiveJournal.com每天已经在向一百万用户提供多达两千万次的页面访问。而这些,是由一个由web服务器和数据库服务器组成的集群完成的。Memcached几乎完全放弃了任何数据都从数据库读取的方式,同时,它还缩短了用户查看页面的速度、更好的资源分配方式,以及Memcache失效时对数据库的访问速度。
Memcached的特点
Memcached的缓存是一种分布式的,可以让不同主机上的多个用户同时访问, 因此解决了共享内存只能单机应用的局限,更不会出现使用数据库做类似事情的时候,磁盘开销和阻塞的发生。
Memcached的使用
一 、 安装Memcached及Memcached配置和状态查询 (此处将其作为系统服务安装)
要想使用Memcached做缓存首先需要安装Memcached服务,安装方法如下:
1. 下载Memcached:
http://code.jellycan.com/memcached/ 现在的最新版本是1.2.6.注意下载正确的版本,windows 服务的话下载win32 binary。
2.解压之后放在硬盘的目录下,如:D:\memcached. 然后在运行中输入cmd进入命令行,进入到Memcached.exe 所在的目录,例如:D:\memcached,然后输入:Memcached –d install,即可完成安装。
Memcached还有其他的一些常用的命令如下:
-p 监听的端口
-l 连接的IP地址, 默认是本机
-d start 启动memcached服务
-d restart 重起memcached服务
-d stop|shutdown 关闭正在运行的memcached服务
-d install 安装memcached服务
-d uninstall 卸载memcached服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助
按照上面的安装步骤安装之后,使用memcached –m 200来调整最大内存占用之后会发现没有起作用,总是默认的64MB的内存,在网上搜了一下,原因是注册表中并没有写入信息,可以这样来修改。
1. memcached –d shutdown 首先关闭memcached服务。
2.进入注册表,找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\memcached Server, 在其中有一个ImagePath项,值为"d:\memcached\memcached.exe" -d runservice,在后面加上-l 127.0.0.1 -m 3000 -c 2048。
3.memcached –d start 启动memcached服务,这样就将memcached的最大内存修改为了3000MB。
对Memcached缓存服务的状态查询,可以先telnet连接上服务:telnet 127.0.0.1 11211 ,然后使用 stats命令查看缓存服务的状态,会返回如下的数据:
time: 1255537291 服务器当前的unix时间戳
total_items: 54 从服务器启动以后存储的items总数量
connection_structures: 19 服务器分配的连接构造数
version: 1.2.6 memcache版本
limit_maxbytes: 67108864 分配给memcache的内存大小(字节)
cmd_get: 1645 get命令(获取)总请求次数
evictions: 0 为获取空闲内存而删除的items数(分配给memcache的空间用满后需
要删除旧的items来得到空间分配给新的items)
total_connections: 19 从服务器启动以后曾经打开过的连接数
bytes: 248723 当前服务器存储items占用的字节数
threads: 1 当前线程数
get_misses: 82 总未命中次数
pointer_size: 32 当前操作系统的指针大小(32位系统一般是32bit)
bytes_read: 490982 总读取字节数(请求字节数)
uptime: 161 服务器已经运行的秒数
curr_connections: 18 当前打开着的连接数
pid: 2816 memcache服务器的进程ID
bytes_written: 16517259 总发送字节数(结果字节数)
get_hits: 1563 总命中次数
cmd_set: 54 set命令(保存)总请求次数
curr_items: 28 服务器当前存储的items数量
二、客户端使用
下载memcached java client:[url]https://github.com/gwhalin/Memcached-Java-Client [/url] 1 解压后将java_memcached-release_2.5.3.jar jar包添加到工程的classpath中
2 利用memcached java client 一个简单的应用
/**
* Copyright (c) 2008 Greg Whalin
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the BSD license
*
* This library is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*
* You should have received a copy of the BSD License along with this
* library.
*
* @author Greg Whalin <[email protected]>
*/
package com.danga.MemCached.test;
import java.util.Hashtable;
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class MemcachedTest {
// store results from threads
private static Hashtable<Integer,StringBuilder> threadInfo =
new Hashtable<Integer,StringBuilder>();
/**
* This runs through some simple tests of the MemcacheClient.
*
* Command line args:
* args[0] = number of threads to spawn生产的线程的数目
* args[1] = number of runs per thread 每个线程操作次数
* args[2] = size of object to store
*
* @param args the command line arguments
*/
public static void main(String[] args) {
//String[] serverlist = { "hengtiandesk144:11211", "hengtiandesk144:11212" };
String[] serverlist = { "localhost:11211" };
// initialize the pool for memcache servers
SockIOPool pool = SockIOPool.getInstance();
pool.setServers( serverlist );
pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(50);
//Set the sleep time between runs of the pool maintenance thread.
pool.setMaintSleep(30);
//Sets the Nagle alg flag for the pool.
pool.setNagle(false);
pool.initialize();
/*int threads = Integer.parseInt(args[0]);
int runs = Integer.parseInt(args[1]);
int size = 1024 * Integer.parseInt(args[2]); // how many kilobytes
*/
int threads = Integer.parseInt("20");
int runs = Integer.parseInt("30");
int size = 1024 * Integer.parseInt("10"); // how many kilobytes
// get object to store
int[] obj = new int[size];
for (int i = 0; i < size; i++) {
obj[i] = i;
}
String[] keys = new String[size];
for (int i = 0; i < size; i++) {
keys[i] = "test_key" + i;
}
for (int i = 0; i < threads; i++) { //模仿连接服务器数目
bench b = new bench(runs, i, obj, keys);
b.start();
}
int i = 0;
while (i < threads) {
if (threadInfo.containsKey(new Integer(i))) {
System.out.println(threadInfo.get( new Integer( i ) ) );
i++;
}
else {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool.shutDown();
System.exit(1);
}
/**
* Test code per thread.
*/
private static class bench extends Thread {
private int runs;
private int threadNum;
private int[] object;
private String[] keys;
private int size;
public bench(int runs, int threadNum, int[] object, String[] keys) {
this.runs = runs;
this.threadNum = threadNum;
this.object = object;
this.keys = keys;
this.size = object.length;
}
@SuppressWarnings("deprecation")
public void run() {
//单线程,多线程不安全
StringBuilder result = new StringBuilder();
// get client instance
MemCachedClient mc = new MemCachedClient();
mc.setCompressEnable(false);
mc.setCompressThreshold(0);
// time deletes
long start = System.currentTimeMillis();
for (int i = 0; i < runs; i++) {
mc.delete(keys[i]);
}
long elapse = System.currentTimeMillis() - start;
float avg = (float) elapse / runs;
result.append("\nthread " + threadNum + ": runs: " + runs + " deletes of obj " + (size/1024) + "KB -- avg time per req " + avg + " ms (total: " + elapse + " ms)");
// time stores
start = System.currentTimeMillis();
for (int i = 0; i < runs; i++) {
//System.out.println(keys[i]+object[i]);
mc.set(keys[i], object[i]);
}
elapse = System.currentTimeMillis() - start;
avg = (float) elapse / runs;
result.append("\nthread " + threadNum + ": runs: " + runs + " stores of obj " + (size/1024) + "KB -- avg time per req " + avg + " ms (total: " + elapse + " ms)");
// time gets
start = System.currentTimeMillis();
for (int i = 0; i < runs; i++) {
mc.get(keys[i]);
}
elapse = System.currentTimeMillis() - start;
avg = (float) elapse / runs;
result.append("\nthread " + threadNum + ": runs: " + runs + " gets of obj " + (size/1024) + "KB -- avg time per req " + avg + " ms (total: " + elapse + " ms)");
threadInfo.put(new Integer(threadNum), result);
}
}
}