如果您的应用程序严重依赖数据并频繁与数据库交互,则缓存对于降低任何相关成本至关重要。 使用缓存来加速数据检索的在线应用程序必须实现强大的数据复制和一致性。
数据复制对于跨多个服务器创建关键数据的备份至关重要,从而确保信息准确且可访问。 同样,在无缝同步这些分布式副本以防止应用程序过程中出现任何差异时,这种一致性机制至关重要。 因此,为了保证您的应用程序与准确且最新的数据交互,您必须利用这些功能来实现备份、高可用性和数据一致性。
幸运的是, NCache 为此类分布式系统提供了一套可靠的复制和同步机制。 这篇博客详细介绍了如何 NCache 满足这些条件。
NCache 拓扑结构
支持的缓存拓扑 NCache 包括 镜像拓扑, 复制拓扑, 分区和分区副本拓扑及 客户端缓存拓扑.
- 镜像拓扑: 对于镜像缓存拓扑中的所有读写操作,客户端节点仅连接到集群的活动服务器节点。 如果主动服务器节点出现故障,客户端应用程序会立即与之前的被动节点建立连接。
- 复制拓扑: 复制拓扑可确保如果多个服务器同时发生故障,数据不会丢失,因为每个服务器都具有相同的数据副本。
- 分区拓扑: 通过将所有客户端应用程序连接到缓存服务器,分区拓扑通过将数据划分为块来提供高数据可用性 - 因此,将所有客户端应用程序连接到缓存服务器。 这样,即使连接的服务器出现故障,应用程序也可以通过请求其他服务器来获取所需的数据。
- 分区副本拓扑: 分区副本拓扑不仅创建动态分区,还在其他服务器节点上创建这些分区的动态副本,在连接故障或节点故障时充当备份。 在这些情况下, NCache 从副本节点获取数据并分发。
- 客户端缓存拓扑:在客户端缓存拓扑中,缓存距离您的应用程序非常近,可让您快速缓存分布式缓存中的数据。
在博客中了解有关拓扑的更多信息: 横向扩展和保持向上:探索拓扑 NCache.
数据复制在 NCache
数据复制的首要目标是保证数据的一致性、可用性和容错性。 通过在不同位置维护相同的数据副本,系统可以提高性能、减少延迟并防止潜在的数据丢失或服务器故障。
它可以是同步的也可以是异步的,具体取决于所使用的拓扑。 在同步复制中, NCache 确保数据同时写入主缓存服务器及其副本。 然而,这种方法保证了强数据一致性,但性能可能会受到影响,因为任何缓存写操作都必须等待所有副本的确认。
或者,异步复制在将数据写入主缓存服务器后更新副本。 由于这些缓存写入操作不等待副本确认,因此该策略可提供更大的性能提升。 虽然这种策略可能会导致副本和主缓存服务器之间出现暂时的数据不一致。
同步和异步数据复制的拓扑
所讨论的每种拓扑都有自己的一组特性,但并非所有拓扑都适合数据复制。 例如,同步复制,用于 镜像缓存拓扑,通过同时写入主节点和副本节点来保证数据一致性。 另一方面,通过先写入主节点,然后更新副本,异步复制,用于 复制缓存拓扑,提高了效率,但偶尔可能会导致错误。
幸运的是 分区副本拓扑 通过结合分区缓存和复制缓存拓扑的优点,在冗余、可用性和性能之间取得平衡。 用户可以根据自己的要求选择最佳的技术。
数据一致性机制
数据一致性是指整个系统或数据库中信息的可靠性和准确性,确保数据在各种操作中保持准确和可靠。 为了保证数据的一致性, NCache 提供了一种机制 分布式锁定 允许您在并发更新期间锁定特定的缓存项。 多个用户可以同时处理各种缓存项,而不会通过应用锁来影响数据完整性。 因此,缓存锁对于管理分布式环境中的共享资源非常有用。
为了保持数据的一致性, NCache 充当分布式锁管理器,并为您提供两种类型的锁定: 乐观锁定(缓存项版本) 和 悲观锁定(排他锁定)。 乐观锁的用途 缓存项版本控制。 在这种类型的锁定中,Add 方法在缓存中添加新项目并首次保存项目版本,而 insert 方法会覆盖现有项目的值并更新其项目版本。 下面的示例创建一个LockHandle,然后用键Product:1001锁定一个项目,时间间隔为10秒,这样该项目将在10秒后自动解锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Prerequisite: Cache is already connected // Item is already added in the cache // Specify the key of the item string key = $"Product:1001"; // Create a new LockHandle LockHandle lockHandle = null; // Specify time span of 10 seconds for which the item remains locked TimeSpan lockSpan = TimeSpan.FromSeconds(10); // Lock the item for a time span of 10 seconds bool lockAcquired = cache.Lock(key, lockSpan, out lockHandle); // Verify if the item is locked successfully if (lockAcquired == true) { // Item has been successfully locked } else { // Key does not exist // Item is already locked with a different LockHandle } |
或者,悲观锁定使用以下方法锁定项目: 锁柄,阻止所有其他用户对该缓存项执行任何写入操作。 在获取项目时成功获取锁后,应用程序现在可以安全地执行操作,因为知道只要您拥有此锁,其他应用程序就无法获取或更新此项目。 以下示例锁定缓存中的一个项目,然后使用 lockHandle 获取该项目。 然后更新该项目,然后使用插入 API 将其重新插入到缓存中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Specify the key of the item string key = $"Product:1001"; // Set acquireLock flag as true bool acquireLock = true; // Specify time span of 10 seconds for which the item remains locked TimeSpan lockSpan = new TimeSpan(0, 0, 10); // Initialize the lockHandle LockHandle lockHandle = null; CacheItem item = cache.GetCacheItem(key, acquireLock, lockSpan, ref lockHandle); var product = new Product(); product = item.GetValue(); // Update the unitsinstock for the product product.UnitsInStock = 200; bool releaseLock = true; // Item is already locked with a LockHandle // Update the item and release the lock as well since releaseLock is set true // Make sure that the LockHandle matches with the already added LockHandle cache.Insert(key, item, null, lockHandle, releaseLock); |
数据复制和一致性的最佳实践
现在我们已经了解了提供的数据复制和一致性机制 NCache,让我们探讨一下在此背景下推荐的做法。 对于需要高可用性并且能够承受 (n-1) 个节点故障而不会丢失任何数据的应用程序,复制拓扑是最佳选择。 而分区副本拓扑提供了高可用性和满足不断增长的数据需求的能力。
虽然它的可用性不如复制拓扑,但它仍然可以承受节点故障而不会丢失任何数据。 每个分区都有一个备份; 因此,每个节点本质上都有一个分区和另一个分区的备份。 另一方面,镜像拓扑通过从主动节点到被动节点的异步复制来提供数据可靠性和可用性。
NCache 还支持基于时间的数据失效策略,使缓存数据过期。 数据失效通过 過期 意味着您可以指定数据在缓存中保留的持续时间,超过该时间就会过期。 这样,您的缓存将在下一个客户端请求时拥有最新数据,从而消除陈旧数据 - 确保数据一致性。 无效通过 依赖 确保每当数据库发生更改时都会删除缓存数据。
结论
数据复制和一致性是可靠和高性能应用程序的支柱。 幸运的是,与 NCache,您可以使用强大的功能来创建高度可扩展、容错的应用程序,甚至可以处理最复杂的数据需求。