MemCachedClient优化记

最近跟个朋友的聊天,谈到了JavaMemcached客户端的问题。他提到JavaMemcached客户端的性能并不乐观,而这激起了我的兴趣。

于是下载了一个MemCachedClient,开始研究。既然说它慢,但是光看源代码来进行“大脑编译运行”太累,而且还不容易找到病根,所以借助了一下JProfiler。在eclipse下写了个简单的UnitTest,不过是循环地向memcached添加和读取数据罢了。不运行不知道,一运行吓一跳。1000次循环写入居然用了3秒多,而且memcached服务器还在本机,没有网络延迟。(不过机器并不快)

索性挂着JProfiler跑了一遍,发现一个非常恶心的热点——Log4J的日志操作!反正源代码是要改的,所以干脆把所有日志的调用都注释掉了。再跑一遍测试,恩,这次快了不少。有人要说了,日志并不会浪费太多的时间啊。没错,有时候我们使用日志来反馈程序运行的状态是个好习惯,但当你看到MemCachedClient代码中所用到的日志,想必你也会大吃一惊,这里有太多的日志了!而且有一部分日志所调用的Socket.toString方法更是耗费CPU时间。虽然这种优化有一棍子打死之嫌疑,不过真正要在系统跑起来,你不会太在乎一个缓存系统的日志,何况这玩意的日志信息多的要命!

好了,日志的问题解决了,但在此用JProfiler分析之后,发现了3个比较重要的热点,1个是I/O流的读取,一个是String.format,一个是String.split

对于I/O流的读取,优化的方法是套一层BufferedInputStream,这一点很容易想到(为啥MemCachedClient的作者没有用BufferedInputStream套一下呢?)。至于String.format,看看源码发现直接用字符串相加,让编译器编译成StringBuffer.append比较合适。优化后跑了一遍测试发现这样效果确实会好一点。

对于String.split,可以看看JDK的源码,其实是调用了正则表达式的库,所以这个split会比较慢。不过从MemCachedClient的源码和memcached的协议来看,可以手工写一个split方法来替代这个比较慢的split方法:

 1public String[] splitByChar(String src, char schar, int count){
 2    String[] res = new String[count];
 3    char[] csrc = src.toCharArray();
 4    int nowc = 0;
 5    int nows = 0;
 6    boolean isend = false;
 7    int strlen = src.length();
 8    for(int i = 0; i < strlen; i++){
 9        isend = i == strlen - 1;
10        if(csrc[i] == schar || isend){
11            if(isend)
12                res[nows++= src.substring(nowc);
13            else
14                res[nows++= src.substring(nowc, i);
15            nowc = i + 1;
16            if(nows >= count) break;
17        }

18    }

19    return res;
20}

21

在另外一个地方需要提到的是在做JProfiler时,发现ByteArrayOutputStream.toString也是比较耗时的方法。在此我有个不太有把握的优化:

ByteArrayOutputStream替换成CharArrayWriter。虽然在向ByteArrayOutputStream中写入数据时是写入byte,到了CharArrayWriter时是char,但是可以用0x00FF & b的方法来把byte转换成char。也就是前面加一个字节的0。不过这可能会遇到一个问题——当我们存入memcachedkey是非英文字母时,这个优化就得不到正确的结果。不过另外一想,用中文当key的情况会有很多吗?当然如果真的会出现这种情况,替换成原来的方案也不是很方便的。

为了正确性,这个优化可以暂时放弃。不过在某种极限的情况下,似乎还是可行的。

 

通过这次经历,发现了几点对性能考量比较多的时候应该注意的地方:

1 日志太多会影响性能

2 format方法虽然好用,但性能不如StringBuilder

3 split方法有时候自己写会比库中的要快一点

posted on 2009-01-06 11:16  blacktear  阅读(2152)  评论(0编辑  收藏  举报