发表于: 2017-08-09 22:08:15
2 1103
一.今天完成的事情
1.学习memcached相关的知识
自己的理解就是,memcached可以先将常用的数据放在内存中,当客户端请求数据时先从缓存中寻找,找不到了以后再去访问数据库,从数据库中找到之后会将数据放进缓存,以后查询该数据时,可以从内存中找,因为内存的读取速度远快于数据库,所以这样能够提高查询效率.
memcached不仅可以作为简单的缓存,还可以做分布式,但是这要求我们在代码中体现出来.memcached将分配给它的内存分为一个一个slab_class,每个slab_class又可以分为若干个slab,每个slab又可以分为若干个page,每个page中又分为trunk,trunk就是memcached最小的存取单位,在存放数据时,memcached会根据数据的大小分配创建和该数据相近的trunk,而且会创建相应的page,以及slab,所以在刚刚安装好memcached时,通过stats slab是没有结果的,必须要放入一个数据才可以,这样memcached实际上是会对内存产生一定的浪费,memcached的分布式是简单的通过增加节点实现的,可以在程序中配置,但是没有主从关系,所以一旦服务器重启,缓存中的数据就会丢失.
2.使用memcached缓存数据
首先,安装memcached客户端,注意要先安装libevent
其次,启动memcached服务,加参数,几个常用的参数有-m 占用内存大小,默认为64M,-p 为端口号, -l 为服务器ip,-u 为以什么用户执行,必须是root才有作用,-d start是启动
如:/usr/local/bin/memcached -d start -p 9006 -m 128 -u root
意思是启动服务,ip默认本机,端口号为9006,占用内存128M,以root身份运行
接着可以用telnet ip 端口号链接至memcache服务
之后用stats可以查看memcacahed基本状态信息,用stats slab查看slab信息,其中比较重要的参数有
用这两个参数计算缓存命中率.
然后就是选择一个java memcached客户端(目前有三种,java官方传统客户端,spymemcached,XMemcached)
编写demo测试
package com.putaoteng.task5.utils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import net.rubyeye.xmemcached.GetsResponse;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.command.BinaryCommandFactory;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.utils.AddrUtil;
public class Cache {
private MemcachedClientBuilder builder = null;
private MemcachedClient client = null;
// 构造方法,参数为memcached的ip地址以及权重值
public Cache(String address, int[] weight) {
builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(address), weight);
// 设置连接池大小,即客户端个数
builder.setConnectionPoolSize(50);
//宕机报警
builder.setFailureMode(true);
//使用二进制文件
builder.setCommandFactory(new BinaryCommandFactory());
try {
this.client = builder.build();
} catch (IOException e) {
Log.loggerCreate(LogLevel.ERROR, "创建Memcached客户端失败:" + e.getMessage());
e.printStackTrace();
}
}
// 添加方法
public void set(String key, int exp, Object value) {
try {
if (!client.set(key, exp, value)) {
System.err.println("set error, key is" + key + "value is" + value);
}
} catch (TimeoutException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation timeout-----(set)" + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation interrupted-----(set)" + e.getMessage());
e.printStackTrace();
} catch (MemcachedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation failed-----(set)" + e.getMessage());
e.printStackTrace();
}
}
// 删除一个key
public void delete(String key) {
try {
client.delete(key);
} catch (TimeoutException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation timeout-----(delete)" + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation interrupted-----(delete)" + e.getMessage());
e.printStackTrace();
} catch (MemcachedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation failed-----(delete)" + e.getMessage());
e.printStackTrace();
}
}
//原子更新,将比较和更新绑定为一个原子的操作
public boolean update(String key, Object value) {
try {
GetsResponse<Integer> result = client.gets(key);
long cas = result.getCas();
if (!client.cas(key, 0, value, cas)){
return false;
}
} catch (TimeoutException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation timeout-----(update)" + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation interrupted-----(update)" + e.getMessage());
e.printStackTrace();
} catch (MemcachedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation failed-----(update)" + e.getMessage());
e.printStackTrace();
}
return true;
}
//根据key,获取值
public Object get(String key){
try {
return client.get(key);
} catch (TimeoutException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation timeout-----(get)" + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation interrupted-----(get)" + e.getMessage());
e.printStackTrace();
} catch (MemcachedException e) {
Log.loggerCreate(LogLevel.ERROR, "MemcachedClient operation failed-----(get)" + e.getMessage());
e.printStackTrace();
}
return null;
}
//额外添加服务
public void addServer(String server, int port, int weight){
try {
client.addServer(server, port, weight);
} catch (IOException e) {
Log.loggerCreate(LogLevel.ERROR, "添加Memcacahed失败:" + e.getMessage());
e.printStackTrace();
}
}
//移除一个服务
public void removeServer(String hostList){
client.removeServer(hostList);
}
}
该类就是主要的执行增加,更新,删除数据的操作,其中更新是将比较和更新绑定在一起原子操作,同时捕捉各种异常,记录日志服务.
编写测试类,测试该类的结果
测试代码,设置在缓存中保存10分钟:
结果:
最后,就是在task5中使用,今天没有用spring整合memcachend,使用简单的定义类的方式,以home页面为例
添加日志是为了查看结果,逻辑很简单,当有请求时先查询缓存中的key为list的值,该值若为空,则从数据库中获取数据,然后将该数据放入缓存,设置有效时间为30天,以后读取时从缓存读取.
3.测试增加缓存之后的结果
在这点上遇到了一个问题,即我从日志中看,缓存确实起到了作用,除第一次是从数据库中获取数据,其余均是从缓存获取数据,从日志可以看到,执行一个controller,包括逻辑和读取数据,大部分时间仅用几毫秒,少数数据用20毫秒左右
缓存命中率也还行
在缓存中查找数据也可以找到
但是压力测试的结果就很一般,我用的还是单线程,无限循环测试,结果如下
基本都是平均95毫秒左右,
不用缓存的测试结果,基本相同
今天没时间了,明天解决它
二.明天的计划
1.解决今天的 问题
2.将memcached和spring整合在一起,有的部分可以用spring AOP实现,减少冗余代码
3.如果有时间,尽量完成这一步骤
三.问题
以上
四,收获
memcached相关知识,具体见以上
五.进度
预计用一周的时间完成任务六,今天一过就剩下六天了,应该可以完成.
评论