项目的排他锁(悲观锁)
NCache 提供悲观锁机制,独占锁定缓存数据。此机制使用锁句柄锁定该项目,因此,所有其他用户都被阻止对该缓存项目执行任何写入操作。 A LockHandle
与缓存中的每个锁定项目相关联,由锁定 API 返回。
备注
此功能也可用于 NCache Professional.
仅当在 API 级别提供锁定句柄时,才能获取/更新或解锁锁定的项目。 但是,您应该小心执行此操作,以避免数据完整性问题。 如果要实现的目标是数据一致性,悲观锁定是一个非常好的方法。
一旦使用获取锁 LockHandle
,有两种释放它的机制。 下面解释这两种机制。
备注
建议使用基于时间的Locking机制,在满足条件后解锁物品,使资源保持获取的时间最短。
何时使用悲观锁定
以前一章讨论的例子为例。 如果两个不同的用户在同一实例访问同一银行账户进行更新操作,可能会发生冲突,导致数据不一致。
在这种情况下,悲观锁定将使一个用户能够同时访问该帐户。 成功操作后,用户解锁该项目并释放控制权,这意味着第二个用户现在可以访问该帐户并进行相应的修改。
使用这种方法,数据保持一致并且不会发生冲突。
A LockHandle
与项目相关联,以确保特定项目在整个缓存中保持不可访问。
NCache 提供专门调用锁定的方法,以及操作锁定机制的大量重载。
先决条件
显式锁定项目
您可以在执行任何操作之前显式锁定某个项目。 该方法需要一个 TimeSpan
将项目锁定指定时间。 但是,如果您不希望获取的锁过期,请指定 TimeSpan.Zero
。 指定否 TimeSpan
将无限期锁定项目。
Lock
本例中使用的方法将一个 LockHandle
用钥匙。 请确保单 LockHandle
与单个键相关联。 重新使用手柄之前先释放锁; 否则,可能会导致行为不一致。
如果某个项目已被锁定,则将返回 false 值,但您将获得更新的值 LockHandle
.
警告
锁定一个项目的最小值 TimeSpan
以避免死锁或饥饿状态。
下面的示例创建一个 LockHandle
然后用钥匙锁定一个项目 Product:1001
时间跨度为 10 秒,这意味着该项目将在 10 秒后自动解锁。
// Preconditions: 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
}
// Precondition: 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 = new LockHandle();
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Lock the item for a time span of 10 seconds
boolean lockAcquired = cache.lock(key, lockSpan, 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
}
// This is an async method
// Precondition: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
var key = "Product:1001";
//Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// Specify time span of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Lock the item for a time span of 10 seconds
var lockAcquired = await this.cache.lock(key, lockSpan, 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
}
# Precondtion: Cache is already connected
# Item is already added in the cache
# Specify the key of the item
key = "Product:1001"
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Specify time span of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Lock the item for a time span of 10 seconds
lock_acquired = cache.lock(key, lock_span, lock_handle)
# Verify if the item is locked successfully
if lock_acquired:
# Item has been successfully locked
print("Lock successful")
else:
# Key does not exist
# Item is already locked with a different LockHandle
print("Lock failed")
备注
为确保操作是故障安全的,建议处理应用程序中的任何潜在异常,如中所述 处理故障.
在获取操作期间锁定项目
项目在从缓存检索期间可以被锁定。 这意味着除非您释放该项目,否则其他人将无法访问该项目。 如果键不匹配,则返回空值。
如果项目未锁定且 acquirelock
设置为 true 那么您将获得该项目以及 LockHandle。
如果一个项目被锁定并且 acquirelock
设置为 false,如果您传递了不正确的或新的空 LockHandle,则 null
返回值,但您将获得之前用于锁定项目的 LockHandle。
如果一个项目被锁定并且 acquirelock
设置为 false 并传递正确的 LockHandle,之前用于锁定项目,然后您将获得该值。
在这个例子中,一个键和 LockHandle
指定获取缓存对象并锁定它。 如果需要获取锁则需要指定true。 这里该项目被锁定,有效期为 10 秒,这意味着该项目将在 10 秒后自动解锁。
// 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 = TimeSpan.FromSeconds(10);
//Create a new LockHandle
LockHandle lockHandle = null;
// Lock the item for a time span of 10 seconds
var result = cache.Get<Product>(key, acquireLock, lockSpan, 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
}
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
//Create a new LockHandle
LockHandle lockHandle = new LockHandle();
// Lock the item for a time span of 10 seconds
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// 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
}
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
//Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// Lock the item for a time span of 10 seconds
//CacheItemVersion set to null and Readthrough Options set to null
var result = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, 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
}
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify time span of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Lock the item for a time span of 10 seconds
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Verify if the item is locked successfully
if result is not None:
# Item has been successfully locked
print("Lock successful")
else:
# Key does not exist
# Item is already locked with a different LockHandle
print("Lock failed")
使用更新操作释放锁
更新项目时,您可以释放锁定,允许其他人使用缓存的数据。 要成功释放锁定的项目,您需要指定 LockHandle
最初用于锁定项目。
LockHandle
应与最初用于锁定项目的相同,否则,您将收到一条异常消息,显示“项目已锁定”。
If releaseLock
设置为 false 你仍然需要通过正确的 Lockhandle
更新项目。
如果某项未锁定,则 LockHandle
和 releaseLock
没有用,它们被忽略。
以下示例锁定缓存中的一个项目,然后使用以下方法获取该项目 LockHandle
。 然后更新该项目,然后使用以下命令将其重新插入到缓存中: 插页 API。
// 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<Product>();
// 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);
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean 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 = new LockHandle();
CacheItem item = cache.getCacheItem(key, acquireLock, lockSpan, lockHandle);
// Update the value of item
product = item.getValue(Product.class);
product.setUnitsInStock(200);
boolean 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
// writeThruOptions set to null
cache.insert(key, item, null, lockHandle,releaseLock);
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Initialize the lockHandle
var lockHandle = new ncache.LockHandle();
// cacheItemVersion set to null and readThruOptions set to null;
var item = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, lockHandle);
// Update the value of item
var product = item.getValue(ncache.JsonDataType.Object);
product.unitsInStock = 200;
var 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
// writeThruOptions set to null
await this.cache.insert(key, item, null, lockHandle, releaseLock);
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify time span of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Lock the item for a time span of 10 seconds
item = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Update the value of item
product = item.get_value(Product)
product.set_units_in_stock(20)
release_lock = True
# Item is already locked with a LockHandle
# Update the item and release the lock as well since release_lock is set true
# Make sure that the LockHandle matches with the already added LockHandle
cache.insert(key, item, lockhandle=lock_handle, releaselock=release_lock)
显式释放锁
显式释放先前锁定的缓存项目的锁定; 您需要指定 LockHandle
最初用于锁定项目。
如果 LockHandle
没有保存,还可以使用另一个重载的 Unlock
它只需要钥匙来解锁物品。
备注
如果无效 LockHandle
如果通过,则不会抛出异常,但该项目将保持锁定状态。
以下示例使用 LockHandle
然后使用 Unlock
API使用 Lockhandle
之前保存的。
// 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 = TimeSpan.FromSeconds(10);
//Create a new LockHandle
LockHandle lockHandle = null;
Product result = cache.Get<Product>(key, acquireLock, lockSpan, ref lockHandle);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
cache.Unlock(key, lockHandle);
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
//Create a new LockHandle
LockHandle lockHandle = new LockHandle();
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
cache.unlock(key, lockHandle);
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
//Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// cacheItemVersion set to null and readThruOptions set to null;
var result = await this.cache.getCacheItem(key,null,null,acquireLock,lockSpan,lockHandle);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
await this.cache.unlock(key, lockHandle);
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify time span of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Make sure that the item is already locked and the saved LockHandle is used
# Unlock locked item using saved LockHandle
cache.unlock(key, lock_handle)
警告
NCache 如果其他重载将忽略锁 Get
, Insert
及 Remove
调用不采取或不使用的方法 LockHandle
.
使用 LockHandle 移除项目
remove方法是一个基本方法,它从缓存中删除key并将删除的对象返回给客户端。 如果将自定义对象添加到缓存中,则remove方法将返回Object。
以下示例使用以下方法获取先前锁定的项目 LockHandle
然后由保存的将其删除 LockHandle
从缓存中使用 删除 API。
// Specify the key of the item
string key = $"Product:1001";
// Initialize the lockHandle
LockHandle lockHandle = null;
// Set acquireLock flag as true
bool acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Get the item using the lockHandle
Product result = cache.Get<Product>(key, acquireLock, lockSpan, ref lockHandle);
// Removing locked item using saved lockHandle.
cache.Remove(key, lockHandle);
// Check if item is successfully removed
if (result != null)
{
if (result is Product)
{
Product product = (Product)result;
}
}
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
//Create a new LockHandle
LockHandle lockHandle = new LockHandle();
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// Removing locked item using saved lockHandle.
//cacheItemVersion and writeThruOptions set to null
cache.remove(key, lockHandle, null, null, Object.class);
Object test = cache.get(key, Object.class);
// check if item has been removed
if (test != null)
{
//Item has not been removed
}
else
{
// Item has been removed
}
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify time span of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
//Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// CacheItemVersion and ReadThruOptions set to null
var result = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, lockHandle, ncache.JsonDataType.Object);
// Removing locked item using saved lockHandle.
//cacheItemVersion and writeThruOptions set to null
await this.cache.remove(key,ncache.JsonDataType.Object, lockHandle, null, null);
var test = await this.cache.getCacheItem(key);
// check if item has been removed
if (test != null)
{
// Item has not been removed
}
else
{
// Item has been removed
}
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify time span of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Removing locked item using saved lock_handle.
cache.remove(key, Product, lockhandle=lock_handle)
test = cache.get_cacheitem(key)
# Check if item has been removed
if test is not None:
# Item has not been removed
print("Remove with lock failed")
else:
# Item has been removed
print("Remove with lock successful")
使用 API 锁定时的特殊注意事项
NCache 提供一组有和没有的 API LockHandle
获取/更新缓存项。 不带 LockHandle
忽略项目锁定。 因此,您应该使用所有锁定 API 来进行数据操作。 例如,如果某个项目被锁定,并且您进行了更新 API 调用,但该调用不接受 LockHandle
作为输入参数,那么无论其锁定状态如何,该项目都将在缓存中更新。
重要
使用锁定功能时,您应该只使用需要以下操作的 API 调用: LockHandle
作为参数。 可以使用不采用锁句柄的 API,但应非常小心,以免影响数据完整性。
备注
如果驱逐/到期, NCache 忽略锁定,这意味着锁定的项目可以由于过期或驱逐而被删除。
拓扑明智的行为
在 镜子 拓扑结构,对于所有锁操作,都会在Active节点上获取锁,并且相同 LockHandle
然后复制到被动节点,以便当被动变为主动时,项目将保持锁定状态。 同样,解锁调用也会复制到被动节点以从被动节点解锁项目。
在 已复制 拓扑中,客户端连接到一个节点,并且对于所有锁定操作 LockHandle
生成接收客户端锁操作,然后同样的 LockHandle
将被复制到所有其他节点以确保数据一致性。 同样,解锁操作也将被复制到所有其他节点。
在 分区的 拓扑结构, LockHandle
生成并存在于包含该项目的同一节点上,并且在状态转移期间 LockHandle
信息也会与项目一起传输,以防项目移动到另一个节点。
In 分区副本 拓扑结构, LockHandle
在包含该项目的活动节点上生成,并且相同 LockHandle
然后在状态传输期间复制到其副本以确保数据一致性。 LockHandle
如果项目移动到另一个节点,信息也会与项目一起传输。
在 客户端缓存,所有基于锁的操作都直接在集群缓存中执行,这意味着 LockHandle
生成并存储在集群缓存中。 客户端缓存中不维护与锁定相关的信息。
更多资讯
NCache 提供了项目锁定的示例应用程序 GitHub上.
参见
.NET: Alachisoft.NCache.运行时.缓存 命名空间。
Java的: COM。alachisoft.ncache.runtime.caching 命名空间。
节点.js: 缓存 类。
Python: ncache.runtime.caching 类。