一、安装、启动
#安装
# wget http://memcached.googlecode.com/files/memcached-1.4.15.tar.gz
# tar zxvf memcached-1.4.15.tar.gz
#./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent && make && make install
#后台启动:
./usr/local/memcached/bin/memcached -d -m 64 -u root -l 127.0.0.1 -p 11621 -c 1024 -P /usr/local/memcached/memcached.pid
-d 以守护程序(daemon)方式运行
-m 1024 数据内存数量,不包含memcached本身占用,单位为 MB
-u root 指定用户,仅root可以使用此参数。
-l 127.0.0.1 监听的 IP 地址,本机可以不设置此参数
-p 11211 TCP端口,默认为11211,可以不设置
-c 1024 最大并发连接数,默认1024
-P 保存pid到指定文件
启动或重启memcached脚本
放到/usr/local/memcached/目录下
#!/bin/bash #killall memcached pid=`cat memcached.pid` kill -9 $pid sleep 12 ./bin/memcached -d -m 128 -p 11211 -u root -P memcached.pid -v 1>>memcached.out 2>>memcached.err
二、相关知识
主要目标:解决直接访问数据库压力大问题。
适用场景:不经常发生变化的对象信息,如:商品信息不适合,商品图片(图片名称、地址)、企业信息适合。
读取:先读缓存,没有再读库并直接设置缓存。
保存/修改/删除完,不能直接设置缓存:如果直接刷新缓存数据的话,后续操作发生异常,就会导致实际数据与缓存数据不一致,为了解决这个问题,只有保证数据更新成功,再循环刷缓存。因此,先将要刷新的缓存对象放置到临时容器中,等到操作成功后统一flush。
memcached是通过socket与服务端建立连接传输数据的,tcp协议。
取模的算法:有增/删server导致的cache全部不能命中的问题。
一致性hash算法:cache不能命中的问题仍然存在,但是只存在于2个节点之间的位置。
相对于取模的算法,一致性hash算法除了计算key的hash值外,还会计算每个server对应的hash值,然后将这些hash值映射到一个有限的值域上(比如0~2^32)。通过寻找hash值大于hash(key)的最小server作为存储该key数据的目标server。如果找不到,则直接把具有最小hash值的server作为目标server。
通过主机名判断所属地区: String hostName = InetAddress.getLocalHost().getHostName();
内存
64位能分配2GB以上的内存。32位每个进程最多只能使用2GB内存。
memcached启动时指定的内存分配量是memcached用于保存数据的量,没有包括“slab allocator”本身占用的内存、以及为了保存数据而设置的管理空间。因此,memcached进程的实际内存分配量要比指定的容量要大
一台服务器上启动多个问题:TCP连接数就会成倍增加,管理上也变得复杂
服务器的内存为4GB,却仅分配了3GB,是因为内存分配量超过这个值,就有可能导致内存交换(swap)。
简单的使用malloc和free,这样将产生大量的内存碎片,从而加重操作系统内存管理器的负担。memcached的内存管理机制采用了slab allocator内存分配和管理机制,以解决内存碎片问题。slab allocator基本原理是按照预先定义的大小,将内存分割为多种特定长度的trunk块,并将长度相同的trunk块归成slab组,每次请求内存时,采用最佳适应算法查询并获得一个trunk,用于保存item。
参考:memcached源码学习-内存管理机制slab allocator http://blog.csdn.net/tankles/article/details/7027645
查内存
$top
shift+m
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20888 xx 15 0 47040 32m 444 S 0.0 0.4 11:13.92 mem1
三、运行状态监控:
//windows cmd/linux 终端 连接memcached服务器
telnet ip port
//查询状态
stats
STAT pid 5085
STAT uptime 239832
STAT time 1385378823
STAT version 1.2.2
STAT pointer_size 64
STAT rusage_user 62.917435
STAT rusage_system 152.025888
STAT curr_items 45485
STAT total_items 3047438
STAT bytes 17357992
STAT curr_connections 2
STAT total_connections 86
STAT connection_structures 12
STAT cmd_get 7
STAT cmd_set 3047438
STAT get_hits 6
STAT get_misses 1
STAT evictions 0
STAT bytes_read 1052001370
STAT bytes_written 24410844
STAT limit_maxbytes 134217728
STAT threads 1
END
//退出
quit
遗失对主机的连接。
$ ./memcached-tool
Usage: memcached-tool <host[:port]> [mode]
memcached-tool 10.0.0.5:11211 display # shows slabs
memcached-tool 10.0.0.5:11211 # same. (default is display)
memcached-tool 10.0.0.5:11211 stats # shows general stats
memcached-tool 10.0.0.5:11211 dump # dumps keys and values
$ ./memcached-tool 192.168.1.238:11621 stats
#192.168.1.238:11621 Field Value
bytes 17357992
bytes_read 1052001603
bytes_written 24415536
cmd_get 7
cmd_set 3047438
connection_structures 12
curr_connections 2
curr_items 45485
evictions 0
get_hits 6
get_misses 1
limit_maxbytes 134217728
pid 5085
pointer_size 64
rusage_system 152.029887
rusage_user 62.919434
threads 1
time 1385380482
total_connections 91
total_items 3047438
uptime 241491
version 1.2.2
[search@sortserver0 script]$ ./memcached-tool 192.168.1.238:11621
# Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM
7 384B 3022s 17 45467 no 0 0 0
10 752B 2458s 1 6 no 0 0 0
11 944B 2457s 1 2 no 0 0 0
12 1.2K 2458s 1 1 no 0 0 0
14 1.8K 2457s 1 1 no 0 0 0
15 2.3K 2458s 1 1 no 0 0 0
17 3.5K 2457s 1 1 no 0 0 0
19 5.5K 3017s 1 3 no 0 0 0
20 6.9K 2457s 1 1 no 0 0 0
21 8.7K 2457s 1 1 no 0 0 0
32 101.1K 232725s 1 1 no 0 0 0
version: memcached的版本号。
bytes:表示系统存储缓存对象所使用的存储空间,单位为字节。
curr_connections:表示当前系统打开的连接数。
total_connections:表示从memcached服务启动到当前时间,系统打开过的连接的总数。
limit_maxbytes:memcached服务缓存允许使用的最大字节数。总的可用储存空间大小 .与我们启动memcached服务设置的大小一致 。
四、客户端
java 客户端 Memcached Client目前有3种: Memcached Client for Java SpyMemcached XMemcached
danga Memcached-Java-Client 下载地址:https://github.com/gwhalin/Memcached-Java-Client/downloads
danga 源码下载:https://github.com/gwhalin/Memcached-Java-Client/releases
import java.io.Serializable;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
/**
*
* @Description: 封装MemCached,同时向多台(跨机房)写一样的数据。
*
* @author thrillerzw
* @version 1.0
* @create time 2013-11-22 下午12:02:21
*/
public class MemcachedManager {
private static Logger log = Logger.getLogger(MemcachedManager.class.getName());
//存储所有服务器Memcached的实例
private List<MemCachedClient> pools = new ArrayList<MemCachedClient>();
private static Map<String, MemcachedManager> memManagerMap = new HashMap<String, MemcachedManager>();
private MemcachedManager(String configFileName) {
String servers[] = readMemcachedFile(configFileName);
for (int i = 0; i < servers.length; i++) {
MemCachedClient mc = new MemCachedClient("memcached" + i);
String[] serverlist = { servers[i] };
//没有参数为"default"
SockIOPool pool = SockIOPool.getInstance("memcached" + i);
pool.setServers(serverlist);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(1000);
//private long maintSleep = 1000 * 30; // maintenance thread sleep time
pool.setMaintSleep(30);
//enable/disable Nagle's algorithm
pool.setNagle(false);
//默认1000 * 30 读超时
pool.setSocketTO(1000 * 60);
// 默认1000 * 3 连接超时
pool.setSocketConnectTO(1000 * 5);
// 初始化连接池
pool.initialize();
// 2.5.1这2个方法过期,2.6.6已经没有这2个方法了。
//压缩设置,超过指定大小(单位为K)的数据都会被压缩
/*mc.setCompressEnable(true);
mc.setCompressThreshold(64 * 1024);*/
pools.add(mc);
}
}
public static MemcachedManager getInstance(String configFileName) {
MemcachedManager instance = memManagerMap.get(configFileName);
if (instance == null) {
instance = new MemcachedManager(configFileName);
memManagerMap.put(configFileName, instance);
}
return instance;
}
public String[] readMemcachedFile(String configFileName) {
String servers[] = null;
try {
// 取得XML路
SAXReader reader = new SAXReader();
// 获得XML文档对象
//Document document = reader.read("memcachedconfig.xml");
Document document = reader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream(configFileName));
Element element = document.getRootElement();
List list = element.elements();
servers = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
Element child = (Element) list.get(i);
String hostName = child.elementText("IP");
String port = child.elementText("PORT");
//通过hostName获取hosts配置的ip。如果xml中配置的是ip,也没有问题。 servers[i]=ip+":"+port;
servers[i] = InetAddress.getByName(hostName).getHostAddress() + ":" + port;
log.info("INIT cach eserver:" + servers[i]);
}
} catch (Exception e) {
throw new RuntimeException("init Memcache server array error:", e);
}
if (servers == null || servers.length == 0) {
throw new RuntimeException("init Memcache server array error: empty");
}
return servers;
}
/**
* 随机从一台服务器上查找对应key的值
* 若有,则返回
* 若没有,则遍历所有服务器
*/
public Object get(String key) {
int num = (Math.abs(new Random().nextInt())) % pools.size();
Object object = null;
object = ((MemCachedClient) pools.get(num)).get(key);
if (object != null) {
return object;
}
for (int i = 0; i < pools.size(); i++) {
if (i == num)
continue;
MemCachedClient mc = (MemCachedClient) pools.get(i);
object = mc.get(key);
if (object != null) {
return object;
}
}
return object;
}
/**
* 将数据存储到Memcached中(带有效时间)
* @param key 键值
* @param value 对象
* @param time 过期时间,若要设置过期时间,需time值大于1000,如果小于1000
* 则过期时间均为0,即为永不过期
*/
public void setMemcached(String key, Object value, long time) {
for (int i = 0; i < pools.size(); i++) {
MemCachedClient mc = (MemCachedClient) pools.get(i);
log.debug(key + ":" + mc.set(key, value, new Date(time)));
}
}
public void delete(String key) {
for (int i = 0; i < pools.size(); i++) {
MemCachedClient mc = (MemCachedClient) pools.get(i);
mc.delete(key);
}
}
public boolean flushAll() {
boolean success = true;
for (int i = 0; i < pools.size(); i++) {
MemCachedClient mc = (MemCachedClient) pools.get(i);
success = mc.flushAll();
}
return success;
}
/**
* 是否存在于每个mem中
* @param key
* @return
*/
public boolean keyExistsInAll(String key) {
boolean exist = false;
for (int i = 0; i < pools.size(); i++) {
MemCachedClient mc = (MemCachedClient) pools.get(i);
exist = mc.keyExists(key);
if (!exist)
return false;
}
return exist;
}
/**
* 是否存在于任意一个mem中
* @param key
* @return
*/
public boolean keyExistsInAny(String key) {
boolean exist = false;
for (int i = 0; i < pools.size(); i++) {
MemCachedClient mc = (MemCachedClient) pools.get(i);
exist = mc.keyExists(key);
if (exist)
return true;
}
return exist;
}
/**
*
* @Description: 必须实现Serializable,否则取出来为null
*
* @author thrillerzw
* @version 1.0
* @create time 2014-4-22 下午4:25:11
*/
static class MyObject implements Serializable {
/**
* @Fields serialVersionUID : TODO
*/
private static final long serialVersionUID = -2582821459094658151L;
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
MemcachedManager memcachedManager = MemcachedManager.getInstance("sellerInfoMemcachedConfig.xml");
MyObject o1 = new MyObject();
o1.setId(1);
o1.setName("thrillerzw中文");
/*
* long time单位毫秒
* 0 或者 999:Thread.sleep(1000*10);后取出有值
* 1000 :Thread.sleep(1000); 后取出为null
*/
memcachedManager.setMemcached("key1", o1, 0);
MyObject myObject = (MyObject) memcachedManager.get("key1");
//0 :myObject=com.dhgate.searchranking.service.MemcachedManager$MyObject@19c0bd6
System.out.println("myObject=" + myObject);
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject = (MyObject) memcachedManager.get("key1");
System.out.println("myObject=" + myObject);
//----------list----------
List<MyObject> list = new ArrayList<MyObject>();
list.add(o1);
memcachedManager.setMemcached("keyList", list, 1000 * 10);
List<MyObject> list2 = (List<MyObject>) memcachedManager.get("keyList");
System.out.println("list2=" + list2);
}
}