CaseStudy-数据缓存出错

这个星期负责的项目出现了一次线上的故障,发现的很偶然,要不后边很可能会时不时地让人纠结一段时间,同时还不容易定位和解决。在这里把这次事故记录下来,引以为戒吧。

1、问题描述

在项目的服务器端中用了一个ConcurrentHashMap<Integer,ArrayList<Object>>的并发哈希表来缓存来自于其它系统的业务数据,策略是每三个小时同步一次,为了防止在同步过程中过大地影响客户端的请求,每一次同步时不能直接清空整个哈希表,而是根据Key值一项一项地进行重新写入。

ArrayList<Object> value = map.get(key);
if (value == null) {
    value = new ArrayList<Object>();
}
values.add(newElement);
map.put(key, value);

 第一次同步并不会出错,但接下来的同步就会产生问题,key对应的ArrayList中会被写入大量的重复元素,导致整个map越来越大,以至于到了后来严重地影响相关接口的性能,因为在程序后边的处理逻辑中有对重复元素的过滤,所以问题并没有被很快地暴露出来,直到有次发现线上api访问大量地发生严重超时,但相关的数据库访问并没有发生超时问题,当时觉得问题比较地诡异,但也没有定位到问题,更谈不上找到解决的方法,所以对服务器进行了重启,所有的访问又都正常了。但在后边进行的一项测试时,在没有重启的情况下,短时间内多次进行了同步,无意中发现了整个map中的元素越来越多,这才发现了这个缓存逻辑的问题,接下来就进行了解决。

2、解决方法

定位到了问题,解决的方法就很简单了,主要有两种解决的方案,一种是在每次add时检测ArrayList中是否已经存在了同样的元素,还有就是新建一个新的ArrayList,直接通过put操作覆盖原来的数据,相对而言,第二种方法的效率更高,所以后边采用了第二种解决方法。

ArrayList<Object> value = new ArrayList<Object>();
for (Object element : elements) {
       value.add(element);
}
dealMap.put(key,value);

 到此,程序上的问题就修复了,部署上线了一切正常

3、反思

客观地来说,这种问题仅仅通过单元测试是不太可能被发现的,同时由于其它程序逻辑的影响,短时间内也不会体现在业务上,导致最后影响的面挺大,同时还很难定位和解决。个人觉得这样的问题首先要有意识地在程序设计的时候加以解决,在设计这种定时同步并缓存的功能时,需要把握整个程序流的走向,考虑各种情况的处理,分析前后多次同步时对缓存表的影响和相互之间关联,同时在测试时也得注意测试到这些情况。

 

posted @ 2012-12-09 13:39  hitlyq  阅读(228)  评论(0编辑  收藏  举报