随着高事务分布式应用程序的发展,分布式缓存解决方案已成为实现性能可扩展性的理想选择。 NCache 作为内存分布式数据存储是一个不错的选择,因为它提供 线性可扩展性 和 高可用性.
到目前为止,一切都很好,但如何确保这种共享环境中的数据完整性是一件大事。由于两个或多个客户端可以在应用程序中同时访问和更改相同的数据,因此结果可能是不一致的数据。一旦发生数据完整性破坏,缓存中的数据实际上变得毫无用处。
在这篇博文中,我将解释这个问题是如何发生的以及如何 NCache 用它来拯救你 分布式锁定 功能。
NCache 更多信息 NCache 锁定 Ncache 配套文档
没有锁定的数据完整性问题
为了详细了解数据完整性问题,我们考虑了一个在线电子商务商店的示例,其中卖家上传他们的产品,而客户查看并下订单购买这些产品。
假设客户想要查看某个产品,而该产品的卖家想要更新其价格。 现在,如果客户以更高的价格查看产品,而卖家添加了用户还看不到的折扣,则用户最终会以更高的价格购买产品。
场景如图 1 所示。
- 客户端 1 读取缓存中产品的详细信息。
- 客户端 2 还读取产品数据。 它是正确的。
- 客户端 1 现在更新缓存中的产品详细信息。
- 客户端 2 还会更新缓存中的产品详细信息。
- 客户端 1 所做的更新丢失。
我们来看看如何 NCache 解决了这个问题。
NCache 分布式锁定
NCache 通过允许您锁定数据,提供了一种根据您的业务需求保护数据的灵活方式。 一个用户控制一大块数据并对其进行更新。 同时,没有其他用户可以操作该数据。
根据您的应用场景,您可以选择其中之一 NCache 锁定机制:
- 悲观锁 (事务或独占锁):独占锁定项目,使其无法被其他用户访问。
- 乐观锁定 (通过项目版本锁定):使用项目版本控制,使其他用户可以访问项目。
敏感数据的悲观锁定
您应该使用 悲观锁定策略 当应用程序中需要更新的数据是敏感数据时。您可以使用 LockHandle 来获取数据的显式锁定。完成后,锁就会被释放。
现在,我们看到了 NCache 悲观锁定解决了我们示例中的数据一致性问题。 您可以同时获取产品的排他锁进行更新,其他用户无法访问该产品。 这如图 2 所示。
- 客户端 1 获取产品的锁定并开始添加和更新产品详细信息。
- 客户端 2 被拒绝读取产品详细信息,并且必须等待直到锁定被释放。
- 一旦锁被释放,客户端 2 就可以对产品进行常规操作。
需要注意的是, NCache 支持两组 API:带锁定和不带锁定。 如果用户想使用悲观锁定,那么在需要强数据一致性的应用程序中,任何地方都应该使用带有锁定参数的 API。 让我们看看你如何做到这一点 NCache 锁定功能。 在以下代码段中,首先创建一个新的 LockHandle,然后设置时间跨度以指定获取锁的时间段。 此 LockHandle 用作识别锁的锁 ID。 然后使用带有键和 LockHandle 的 Get 方法获取锁。
现在您可以执行业务操作并手动释放锁定或等待时间跨度结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Pre-condition: Cache is already connected // Create a new lock handle to fetch an Item using locking LockHandle lockHandle = new LockHandle(); // Timespan for which lock is to be taken TimeSpan timeSpan = TimeSpan.FromSeconds(5); // Get item from the cache and lock it var result = cache.Get(key, true, timeSpan, ref lockHandle); // Verify if the item is locked successfully if (result != null) { // Item has been successfully locked } else { // Key does not exist // Item is already locked with a different LockHandle } //Unlock item in cache manually cache.Unlock(key, lockHandle); |
此处,带锁参数的Get API 立即返回,无需等待获取锁。 您需要进行显式的 reties 以获得对项目的锁定,以防万一您一开始就失败了。
您还可以使用将 LockHandle 与键相关联的 Lock 方法获取锁。 NCache 提供多种方式 获取/释放显式锁 从而实现灵活的锁定。 如果您打算实施 NCache 锁定,使用 NCache 项目锁定的示例应用程序 GitHub上 会有所帮助。
数据可用性的乐观锁定
悲观锁定很棒,但当响应时间对您的应用程序至关重要时,它可能不是最佳方法。 这就是乐观锁定派上用场的地方。
NCache 乐观锁使用 缓存项版本控制 克服显式锁情况下造成的线程饥饿。 因此,您可以处理缓存项目的版本,该版本随着对该项目的每次更新而递增。 NCache 跟踪项目版本,您无需担心数据一致性。
图 3 显示了它是如何完成的:
- 客户端 1 添加有关产品的详细信息,并将 CacheItemVersion 设置为 v1。
- 客户端 2 使用 CacheItemVersion v1 读取更新的产品详细信息。
- 由于 CacheItemVersion 增加,客户端 1 再次更改了详细信息,并变为 v2。
- 现在,当客户端 2 尝试使用旧的 CacheItemVersion v1 更新详细信息时,操作失败并显示 CacheItemVersion
- 客户端 2 获取最新的 CacheItemVersion
- 客户端 2 使用 CacheItemVersion 更新产品详细信息。现在操作已成功执行,并且 CacheItemVersion 自动加 1。
这可确保所有用户始终更新 CacheItemVersion,并且不会拒绝用户访问商店中的任何产品。
以下代码显示了如何执行此操作:
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 27 28 |
// Specify the key of the cacheItem string key = "Product:1001"; // Initialize the cacheItemVersion CacheItemVersion version = null; // Get the cacheItem previously added in the cache with the version CacheItem cacheItem = cache.GetCacheItem(key, ref version); // If result is not null if (cacheItem != null) { // CacheItem is retrieved successfully with the version var prod = cacheItem.GetValue(); prod.Discount = 0.5; // Create a new cacheItem with updated value var updateItem = new CacheItem(prod); //Set the itemversion. This version is used to compare the // item version of the cached item updateItem.Version = version; //Insert call will fail with LockingException, if cache contains a newer version of the cache item. //In case of LockingException, we can fetch the latest cache item from cache and update it cache.Insert(key, updateItem); // If it matches, the insert is successful, otherwise it fails } |
为何使用 NCache 锁定
安全和一致的数据对于当今的企业来说至关重要,如果像多用户交易这样简单的事情会损害您的数据完整性,那将是一种耻辱。
NCache 以最大的灵活性确保您的数据在高度分布式环境中的完整性和一致性。 根据您的应用场景,您可以采用不同的锁定机制 NCache. 最终,您将拥有没有任何数据不一致的并发!
您拥有如此惊人的信息,可以提供给其他人。 感谢您让我们知道这件事。 请继续分享此类信息,以确保我们财产的安全。