如何为 Web 应用程序实现缓存

9

使用Java和NoSQL数据库开发的Web应用程序有哪些不同的缓存方式?数据库也提供缓存,它们是唯一且始终是最好的缓存选项吗?

除了数据库之外,我还可以如何缓存应用程序中的用户数据。应用程序包含非常特定于用户的数据,例如社交网络中的数据。是否有一些简单的规则来确定应该缓存哪些类型的内容?

我是否也可以使用Java在应用程序服务器上缓存我的数据?


1
你尝试过使用memcached吗?http://memcached.org/ - Herberth Amaral
@user01 - 请分享您5年后的编程经验/解决方案/心得体会。这对其他人很有帮助。 - C.P.
2个回答

32
如果你想要一个经验法则,这是迈克尔·杰克逊(不是那个迈克尔·杰克逊)的建议:
  1. 程序优化的第一条规则:不要优化
  2. 程序优化的第二条规则(仅供专家使用!):暂时不要优化
古老的传统是,在进行剖析之前不要进行优化 - 也就是说,在你有确切证据表明需要进行优化之前不要进行优化。缓存是一种优化方式;它很可能对你的应用程序非常重要,但在你能够让你的应用程序承受负载并查看哪些对象需要大量时间来获取(从数据库或其他地方加载),你就不会知道需要缓存什么。无论你有多聪明,或者在这里得到了什么建议 - 直到你这样做,你都不会知道需要缓存什么。
至于可以缓存的内容,任何东西都可以,但我想你可以将其分类为三组:
  1. 来自数据库的新数据。这些很容易缓存,因为在你访问数据库时,你有需要用作缓存键的标识信息(主键、查询参数等)。通过缓存它们,你可以节省从数据库获取它们所需的时间 - 这涉及到IO,所以可能会很大。
  2. 由领域模型中的计算生成的东西(例如社交应用中的新闻提要)。这些可能更难缓存,因为产生它们需要更多的上下文信息; 你可能需要重构你的代码,创建一个单一点,将所需的信息都准备好,以便可以对其应用缓存。或者你可能已经发现了这一点。缓存这些将节省获取制作它们所需的所有信息的数据库访问,以及所有计算所需的时间;计算所需的时间可能是IO所需时间的显著增加,也可能不是。使此类缓存无效比纯数据库对象更难。
  3. 正在发送到浏览器的页面或页面片段。这些可能很容易缓存,因为在正确设计的应用程序中,它们是由URL或URL和用户的组合唯一标识的。缓存这些将节省你的应用程序中的所有计算;甚至可以避免服务请求,因为它可以由位于应用程序服务器前面的反向代理完成。有两个问题。首先,它使用大量内存:从几千字节的对象渲染的页面可能是几十或几百千字节的大小(我的Facebook主页是50 kB)。这意味着你必须保存大量计算才能使其比在数据库或领域模型层进行缓存更划算,而在一个设计合理的应用程序中,在领域模型和HTML之间的计算量并不是很大。其次,使其无效甚至比在领域模型中更难,并且很可能发生得太频繁 - 任何改变页面或片段的内容的事情都需要使缓存失效。

最后,真正的机制:从简单且在进程中的东西开始,比如一个有限大小和最近最少使用驱逐策略的映射。这很简单但有效。像 EHCache 这样的外部进程更复杂,但有两个优点:你可以在多个进程之间共享缓存(如果你有一个集群,在某些时候可能会很有用),并且你可以将数据存储在垃圾回收器看不到的位置,这可能会节省一些 CPU 时间(可能 - 这是一个太大的主题,本文无法深入探讨)。

但我重申我的第一点:在知道需要缓存什么之前不要进行缓存,一旦你知道了,要注意缓存的好处受限的限制,并尽量使你的缓存策略尽可能简单(但当然不能过于简单)。


您可以立即开始使用进程内EHCache。没有开发时间惩罚,而且您将更好地准备好进行扩展。 - smola

11

假设您正在构建一个相对典型的Web应用程序,其中:

  1. 有一个用于持久性的单个服务器
  2. 多个Web服务器
  3. 通过负载均衡器将经过身份验证的用户与单个服务器绑定,通过粘性会话保持连接

现在,回答您的一些问题。大多数持久性数据库或NoSQL数据库都可能具有某种缓存功能,使得如果您重复执行相同的简单查询(例如按主键检索),它能够缓存结果。但是,查询越复杂,持久性执行缓存的可能性就越小。此外,如果只有一个服务器用于持久性(即没有分片或写入主/读取从),则它很快成为瓶颈。因此,您想要进行的应用程序级缓存通常应该发生在Web服务器上,以减少对数据库的负载。

至于应该缓存什么,启发式方法是频繁访问和/或生成成本高昂的项(从数据库/ Web服务器处理/ 内存方面考虑)。典型的候选者是网站的主页和任何其他着陆页面-通常最好的方法是生成静态文件并提供服务。下一步取决于您的应用程序,但通常最有效的策略是尽可能靠近最终结果进行缓存-通常是提供的HTML。对于您的社交网络,这可能是一些特色更新列表之类的。

就用户会话而言,这些肯定是缓存的好选择。在这种情况下,您可能可以通过审慎使用Web服务器的会话范围(假设是JSP服务器)获得很多收益。这些数据保存在内存中,并且是保留用户在每个页面上认证后显示的特定信息的好地方(例如姓和名)。

现在需要考虑的最后一件事是处理缓存失效,这实际上是所有问题中最难的部分(命名事物是计算机科学中的另一个难题)。在这种情况下,像memcached或者ehcache这样的工具是正确的选择,正如其他人所提到的那样。ehcache可以轻松地与您的Java应用程序在同一进程中运行,并且可以很好地过期,采用最近最少使用和最不频繁使用的策略,并允许您同时使用内存和磁盘进行缓存。您需要思考的是,当数据发生更改时,您需要在缓存之前使缓存失效的情况。在这种情况下,您需要在应用程序架构中解决这些依赖关系,以适当地读取/写入缓存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接