使用Memcached java client时的对象系列化

最近实施的一个WEB项目中,需要处理较高的并发访问,如果使用数据库查询,恐怕难以满足要求,考虑到数据绝大数情况下只做select,不做update,还有少量insert,使用memcache应该是减少直接访问DB的有效方式。
memcached中只是简单的存储key/value,对于memcached来说,不认为value具有任何业务上的意义。将java中的对象存储到memcached中的时候,需要将对象作适当的变换,比如变换成字符串形式,或者二进制形式。当从memcached中取出来时需要做相应的逆变换。
默认情况下,memcached java client会使用java的默认系列化机制。一般说来效率都很低。在项目进行中,最先尝试了JSONReader和JSONWriter这两个工具类,高并发的测试条件下表现一般(应该是因为,在反系列化的时候,是一个字符一个字符处理的)。
考虑到项目中需要存放到memcahed中的对象数量较少,而且对象属性基本上是原始类型,最后选择了自己实现系列化和反系列化。
1)在已知对象属性类型和数量的情况下,直接将对象属性变化为String形式,然后属性之间使用分隔符(为了避免这样的分隔符在对象属性中本来就可能出现,可是使用比较复杂的分隔符,比如[$])。
2)为了存储List类型的对象或者对象数组,可以定义对象间的分隔符,然后每个对象使用1)中描述的方法系列化后,做字符串拼接。
3)反系列化的时候,将字符串分割为字符串数组,然后逐一赋值给对应的对象属性。

这里,比较费时间的应该是反系列化操作过程中的字符串分割,在网上看到一种相对高效的方法,暂且称之为双扫描法,实测发现,性能确实优于String.split。

public static String[] split(String s, String delimiter) {
if (s == null) {
return null;
}
int delimiterLength;
int stringLength = s.length();
if (delimiter == null || (delimiterLength = delimiter.length()) == 0) {
return new String[] { s };
}

// a two pass solution is used because a one pass solution would
// require the possible resizing and copying of memory structures
// In the worst case it would have to be resized n times with each
// resize having a O(n) copy leading to an O(n^2) algorithm.

int count;
int start;
int end;

// Scan s and count the tokens.
count = 0;
start = 0;
while ((end = s.indexOf(delimiter, start)) != -1) {
count++;
start = end + delimiterLength;
}
count++;

// allocate an array to return the tokens,
// we now know how big it should be
String[] result = new String[count];

// Scan s again, but this time pick out the tokens
count = 0;
start = 0;
while ((end = s.indexOf(delimiter, start)) != -1) {
result[count] = (s.substring(start, end));
count++;
start = end + delimiterLength;
}
end = stringLength;
result[count] = s.substring(start, end);

return (result);
}


使用以上方法实现后,对比测试,比JSONReader和JSONWriter方法系列化和反系列化约快5倍。

虽说,速度是提高了,测试发现,对象的系列化和反系列化仍然占用了大量的系统处理时间。曾有一种说法,memcached不适于java,一个重要的原因应该就是java系列化效率不高吧。
针对这个问题,后来使用了二级cache的方式,将一部分结果作为java对象存储在HashMap类似的结构中,主要目的也是为了减少直接访问memcached,减少系列化,尤其是反系列化操作。
虽说使用HashMap或者ConcurrentHashMap实现一个cache并不需要太多的工作量,但是如果要具备命中率查询、cache占用内存控制、自动失效等功能却也不是那么简单。网上找到一个双链表方式实现的高速缓存(org.jivesoftware.util下的Cache相关的几个工具类)的描述,据说性能不错,因此也就拿来试试了。其中有对对象Size的计算,使用中可能需要自己做部分调整。由于这些代码内部是用的是普通的HashMap,get和set方法都是用了synchronize保证线程安全。并发测试中,很快发现这些get方法成为性能瓶颈。于是借鉴ConcurrentHashMap类似的方式,对jivesofteware中的这个Cache实现进行简单封装,基本思路就是:在内部使用多个(比如32个)jivesoftware这样的Cache,不同key的对象分布在不同的内部cache中(比如使用简单的求模方式决定某个Key的对象应该在哪个cache中)。这样,理论上可以同时处理32个并发请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值