`
luciferdevil
  • 浏览: 3164 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

并发环境下JavaWeb的缓存过期策略

阅读更多

最近公司的几个平台经常在高峰期挂掉,经检查是因为数据库有太多Slow Query导致的,当初也没细想为什么会出现这么多的Slow Query,而且大部分还是相同的查询,单独拿某个Sql查询消耗时间大都在毫秒级别,为了安全起见,对所有Sql又做了一次优化,并且写了监测脚本,定期杀掉太慢的查询,但这样的话还是会影响到有些用户的访问。

     网站采用了Spring+SpringJDBC+Servlet+Memcache的架构,数据库是一对Master-Slave的Mysql,有大概几十个接口和4个网站共用这个数据库,采用了proxool数据库连接池。因为数据的实时性,所以CDN和Memcache的缓存过期时间都是在5分钟左右,好了环境介绍完毕,开始着手解决这个问题。

     为了模拟高峰期并发环境,使用Apache的ab命令对网站进行压力测试,此时测试环境是没有Cache的,果不其然,log里出现了数据库连接已经占满的异常信息,猜测是在大并发环境下,缓存正好过期,所有的访问都去请求数据库导致连接占满,经过考虑,有了以下解决方案,不足之处请说明。

 

备注:以下过程中出现的client为Memcache的实例,省略了初始化的过程,所用到的Memcache库为xmemcache1.3.3

 

1、  增加数据备份,防止缓存过期后同时请求数据库

2、  增加同步机制,保证并发环境下只有一个用户在更新数据

3、  增加数据更新回调接口,当缓存过期后,调用接口更新数据

4、  验证数据正确性,防止在更新pojo类时出现的ClassCastException

 

定义数据更新回调接口:

1 public interface MemcachedCallback {
2         Object update(Map<String, Object> args);
3         boolean validate(Object data);
4 }

写入缓存时,增加数据备份

 1 public static void setCallBack(String keyName, Object object) {
 2         if (client == null)
 3             return;
 4         try {
 5             client.set(keyName, MemcachedMgr.DEFAULT_TIMEOUT, object);
 9             client.set(keyName + "_OLD", MemcachedMgr.OLD_DATA_TIMEOUT, object);
10         } catch (Exception e) {
11             log.error("Cache set timeout for key" + keyName + " with value: "
12                     + object.toString() + " Error: " + e.getMessage());
13         }
14 }

操作线程池更新缓存

 1 public class MemcachedPolicy{
 2     private static ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 200, 500, 
 3             TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),
 4             new ThreadPoolExecutor.CallerRunsPolicy());
 5     public static synchronized void exec(String key, MemcachedCallback callback, Map<String, Object> args) {
 6         //判断当前是否有更新缓存操作
 7         if(MemcachedMgr.get(key + "_MPFlag") ==  null || "0".equals(MemcachedMgr.get(key + "_MPFlag"))) {
 8             MemcachedMgr.set(key + "_MPFlag", "1");
 9             pool.execute(new PolicyHandler(key, callback, args));
10         }
11     }
12 
13     public static synchronized void remove(String memKey) {
14         MemcachedMgr.set(memKey + "_MPFlag", "0");
15     }
16 }
17 
18 class PolicyHandler extends Thread{
19     private MemcachedCallback callback;
20     private Map<String, Object> args;
21     private String memKey;
22     public PolicyHandler(String key,MemcachedCallback callback, Map<String, Object> args) {
23         this.memKey = key;
24         this.callback = callback;
25         this.args = args;
26     }
27     
28     public void run() {
29         MemcachedMgr.setCallBack(memKey, callback.update(args));//更新数据
30         MemcachedPolicy.remove(memKey);
31     }
32 }


准备工作都已经做好,开始实现具体的策略,MemcacheMgr将只对外提供get方法:

 1 public static Object get(String keyName, Map<String, Object> args, MemcachedCallback callback) {
 2         if (client == null){ //如果memcache意外重启,则读取数据库(适用于极端情况)
 3             return callback.update(args);
 4         }
 5         try {
 6             Object data = client.get(keyName);
 7             boolean hasError = false;
 8             if(data != null && !callback.validate(data)) { //如果数据校验失败,则更新数据
 9                 data = null;
10                 hasError = true;
11             }
12             if(data == null) {
13                 if(!hasError) {
14                     data = client.get(keyName + "_OLD");//获取备份缓存
15                 }
16                 //将缓存更新任务加入线程池队列
17                 MemcachedPolicy.exec(keyName, callback, args);
18                 if(data == null) { //当memcache重启,备份数据为空的情况下
19                     int count = 10;//最多5秒超时
20                     while(count > 0) {
21                         if((data = client.get(keyName)) != null) {
22                             break;
23                         }
24                         count -- ;
25                         Thread.sleep(500);
26                     }
27                 }
28             }
29             return data;
30         } catch (Exception e) {
31             return null;
32         }
33     }

 

Memcache的工具类已经重构好了,接下来开始使用吧:

at IndexServlet

1、在Servlet里有一些从request获取到的参数,可以直接通过加final关键词的方式让update里直接使用,不过对于参数的校验还是应该跟数据操作隔离开的。

 1 Map<String, Object> args = new HashMap<String, Object>();
 2 args.put("page", page);
 3 args.put("sort", sort);
 4 
 5 Map<String, Object> data = (Map<String, Object>) MemcachedMgr.get("your key", args, new MemcachedCallback() {
 6             
 7             public Object update(Map<String, Object> args) {
 8                 Map<String, Object> map = new HashMap<String, Object>();
 9                 //所有的参数都可以从args拿到
10           //TODO 查询数据库,并将结果存入map
11                 return map;
12             }
13             
14             public boolean validate(Object data) {
15                 Map<String, Object> map = (Map<String, Object>) data;
16                 try {
17                     //TODO 通过强制类型转换来判断是否有转换错误,或者自定义校验
18                 } catch (Exception e) {
19                     return false;
20                 }
21                 return true;
22             }
23         });
24         //TODO request.setAttribute & 转发


算是告一段落,开始压力测试,模拟300个并发测试该接口,数据库只有一个process,而且QPS基本没什么变化。

据同事讲,缓存过期请求击穿数据库这种情况叫“Dogpile”,google了下dogpile,只发现hibernate里自带了DogpilePrevention,百度没有找到相关资料……

采用map存放页面所需所有数据感觉上还是不太好,暂时没想到更好的办法,先这么着吧,如果有好的解决方案,请大家不吝指教。

分享到:
评论

相关推荐

    javaweb之encache缓存所需所有jar包

    其中的`ehcache.xml`是EnCache的配置文件,定义了缓存区域、大小、过期策略等。 4. **编程接口**: EnCache提供了简单易用的编程接口,通过`Cache`对象来存取数据: ```java @Autowired private CacheManager ...

    基于javaweb的 微信网页分享 获取Token

    1. **Ehcache** 是一个广泛使用的开源缓存解决方案,支持内存和磁盘存储,提供了缓存过期、缓存更新和缓存淘汰策略。 2. **Guava Cache** 是Google Guava库的一部分,提供线程安全的本地缓存,可以设置容量限制、...

    JavaWeb监听器—案例(二)页面静态化

    这不仅可以提高图书管理系统的响应速度,还可以减轻服务器负载,尤其在高并发场景下效果显著。但同时要注意,页面静态化可能会导致数据实时性下降,对于需要实时更新的信息,应避免静态化处理。此外,合理的静态页面...

    方立勋JavaWeb ppt、代码

    - **缓存策略**:通过设置缓存头控制缓存行为。 - **缓存失效**:何时重新请求服务器更新资源。 **9.14 response、request 对象** - **HttpServletRequest**:封装了客户端请求信息。 - **HttpServletResponse**:...

    实战Nginx取代Apache的高性能Web服务器

    NCACHE是新浪开源的Nginx缓存模块,提供了更高级的缓存策略和管理功能,例如基于URL的智能缓存、缓存过期策略等。 **第11章 Nginx的非典型应用实例** 除了常见的Web服务,Nginx还可以用于负载均衡、SSL终止、...

    分布式锁与信号量avaWeb-mast开发笔记

    分布式系统在现代软件开发中扮演着重要角色,尤其是在高并发、大数据量的场景下,确保数据一致性、资源的公平访问成为关键问题。本篇将详细探讨分布式锁与信号量的概念、实现方式及其在JavaWeb开发中的应用。 ...

    java常见的Jar包

    它支持页面级、对象级和碎片级的缓存,可配置性强,具有内存管理、过期策略、缓存同步等多种特性。开发者可以通过`oscache.jar`轻松地在Java应用中集成缓存功能,提升系统的响应速度和并发处理能力。 这三款`jar`包...

    德鲁伊-连接池jar包.zip

    - **高性能**:德鲁伊连接池通过引入一系列优化策略,如Statement缓存、批处理、快速失败等,确保了在高并发场景下的优秀性能。 - **监控和扩展性**:内置Web监控系统,可以实时查看连接池状态,便于问题排查。...

    Java Web开发之基于Session的购物商店实现方法

    1. **Session过期策略**:设置合理的Session超时时间,防止用户长时间不活动导致购物车丢失。 2. **并发处理**:多用户同时操作时,需要考虑并发安全问题,例如使用同步锁保护对Session的修改。 3. **Session复制与...

    基于SSH框架的旅游网站

    6. **性能优化**:缓存策略、数据库索引优化、负载均衡等,保证网站在高并发情况下的稳定运行。 总之,基于SSH框架的旅游网站是一个涉及多方面技术的复杂系统,它利用这些框架和工具来提供高效、安全、易用的在线...

Global site tag (gtag.js) - Google Analytics