Memcached 作用与使用 基本介绍
主要从事网页设计、PC网站建设(电脑版网站建设)、wap网站建设(手机版网站建设)、响应式网站开发、程序开发、微网站、微信小程序定制开发等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了丰富的做网站、成都网站设计、网络营销经验,集策划、开发、设计、营销、管理等多方位专业化运作于一体,具备承接不同规模与类型的建设项目的能力。
1,对于缓存的存取方式,简言之,就是以键值对的形式将数据保存在内存中。在日常业务中涉及的操作无非就是增删改查。加入缓存机制后,查询的时候,对数据进行缓存,增删改的时候,清除缓存即可。这其中对于缓存的闭合就非常重要,如果缓存没有及时得到更新,那用户就会获取到过期数据,就会产生问题。
2,对于单一业务的缓存管理(数据库中只操作单表),只需生成一个key,查询时,使用key,置入缓存;增删改时,使用key,清除缓存。将key与表绑定,操作相对简单。
3,但是在现实业务中,更多的是对关联表的增删改查(数据库多表操作),业务之间互相关联,数据库中的某张表不止一个业务再进行操作,将缓存拦截在service层,对业务进行缓存,对多表进行缓存。
4,业务层缓存实现策略:
4.1,在缓存中建立一个key为"union_query",value为“hashmap(‘简称uqmap’)“的缓存,prefix保存的是当前业务操作涉及到的数据库表名的组合(数据库表名的唯一性),使用‘|’分隔(例 prefix=“A|B”,此次业务将操作A表与B表),uqversion是业务版本号,从0开始递增。
4.2,调用一个查询业务时,对数据进行缓存,设置operation为1,告诉cache对象,这是一个缓存操作,例如调用 queryAB(args[])方法时,cache对象切入,将prefix(即”A|B“)与uqversion(初始化为0),存入uqmap中进行缓存。
4.3,将prefix,uqversion,方法明+参数,进行拼接,使用md5进行加密后作为一个key,将方法的结果集作为value,进行缓存。至此缓存成功。
4.4,当第二个请求来调用queryAB时,cache对象切入,首先,查询uqmap对象,使用prefix找到对应的uqversion,然后,通过拼接加密获取key,最后取得结果集进行返回。
4.5,当有一个updateA方法被调用时,设置operation为4,告诉cache对象,这是一个删除缓存的操作,此时prefix的值为“A”,cache对象切入,获取全局的uqmap,遍历其中的prefix,是否包含了表A的名称:如果包含,则更新此prefix的uqversion进行自增,uqversion一旦发生变化,4.3中组合的key将不复存在,业务缓存也就消失了。(对于复杂的updateAB方法,遍历prefix要复杂一点,可以实现)
4.6,当第三个请求来调用queryAB时,可以获取到uqversion,组合成key后,但是没有对应的value。此时确定缓存不存在时,继续正常执行方法,获取结果集,返回给客户的同时,将结果集进行缓存。
5,对于缓存的操作,网上有三种api可以选择(memcached client forjava、spymemcached、xmemcached),具体的好坏,本人在这就不做分析。本人使用的是XMemcached api。
具体实现细节:
1,新建 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。定义缓存策略。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用于查找的时候,放置缓存信息
* @author shufeng
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface XmemCache{
/**
* 值为当前操作的表名,表名唯一
* 涉及到多表操作,使用|分隔
*/
String prefix() default "";
/*
* 缓存有效期 设置,单位为秒
* 指定间隔时间,默认值为3600秒(1小时)
* */
int interval() default 3600;
/**
* 1 从cache里取值,如果未置入cache,则置入
* 2 replace cache value 未扩展
* 3 replace cache value,并返回旧值 未扩展
* 4 remove cache key 从cache里删除对应的缓存
* 5 remove cache key 从cache里删除对应的缓存,并返回未删除之前的值 未扩展
**/
int operation() default 1;
}
2,memcache基础操作类,对一些常用方法进行封装,对memcachedclient进行配置
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import com.node.hlhw.rbac.api.constant.Constant;
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.impl.KetamaMemcachedSessionLocator;
import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
import net.rubyeye.xmemcached.utils.AddrUtil;
/**
* @author Melody shufeng
* 对memcachedclient进行封装,添加一下常用方法
*/
public class MemcachedOperate implements DisposableBean {
/*
* timeout - Operation timeout,if the method is not returned in this
* time,throw TimeoutException timeout - operation timeout,in milliseconds
* exp - An expiration time, in seconds. Can be up to 30 days. After 30
* days, is treated as a unix timestamp of an exact date. value - stored
* data
*/
private static final Logger logger = LoggerFactory.getLogger(MemcachedOperate.class);
private static Properties PROPERTIES = new Properties();
private static String MEMCACHED_SETTING = "memcached.properties";
private static MemcachedClient memcachedClient;
public static MemcachedClient getClient(){
return memcachedClient;
}
/**
* 静态代码块,类加载时,初始化缓存客户端
* 确保只创建一个client实例
* author shufeng
*/
static {
InputStream in = Object.class.getResourceAsStream("/" + MEMCACHED_SETTING);
try {
PROPERTIES.load(in);
} catch (IOException e) {
e.printStackTrace();
}
String servers = PROPERTIES.getProperty("memcached.servers", "");
if (null != servers && !"".equals(servers)) {
try {
logger.debug("启动memcached连接");
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(servers));
builder.setConnectionPoolSize(100);
builder.setFailureMode(true);
builder.setCommandFactory(new BinaryCommandFactory());
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
builder.setTranscoder(new SerializingTranscoder());
memcachedClient = builder.build();
memcachedClient.setEnableHeartBeat(false); // 关闭心跳
memcachedClient.flushAll(); // 清空缓存
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @param key
* @return 获取value
*/
public static Object get(String key) {
Object object = null;
try {
object = memcachedClient.get(key);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
return object;
}
public static void setWithNoReply(String key, int exp, Object value) {
try {
memcachedClient.setWithNoReply(key, exp, value);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
}
/**
* 查询联表的业务版本号 如果为空,则初始化
*
* @param prefix
* @return
*/
@SuppressWarnings("unchecked")
public static Long getUnionQueryVersion(String prefix) {
try {
Map uqmap = null;
GetsResponse