Java 使用Spring/EHCache加载时刷新缓存

Java 使用Spring/EHCache加载时刷新缓存,java,multithreading,spring,caching,ehcache,Java,Multithreading,Spring,Caching,Ehcache,我在使用数据库后端和基于EHCache的缓存的Spring多线程web服务上遇到了缓存问题。该服务有许多客户端,它们一次又一次地请求同一个对象,每秒有几十个请求。只有两个对象经常被请求,而大量其他对象很少被请求。对象可以每隔几分钟更改一次,因此缓存的TTL设置为一分钟。从数据库加载对象很慢,至少需要几秒钟 首先,我使用了一个简单的实现来获取对象: 检查对象是否在缓存中 如果是,则从缓存中返回 如果没有,则从数据库加载它,将其放入缓存并返回 当最初在本地进行测试时,它运行良好。但是在速度更快的服务

我在使用数据库后端和基于EHCache的缓存的Spring多线程web服务上遇到了缓存问题。该服务有许多客户端,它们一次又一次地请求同一个对象,每秒有几十个请求。只有两个对象经常被请求,而大量其他对象很少被请求。对象可以每隔几分钟更改一次,因此缓存的TTL设置为一分钟。从数据库加载对象很慢,至少需要几秒钟

首先,我使用了一个简单的实现来获取对象:

  • 检查对象是否在缓存中
  • 如果是,则从缓存中返回
  • 如果没有,则从数据库加载它,将其放入缓存并返回
  • 当最初在本地进行测试时,它运行良好。但是在速度更快的服务器上进行的性能测试表明,每当一个更频繁请求的对象在缓存中过期时,就会出现一些非常糟糕的负载峰值。发生这种情况时,在接下来的10秒钟内,对该对象的所有请求都将导致数据库加载,直到第一个线程完成数据库加载并将新对象放入缓存。结果是数据库的负载很短但很高,许多用户需要等待请求完成

    我当前的实现通过跟踪当前是否正在加载哪个对象来提高数据库负载:

  • 检查对象是否已缓存
  • 如果是,则从缓存中返回
  • 如果没有,请检查对象当前是否正在加载
  • 如果是,等待其他线程的加载完成,从缓存中获取新对象并返回它
  • 如果否,将对象放入加载对象列表,完成后将其放入缓存并返回
  • 使用此实现,即使对象过期,也只有一个数据库操作。而且,由于数据库负载较低,它也会更快完成。但这仍然意味着在对象加载期间请求对象的所有用户都需要等待

    我真正想要的是,只有第一个线程等待数据库加载,而所有其他线程在加载对象时只返回“expired”对象。对我来说,响应时间比对象太老几秒更重要

    或者,当我注意到一个对象将在几秒钟内过期时,我可以异步刷新缓存。这更接近于EHCache的单一TTL模型,这意味着没有人需要等待数据库加载


    我真正的问题是:在我重新发明轮子之前,是否有任何现有的框架已经实现了这样的东西(在Spring/EHCache环境中)?或者可能Spring/EHCache中已经存在对此的支持,我就是找不到正确的选项?

    您的设计有缺陷,因为第二个线程无法从缓存中获取任何“过期”对象,因为没有(按照步骤2:当对象在缓存中时立即返回)

    解决办法:

  • 10秒加载单个对象太长了。检查SQL并尝试对其进行优化

  • 缓存对象的时间更长,并运行更新线程,以查询数据库中对象的新状态。这意味着线程1只是触发一些后台工作,最终刷新缓存中的对象。缺点:缓存必须足够大,以便始终将大多数对象保留在内存中。否则,“首次加载对象”将过于可见

  • 在不加载对象的情况下显示网页,并在后台使用AJAX请求加载对象。当对象可用时更新网页。这可能是响应性和准确性之间的一个很好的平衡,这取决于您的站点在并非所有内容都立即准备好的情况下有多有用

  • 改进对象的加载。创建“视图”表,其中包含在每行中显示单个对象所需的所有数据。对“真实”(规范化)对象进行更改时更新这些行。“视图缓存”仅从此表中填充。这使得加载对象的速度非常快,而代价是对数据模型的更改。有关极端解决方案,请参见“”

  • 尝试将数据模型反规范化一点,以减少加载单个对象所需的连接数。或者,缓存一些您通常会加入的对象,并在web服务器上进行过滤/聚合

  • 更新对象时,触发缓存刷新。很可能有人很快就会想看到这个物体。这种方法在人们手动编辑对象时效果最好,在外部系统(新闻行情、股票报价等)随机触发更改时效果最差

  • 如果只需要大量连接来显示所有详细信息,请尝试加载概述,然后使用第二个缓存来缓存详细信息,然后将其加载到第二个线程中。与AJAX一起,您可以快速显示对象的概览,这将为您赢得一些等待细节的好感


  • Ehcache提供的两种构造可以帮助您:

  • 定时刷新
  • 两者都需要您更改与缓存交互的方式,因为它们需要配置
    CacheLoader

    不幸的是,我找不到显示第二个选项示例的在线文档。 它允许使用Quartz刷新缓存条目以进行调度。它还可以根据密钥生成器仅刷新密钥的子集。
    查看package
    net.sf.ehcache.constructs.scheduledrefresh

    中的类,谢谢您的回答。你指的是哪种设计?3号?我知道您无法从EHCache获取过期对象。我的解决方案是在其上放置另一层,该层将保留过期的对象,直到缓存被更新,例如使用第二个缓存