Memcached是一个高性能的服务器内存缓存软件。在早期版本的Memcached使用的是alloc来分配内存,存在内存碎片,在新版本的Memcached采用了Slab Allocator来分配内存。在MC启动时会要求制定一块内存区域,然后会划分为多个Slab,每个Slab默认大小为1M,可以指定。每个Slab又包含多个truck,每个Slab的truck大小不同,但同一个Slab的truck大小是一样的。在存入数据到MC时,MC会查找最合适的truck来存储数据。比如数据是100字节,现在有truck大小分别为64byte,128byte,144byte,那么会找到128byte的truck存储。不过仍然存在空间的浪费,比如这个例子中就浪费了28byte。所以保证合适大小的空间很重要,我们可以在MC启动时指定负载因子-f
参数来指定,默认是1.25.通常情况下这都是一个比较合理的值,无需修改。另外避免空间浪费的一个处理方法是,将数据大小差不多的放入到相同的MC中。比如一个MC的truck大小为1M,另外一个为1K。那么将数据量在1M左右的都放入到1M的那个MC。
与Redis相比:
1.MC只支持key/value,Redis支持key/value,list,set,map等6种数据结构;
2.都支持key的过期
MC不主动检查item是否过期,只有在get时检查,但是检查到过期也不会删除,只是做一个标记。在有数据要set时该空间可以被重试使用。MC在分配空间时优先使用已经过期的key/value对的空间,如果分配的空间占满时,会使用LRU算法,删除最近最少使用的key。可以使用-M
参数来启动MC,这样但MC内存耗尽时,会返回一个报错信息(对于完整数据缓存有用)。
Redis是主动监测,在key过期时会主动删除。
3.MC没有持久化功能,无法恢复数据;Redis可以定时保存数据到磁盘,Redis重启可以重新加载到内存。
1.将变化频率低的数据事先就放入MC中;
比如商品分类、系统角色等等。
后续如有修改,修改数据库并同步到MC。
这样所有用户的请求全部请求的MC,不会请求数据库。
2.热点数据缓存
3.集群会话共享
1.每台MC的内容不一样,所有的服务器内容加起来接近数据库的容量;
2.通过在客户端程序或在负载均衡器上使用Hash算法,让同一内容始终分配到同一台MC。
3.普通的HASH算法会导致MC节点宕机后数据发生流动,可能发生雪崩;
4.采用一致性HASH算法可以使节点宕机对数据的流动降到最低。
5.每个MC节点之间互不通信,数据独立存取。
由于Memcached使用了libevent库,所以要先安装libevent。
从https://github.com/libevent/libevent/releases获取最新稳定版本下载。这里下载的是libevent-2.1.8-stable.tar.gz。
tar zxvf libevent-2.1.8-stable.tar.gz
cd libevent-2.1.8-stable
./configure
make
make install
问题:
执行./configure
时出错。错误信息:libevent error: no acceptable C compiler found in $PATH
。
解决办法:安装gcc,执行yum install gcc
,然后再执行./configure
。
从http://memcached.org/downloads下载最新稳定版本,这里下载的是memcached-1.5.10.tar.gz。
tar -zxvf memcached-1.5.10.tar.gz
cd memcached-1.5.10
./configure
make
make install
验证是否安装成功
memcached -h
提示:
memcached: error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory
。
解决:
find / -name "libevent-2.1.so.6"
可以找到
/usr/local/lib/libevent-2.1.so.6
/data/tools/libevent-2.1.8-stable/.libs/libevent-2.1.so.6
将/usr/local/lib
加到/etc/ld.so.conf中
然后执行ldconfig
,然后再次执行memcached -h
就正常了。
memcached -m 64 -p 11211 -d -c 8192 -u root
相关参数可以使用memcached -h
查看。本例中-m 64指定内存大小为64M,-p 11211指定端口,-d指定为守护进程,-c指定连接数,-u指定用户(在用root启动时需要指定)。
增加实例,只需修改启动的端口即可。下面新增一个实例
memcached -m 64 -p 11212 -d -c 8192 -u root
add key flag expiretime bytes
value
key : 给这个值设置一个名字set key flag expiretime bytes
value
replace key flag expiretime bytes
value
append key flag expiretime bytes
value
prepend key flag expiretime bytes
value
get key
delete key
incr key increment_value
decr key increment_value
flush_all [time]
- time : (可选) 在指定时间后执行清理缓存操作
stats 显示统计信息例如 PID(进程号)、版本号、连接数等
stats slabs 显示各个slab的信息,包括chunk的大小、数目、使用情况等
stats sizes 显示所有item的大小和个数
stats items 显示各个 slab 中 item 的数目和存储时长
命令格式:
gets key
命令格式:
cas key flags exptime bytes unique_cas_token [noreply]
value
gets 获取带有 CAS 令牌的 value(数据值)
cas 执行一个”检查并设置”的操作
参考MemCache 入门极简教程
// 添加数据
add key001 0 10 5 // 回车
hello
// 获取数据
get key001
// 删除数据
delete key001
// 向value前追加数据
prepend key001 0 0 3
123
// 向value后追加数据
append key001 0 0 3
123
// 将key001的value替换为world
replace key001 0 0 5
world
写入:
printf "set key001 0 0 5\r\nhello\r\n" | nc 127.0.0.1 11211
读取:
printf "get key001\r\n" | nc 127.0.0.1 11211
删除:
printf "delete key001\r\n" | nc 127.0.0.1 11211
# cas操作需要先根据gets key来获取CAS令牌
# 1.获取令牌
printf "gets key001\r\n" | nc 127.0.0.1 11211
# 2.cas更新key的value
printf "cas key001 0 0 5 14 \r\nworld\r\n" | nc 127.0.0.1 11211
1.引入xmemcached的依赖
<dependency>
<groupId>com.googlecode.xmemcachedgroupId>
<artifactId>xmemcachedartifactId>
<version>2.4.5version>
dependency>
注意:xmemcached依赖slf4j,默认会打印一堆的Debug日志,可以引入slf4j和相关的日志实现的依赖,然后配置xmemcached的日志级别为info。
比如log4j,可以配置log4j.logger.com.google=info
。
2.基本使用
XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.200.109:11211 192.168.200.109:11212"));
builder.setSessionLocator(new KetamaMemcachedSessionLocator()); // 一致性哈希,使得某个key存的时候存都某个节点,取的时候也会从该节点获取
MemcachedClient mc = builder.build();
// 清空数据
mc.flushAll();
mc.add("hello",30,"你好,Memcached!");
String value = mc.get("hello");
System.out.println("hello=" + value);
mc.delete("hello");
value = mc.get("hello");
System.out.println("hello==" + value);
// 如果不存在key则创建,存在则覆盖value
mc.set("key001",0,"test key");
// 更新过期时间
mc.touch("key001",3);
Thread.sleep(3000L);
value = mc.get("key001");
System.out.println("key001=====" + value);
// cas操作,内部先通过gets命令拿到token,然后再执行cas操作。
mc.set("abc",0,0);
mc.cas("abc", 0, new CASOperation<Integer>() {
@Override
public int getMaxTries() { // cas重试次数
return 3;
}
@Override
public Integer getNewValue(long currentCAS, Integer currentValue) {
return 99; // 要设置的新值
}
});
int casValue = mc.get("abc");
System.out.println("abc=" + casValue);
mc.add("hello",0,"world");
if (mc.add("hello",10,"abcd")) {
System.out.println("key is exists");
}
// 数字原则更新,类似java中的AtomicInteger
System.out.println(mc.incr("aaa",5,0)); // 对aaa加5,默认值为0(aaa不存在时)
System.out.println(mc.decr("aaa",2)); // 对aaa减2
// 或者使用Counter
Counter counter = mc.getCounter("counter",0);
System.out.println(counter.addAndGet(5));
System.out.println(counter.decrementAndGet());
// 命名空间
String ns = "namespace";
mc.withNamespace(ns, new MemcachedClientCallable<Boolean>() {
@Override
public Boolean call(MemcachedClient memcachedClient) throws MemcachedException, InterruptedException, TimeoutException {
boolean result = memcachedClient.set("key1",0,"hello");
return result;
}
});
// 或者使用
mc.beginWithNamespace(ns);
mc.add("key2",20,"world");
mc.add("key3",30,"你好");
System.out.println("key3==>" + mc.get("key3"));
mc.endWithNamespace();
value = mc.withNamespace(ns, new MemcachedClientCallable<String>() {
@Override
public String call(MemcachedClient client) throws MemcachedException, InterruptedException, TimeoutException {
return client.get("key2");
}
});
System.out.println("with namespace key2=" + value);
value = mc.get("key1");
System.out.println("key1=" + value);
value = mc.get("key2");
System.out.println("key2=" + value);
value = mc.get("key3");
System.out.println("key3=" + value);
// 让命名空间的所有key都失效
mc.invalidateNamespace(ns);
value = mc.get("key1");
System.out.println("key1=" + value);
value = mc.get("key2");
System.out.println("key2=" + value);
value = mc.get("key3");
System.out.println("key3=" + value);
mc.shutdown();
这里只是基本使用,可以查看下面的参考链接查看全部用法。
参考:Xmemcached 中文用户指南、Memcached的几种Java客户端(待实践)
参考:Memcached 教程
这里推荐使用MemAdmin,需要PHP环境。参考:windows下memadmin安装