本文转自 bxst 51CTO博客,原文链接:http://blog.51cto.com/13013670/1944035
Memcached 作用与使用 基本介绍
1,对于缓存的存取方式,简言之,就是以键值对的形式将数据保存在内存中。在日常业务中涉及的操作无非就是增删改查。加入缓存机制后,查询的时候,对数据进行缓存,增删改的时候,清除缓存即可。这其中对于缓存的闭合就非常重要,如果缓存没有及时得到更新,那用户就会获取到过期数据,就会产生问题。
2,对于单一业务的缓存管理(数据库中只操作单表),只需生成一个key,查询时,使用key,置入缓存;增删改时,使用key,清除缓存。将key与表绑定,操作相对简单。
3,但是在现实业务中,更多的是对关联表的增删改查(数据库多表操作),业务之间互相关联,数据库中的某张表不止一个业务再进行操作,将缓存拦截在service层,对业务进行缓存,对多表进行缓存。
4,业务层缓存实现策略:
4.1,在缓存中建立一个key为"union_query",value为“hashmap<prefix,uqversion>(‘简称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,一个注解是一个类。定义缓存策略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
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进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
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();
|