Memcached简介
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比较
与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节点之间互不通信,数据独立存取。
安装Memcache
安装libevent
由于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
。
安装Memcached
从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
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
命令行客户端操作MC数据
MC命令
- 添加数据(add)
add key flag expiretime bytes value
key : 给这个值设置一个名字
flag : 标志,是一个整数
expiretime : 有效期,以秒为单位,0表示没有延迟
bytes : 这是一个需要存储在memcached的数据的长度
value : 是一个需要存储的数据。数据需要将通过在新的一行后输入
注意:bytes要和value的长度一致,否则会出现CLIENT_ERROR bad data chunk错误 - 为一个新的或现有的键(key)设置一个值(set)
set key flag expiretime bytes value
- 替换已存在的 key(键) 的 value(数据值)(replace)
replace key flag expiretime bytes value
- 向已存在 key(键) 的 value(数据值) 后面追加数据(append)
append key flag expiretime bytes value
- 向已存在 key(键) 的 value(数据值) 前面追加数据(prepend)
prepend key flag expiretime bytes value
- 获取存储在 key(键) 中的 value(数据值)(get)
get key
- 删除已存在的 key(键)(delete)
delete key
- incr 与 decr 命令用于对已存在的 key(键) 的数字值进行自增或自减操作
incr key increment_value decr key increment_value
- 清理缓存中的所有数据(flush_all)
flush_all [time] - time : (可选) 在指定时间后执行清理缓存操作
- stats / stats slabs / stats sizes / stats items
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
gets 获取带有 CAS 令牌的 value(数据值) cas 执行一个”检查并设置”的操作
通过Telnet来测试
// 添加数据
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
通过nc测试
写入:
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
Memcached客户端Xmemcached使用
1.引入xmemcached的依赖
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</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 教程
Memcached监控
这里推荐使用MemAdmin,需要PHP环境。参考:windows下memadmin安装