两台服务器之间同步缓存数据的最佳方法

15
想要在两台服务器之间同步缓存数据。两个数据库共享同一个数据库,但为了更好的执行效果,在启动时我将数据缓存到哈希映射中。 因此想要在不重新启动服务器的情况下同步缓存数据。(两个服务器同时启动)。
请建议最佳和最有效的方法。
3个回答

44
不是试图在两个服务器实例之间同步缓存数据,而是使用像 memcached/couchbase 或 redis 这样的东西进行集中式缓存,为什么不集中缓存呢?在我看来,使用分布式缓存(例如 ehcache)比使用像上述那样的缓存服务器集中缓存要复杂得多且容易出错。
附加说明一下,当决定使用哪种缓存方法(内存中,集中式),需要考虑缓存的数据的易变性。
如果数据存储在数据库中,但在服务器加载后不会更改,则您甚至不需要在服务器之间同步。只需让它们从源加载此静态数据到内存中,然后继续执行它们的操作即可。数据不会更改,因此无需引入复杂的模式来保持服务器之间的数据同步。
如果数据确实存在一定程度的易变性(例如,如果您正在缓存从数据库中查找的实体数据以节省对数据库的访问次数),那么我仍认为集中式缓存比内存中分布式和同步缓存更好。您只需要确保在缓存数据上使用适当的过期时间,以允许定期刷新数据。此外,您可能需要在特定实体的更新路径上删除集中式存储中的缓存数据,然后只需让其在下一次请求该数据时从缓存中重新加载。在我看来,这比尝试进行真正的写入缓存更好,其中您需要同时写入底层存储和缓存。数据库本身可能会对数据进行调整(例如通过默认未提供的值),在这种情况下,您的缓存数据可能与数据库中的数据不匹配。
编辑:有评论中提出了一个问题,关于集中式缓存的优势(我猜测是针对内存中分布式缓存之类的东西)。 我将提供我的意见,但首先是一个标准免责声明。 集中式缓存并非万应药。 它旨在解决与在 JVM 内存缓存相关的特定问题。 在评估是否切换到它之前,您应该首先了解自己的问题,并查看它们是否与集中式缓存的优点相符。 集中式缓存是一种架构变更,它可能存在自己的问题/注意事项。 不要仅仅因为有人说它比您正在做的更好而轻易切换到它。 确保原因符合问题。
现在,让我们就我认为集中式缓存可以解决哪些问题与在 JVM 内存(以及可能是分布式)缓存相比较的问题提出我的意见。 我将列出两件事情,尽管我确信还有更多。 我的两个重点是:整体内存占用和数据同步问题。让我们从整体内存占用开始。假设您正在使用标准实体缓存来保护您的关系型数据库免受过度压力。假设您有大量数据需要缓存才能真正保护您的数据库,例如在多GB范围内。如果您正在进行基于JVM内存的缓存,并且您有10个应用服务器箱,则需要为每个需要在JVM中进行缓存的箱子获取额外的内存($$$)乘以10。此外,您还必须为JVM分配更大的堆以容纳缓存的数据。我认为JVM堆应该小而简洁,以减轻垃圾回收的负担。如果您有大块无法回收的旧代(Old Gen)空间,那么当它进入完整GC并尝试从膨胀的旧代空间中收回一些东西时,您将会对垃圾收集器造成压力。 您想避免长时间的GC2暂停时间,而让您的Old Gen膨胀不会有所帮助。此外,如果您的内存需求超过某个阈值,并且您的应用程序层正在运行32位机器,则必须升级到64位机器,这可能是另一个限制性成本。
现在,如果您决定集中缓存数据(使用类似Redis或Memcached的东西),则可以显着减少缓存数据的整体内存占用,因为您可以将其放在应用层的所有应用服务器框之外,在一对计算机上处理缓存的需要。您可能希望使用集群方法(这两种技术都支持),并且至少有两个服务器,以提供高可用性并避免缓存层中的单个故障点(稍后会详细介绍)。只需使用几台机器来支持所需的内存要求,您就可以节省相当多的$$。此外,现在您可以分别调整应用程序框和缓存框,因为它们正在服务于不同的目的。应用框可以针对高吞吐量和低堆进行调整,而缓存框可以针对大型内存进行调整。具有较小的堆肯定会有助于整体吞吐量。
现在是针对集中式缓存的一个快速提示。您应该设置应用程序,以使其在缓存在一段时间内完全关闭的情况下也能生存。在传统实体缓存中,这意味着当缓存完全不可用时,您只需直接访问您的数据库以响应每个请求。不太理想,但也不是世界末日。好的,现在说一下关于数据同步问题。对于分布式in-jvm-memory缓存,你需要保持缓存同步。一个节点中的缓存数据发生变化时需要复制到其他节点,并同步到它们的缓存数据中。这种方法有点吓人,因为如果由于某种原因(例如网络故障)一个节点与同步失去联系,那么当请求发送到该节点时,用户看到的数据将不准确,与数据库中当前的内容不符。更糟糕的是,如果他们进行另一个请求并且那个请求到达了不同的节点,他们将看到不同的数据,这会让用户感到困惑。通过集中管理数据,可以消除这个问题。现在,有人可能会认为中央化的缓存需要围绕相同的缓存数据键更新并发控制。如果两个并发更新针对同一键进行,如何确保这两个更新不会互相覆盖?我的想法是先不要担心这个问题;当更新发生时,从缓存中删除该项(并直接写入数据库),在下次读取时重新加载。这样更安全、更容易。如果不想这样做,那么可以使用CAS(Check-And-Set)功能来实现乐观并发控制,以确保在更新时同时更新缓存和数据库。
所以总结一下,如果你集中管理应用程序的缓存数据,可以节省成本并更好地调整应用程序层机器。同时,你也可以获得更准确的数据,因为你将少处理数据同步问题。希望这有所帮助。

谢谢您的回复。 您能否简单解释一下集中式缓存的优点,因为它可能会增加我的服务器负担。目前有大约5000个同时用户。 注意:根据当前统计数据,某些特定时间我也会遇到内存溢出的问题。 - user2310939
更新以响应一些优点... - cmbaxter
谢谢Cmbaxter...这真的很有帮助... - user2310939
@cmbaxter 很好的解释,你对双层缓存有什么看法,比如本地缓存和与redis等集中式缓存同步? - rdhaundiyal

8

首先,尽量不要过早优化。你真的需要缓存吗?99%的情况下你并不需要它。在这种情况下,你的解决方案是删除冗余代码。

如果你确实需要它,尝试停止重复发明轮子。有完美的现成库可供使用。例如ehCache,它具有分布式模式。


谢谢回复。我有大约5000个同时用户,所以我想我不能删除缓存。我会了解一下ehCache... - user2310939
2
"99%你不需要它。" 真的吗?我认为恰恰相反,许多开发人员不使用它,因此继续向他们的后端服务器发送不必要的负载。 - Mandeep Janjua
我尝试在Spring Boot应用程序中实现缓存。我的问题是,无论我添加了什么新记录,它们都没有更新到缓存中。如何将数据库与缓存同步?如果我编写一个比较数据的函数,那么这就是额外的工作。 - Shubham

3

使用HazelCast。它使用多播协议在服务器之间进行数据同步。它易于使用,并支持锁定和其他功能。


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