随笔 - 14  文章 - 0  评论 - 72  阅读 - 27884

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   blacktear  阅读(2153)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
< 2009年1月 >
28 29 30 31 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
1 2 3 4 5 6 7

点击右上角即可分享
微信分享提示