캐시 항목 버전 관리를 통한 잠금(낙관적 잠금)
낙관적 잠금 NCache 읽기 작업이 많은 분산 애플리케이션을 위해 설계된 비차단 동시성 제어 메커니즘을 제공합니다. 작업 수행 시간 동안 캐시 항목을 잠그는 대신, NCache 사용 CacheItemVersion각 캐시 항목에 자동으로 연결되고 업데이트될 때마다 증가하는 숫자 버전입니다. 이를 통해 클라이언트는 다른 스레드를 차단하지 않고 동시 수정을 감지할 수 있으므로 확장성이 향상되고 스레드 기아 현상이 방지됩니다.
클라이언트가 캐시에서 항목을 가져올 때 해당 항목의 현재 버전도 함께 가져올 수 있습니다. 업데이트 또는 삭제 작업 중에는 동일한 버전이 캐시에 다시 저장됩니다. 서버의 버전이 변경된 경우 작업이 실패합니다. 비관적 잠금항목이 작업이 완료될 때까지 잠긴 상태로 유지되어 스레드 기아 현상을 일으킬 수 있는 반면, 낙관적 잠금은 읽기 작업이 빈번하고 쓰기 충돌이 드문 시나리오에 더 적합합니다.
낙관적 잠금을 사용하는 경우
앞의 예시에서는 두 사용자가 동시에 사용하는 하나의 은행 계좌를 살펴보았습니다. 사용자 1이 입금 거래를 하기 위해 계좌에 대한 잠금을 획득했다고 가정해 보겠습니다. 사용자 2는 출금 거래를 하기 전에 사용자 1이 잠금을 해제하기를 기다리고 있습니다. 만약 사용자 1이 네트워크 연결 문제로 인해 잠금을 해제하지 못하고 불안정한 상태에 빠지게 되면, 사용자 2는 이러한 오류를 알지 못한 채 계속 기다리게 됩니다. 이는 사용자 2가 잠금이 해제될 때까지 차단된 상태로 남아 있게 되므로 기아 상태(starvation)를 초래합니다.
이 문제를 방지하기 위해 낙관적 잠금(Optimistic Locking)이 유용한 해결책입니다. 낙관적 잠금을 사용하면 사용자 1은 계정을 잠그지 않고도 계정을 업데이트할 수 있으며, 항목 버전도 그에 따라 업데이트됩니다. 사용자 2가 데이터를 업데이트하려고 할 때, 항목 버전을 기반으로 업데이트된 버전을 받게 되므로 데이터 무결성 문제가 발생하지 않습니다. 만약 어떤 사용자라도 오래된 항목 버전을 사용하여 데이터를 수정하면, 버전이 일치하지 않기 때문에 작업이 실패하게 됩니다.
The CacheItemVersion 이를 통해 애플리케이션 개발에 새로운 차원을 더할 수 있습니다. NCache애플리케이션은 다음을 통해 낙관적 동시성을 달성합니다. NCache 항목 버전 관리. 캐시에 항목이 추가되면, CacheItemVersion 이 값은 캐시 클라이언트로 반환됩니다. 이 값은 해당 항목에 대해 수행된 업데이트 횟수를 나타냅니다. 업데이트가 있을 때마다 항목 버전이 증가합니다.
사전 조건
사용하기 전에 NCache 클라이언트 측 API를 사용할 때 다음 전제 조건을 충족하는지 확인하십시오.
- Java 클라이언트 애플리케이션에 대해 다음 Maven 종속성을 추가합니다.
pom.xml 파일 :
<dependency>
<groupId>com.alachisoft.ncache</groupId>
<!--for NCache Enterprise-->
<artifactId>ncache-client</artifactId>
<version>x.x.x</version>
</dependency>
- .NET 클라이언트 애플리케이션에 다음 NuGet 패키지 중 하나를 설치하세요.
- 기업 :
Install-Package Alachisoft.NCache.SDK -Version 4.9.1.0
- 새로운 콘솔 애플리케이션을 만듭니다.
- 추가되는 데이터가 직렬화 가능.
- 추가 NCache 참고자료 위치를 찾아서
%NCHOME%\NCache\bin\assembly\4.0 추가 Alachisoft.NCache.Web Alachisoft.NCache.Runtime 적절한.
- 포함시키다
Alachisoft.NCache.Web.Caching 애플리케이션의 네임스페이스.
- 에 대한 자세한 내용을 보려면 NCache 레거시 API를 다운로드하세요 NCache 4.9 문서는 다음과 같이 사용 가능합니다. .지퍼 에 파일 Alachisoft 웹 사이트.
아이템 버전을 사용하여 아이템을 검색하고 업데이트하는 방법
An 추가 연산은 CacheItemVersion항목이 처음 추가될 경우, 해당 항목의 생성 타임스탬프가 포함된 긴 값이 반환됩니다. 이 버전은 해당 항목에 대한 작업이 수행될 때마다 "1"씩 증가합니다.
낙관적 잠금은 사용자가 캐시에 있는 항목의 최신 버전을 항상 사용할 수 있도록 보장합니다. 사용자가 오래된 버전으로 작업을 시도하는 경우, NCache 예외가 발생하여 사용자에게 캐시에서 최신 버전을 가져오도록 안내합니다.
아래 예시에서는 여러 애플리케이션에서 캐시를 사용하며 제품 데이터를 저장합니다. CacheItem 캐시에 새 항목이 추가됩니다. 두 애플리케이션 모두 현재 버전으로 해당 항목을 가져옵니다. 애플리케이션 1은 productName을 수정하고 캐시에 항목을 다시 삽입하여 항목 버전을 증가시킵니다. 애플리케이션 2는 여전히 이전 버전을 보유하고 있습니다. 애플리케이션 2가 항목의 재고 수량을 업데이트하고 캐시에 항목을 다시 삽입하면 삽입이 실패합니다. 애플리케이션 2는 해당 항목에 대한 작업을 수행하기 위해 업데이트된 버전을 가져와야 합니다. CacheItem.
주의 사항
다음 방법 중 하나를 사용하여 캐시에 항목을 추가할 수 있습니다. 추가 or 끼워 넣다 방법.
- The
Add 메서드는 캐시에 새 항목을 추가하고 처음으로 항목 버전을 저장합니다.
- The
Insert 이 메서드는 캐시에 항목이 없으면 항목을 추가합니다. 항목이 이미 있으면 기존 값을 덮어쓰고 항목 버전을 업데이트합니다.
다음 코드 섹션에서는 애플리케이션에서 수행하는 작업을 설명합니다.
try
{
// Precondition: Cache is already connected
// The item version is saved when item was added in cache
// 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
// If result is Product type
var prod = new Product();
prod = cacheItem.GetValue<Product>();
prod.UnitsInStock++;
// Create a new CacheItem with updated value
var updateItem = new CacheItem(prod);
// Set the item version. This version will be used to compare the item version of cache item
updateItem.Version = version;
cache.Insert(key, updateItem);
// If it matches, the insert will be successful, otherwise it will fail
}
else
{
// Item could not be retrieved due to outdated CacheItemVersion
}
}
catch (OperationFailedException ex)
{
// NCache specific exception
if (ex.ErrorCode == NCacheErrorCodes.ITEM_WITH_VERSION_DOESNT_EXIST)
{
// If the itemversion mismatches with the already added itemversion
}
else
{
// Exception can occur due to:
// Connection Failures
// Operation Timeout
// Operation performed during state transfer
}
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
try
{
// Precondition: Cache is already connected
// The item version is saved when item was added in cache
// Specify the key of the CacheItem
String key = "Product:1001";
// Initialize the CacheItemVersion
CacheItemVersion version = new CacheItemVersion();
// readThruOptions set to null
CacheItem cacheItem = cache.getCacheItem(key, version, null);
// If result is not null
if (cacheItem != null)
{
var prod = new Product();
prod = cacheItem.getValue(Product.Class);
prod.unitsInStock++;
// Update the retrieved CacheItem according to requirement
// Create a new CacheItem with updated value
CacheItem updatedItem = new CacheItem(prod);
// Set the item version. This version will be used to compare the item version of cache item
updatedItem.setCacheItemVersion(version);
cache.insert(key, updatedItem);
// If it matches, the insert will be successful, otherwise it will fail
}
else
{
// Item could not be retrieved due to outdated CacheItemVersion
}
}
catch (OperationFailedException ex)
{
if(ex.getErrorCode() == NCacheErrorCodes.ITEM_WITH_VERSION_DOESNT_EXIST)
{
// If the itemversion mismatches with the already added itemversion
}
else
{
// Exception can occur due to:
// Connection Failures
// Operation Timeout
// Operation performed during state transfer
}
}
catch (Exception ex)
{
// Any generic exception like IllegalArgumentException or NullPointerException
}
try:
# Precondition: Cache is already connected
# The item version is saved when item was added in cache
# Specify the key of the CacheItem
key = "Product:1001"
# Initialize the CacheItemVersion
version = ncache.CacheItemVersion(0)
cache_item = cache.get_cacheitem(key, cacheitemversion=version)
# If result is not None
if cache_item is not None:
prod = cache_item.get_value(Product)
prod.set_units_in_stock(20)
# Update the retrieved CacheItem according to requirement
# Create a new CacheItem with updated value
updated_item = ncache.CacheItem(prod)
# Set the item_version. This version will be used to compare the
# item version of cache item
updated_item.set_cache_item_version(version)
cache.insert(key, updated_item)
# If it matches, the insert will be successful, otherwise it will fail
except ncache.OperationFailedException as ex:
# NCache specific exception can occur due to:
# - Connection failures
# - Operation performed during state transfer
# - Operation timeout
pass
except Exception as ex:
# Any generic exception
pass
try
{
// This is an async method
// Precondition: Cache is already connected
// The item version is saved when item was added in cache
// Specify the key of the CacheItem
var key = "Product:1001";
// Initialize the CacheItemVersion
var version = new ncache.CacheItemVersion();
// readThruOptions set to null
var cacheItem = await this.cache.getCacheItem(key, null, version);
// If result is not null
if (cacheItem != null)
{
var prod = new Product();
prod = cacheItem.getValue(ncache.JsonDataType.Object);
prod.unitsInStock++;
// Update the retrieved CacheItem according to requirement
// Create a new CacheItem with updated value
// You also need to specify the FQN(Fully Qualified Name) of the class
var updatedItem = new ncache.CacheItem(prod,"FQN.Product");
// Set the item version. This version will be used to compare the item version of cache item
updatedItem.setCacheItemVersion(version);
await this.cache.insert(key, updatedItem);
// If it matches, the insert will be successful, otherwise it will fail
}
}
catch (error)
{
// Handle errors
}
try
{
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// The item version is saved when item was added in cache
// Specify the key of the CacheItem
string key = "Product:1001";
// Initialize the CacheItemVersion
CacheItemVersion version = null;
// Retrieve the cache item with version
CacheItem cacheItem = cache.GetCacheItem(key, ref version);
if (cacheItem != null)
{
// Cast the object to Product
Product prod = (Product)cacheItem.Value;
// Modify the product
prod.UnitsInStock++;
// Create updated cache item and assign the version
CacheItem updatedItem = new CacheItem(prod);
updatedItem.Version = version;
// Attempt to update (will only succeed if version matches)
cache.Insert(key, updatedItem);
// If it matches, the insert will be successful, otherwise it will fail
}
else
{
// Item could not be retrieved due to outdated CacheItemVersion
}
}
catch (OperationFailedException ex)
{
// NCache specific exception
if (ex.ErrorCode == NCacheErrorCodes.ITEM_WITH_VERSION_DOESNT_EXIST)
{
// If the itemversion mismatches with the already added itemversion
}
else
{
// Exception can occur due to:
// Connection Failures
// Operation Timeout
// Operation performed during state transfer
}
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
주의 사항
작업이 안전하도록 하려면 에 설명된 대로 응용 프로그램 내에서 잠재적인 예외를 처리하는 것이 좋습니다. 처리 실패.
새로운 캐시 항목 버전이 있는 경우 이를 검색하는 방법
The GetIfNewer 이 메서드는 캐시에 최신 버전이 있는 경우 항목을 가져오는 데 사용됩니다. 메서드 호출 시 인수로 현재 버전을 지정하면 캐시는 적절한 결과를 반환합니다. 지정된 버전이 캐시에 있는 버전보다 오래된 경우에만 메서드는 새 항목을 반환하고, 그렇지 않으면 새 항목을 반환합니다. null 반환됩니다.
다음 예제는 키를 사용하여 캐시에 항목을 추가합니다. Product:1001 그리고 해당 아이템 버전을 가져옵니다. 그런 다음 다음을 사용하여 아이템을 검색합니다. GetIfNewer 이 메서드는 캐시에 더 최신 버전이 있는 경우에만 캐시 항목을 가져옵니다.
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = FetchProductByProductID(1001);
// Generate a unique key for this item
string key = $"Product:{product.ProductID}";
// Create a new CacheItem
var item = new CacheItem(product);
// Add CacheItem to cache with new item version
CacheItemVersion version = cache.Insert(key, item);
// Get object from cache
var result = cache.GetIfNewer<Product>(key, ref version);
// Check if updated item is available
if (result != null)
{
// An item with newer version is available
if (result is Product)
{
// Perform operations according to business logic
}
}
else
{
// No new item version is available
}
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = fetchProductByProductID(1001);
// Generate a unique key for this item
String key = "Product:" + product.getProductID();
// Create a new CacheItem
CacheItem item = new CacheItem(product);
// Add CacheItem to cache with new item version
CacheItemVersion version = cache.insert(key, item);
// Get object from cache
Object result = cache.getIfNewer(key,version,Object.class);
// Check if updated item is available
if (result != null)
{
// An item with newer version is available
if (result == Product)
{
// Perform operations according to business logic
}
}
else
{
// No new item version is available
}
# Precondition: Cache is already connected
# Get updated product from database against given product ID
product = fetch_product_from_db(1001)
# Generate a unique key for this item
key = "Product:" + product.get_product_id()
# Create a new CacheItem
item = ncache.CacheItem(product)
# Add CacheItem to cache with new item version
version = cache.insert(key, item)
# Get object from cache
result = cache.get_if_newer(key, cacheitemversion=version, objtype=Product)
# Check if updated item is available
if result is not None:
# Perform operations according to business logic
print("New version is available")
else:
# No new item version is available
print("New version is not available")
// Precondition: Cache is already connected
// This is an async method
// Get updated product from database against given product ID
var product = this.fetchProductByProductID(1001);
// Generate a unique key for this item
var key = "Product:" + product.getProductID();
// Create a new CacheItem
// You also need to specify the FQN(Fully Qualified Name) of the class
var item = new ncache.CacheItem(product,"FQN.Product");
// Add CacheItem to cache with new item version
var version = await this.cache.insert(key, item);
// Get object from cache
var result = await this.cache.getIfNewer(key, ncache.JsonDataType.Object, version);
// Check if updated item is available
if (result != null)
{
// Perform operations according to business logic
}
else
{
// No new item version is available
}
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = FetchProductByProductID(1001);
// Generate a unique key for this item
string key = $"Product:{product.ProductID}";
// Insert using CacheItem
CacheItem item = new CacheItem(product);
cache.Insert(key, item);
// Retrieve the item to get version
CacheItem cachedItem = cache.GetCacheItem(key);
CacheItemVersion itemVersion = cachedItem.Version;
object result = cache.GetIfNewer(key, ref itemVersion);
// Check if updated item is available
if (result != null)
{
// An item with newer version is available
if (result is Product)
{
// Perform operations according to business logic
}
}
else
{
// No new item version is available
}
아이템 버전이 있는 아이템을 제거하는 방법
다음의 오버로드를 사용하여 캐시에서 항목을 제거할 수 있습니다. 제거 아이템 버전을 인수로 받는 메서드입니다. 지정된 버전이 캐시에 저장된 버전과 다르면 예외가 발생합니다.
다음 예는 항목 버전을 지정하여 캐시에서 항목을 제거하는 방법을 보여줍니다. 제거 방법.
팁
물품 제거 과정을 모니터링/확인할 수 있습니다.
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = FetchProductByProductID(1001);
// Cache key remains the same for this product
string key = $"Product:{product.ProductID}";
// Create a new CacheItem
var item = new CacheItem(product);
// Insert CacheItem to cache with new item version
CacheItemVersion version = cache.Insert(key, item);
// Remove the item from the cache using the item version
cache.Remove(key, null, version);
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = fetchProductByProductID(1001);
// Cache key remains the same for this product
String key = "Product:" + product.getProductID();
// Create a new CacheItem
CacheItem item = new CacheItem(product);
// Add CacheItem to cache with new item version
CacheItemVersion version = cache.insert(key, item);
// Remove the item from the cache using the item version
// lockHandle and writeThruOptions set to null
cache.remove(key, null, version, null, Object.class);
# Precondition: Cache is already connected
# Get updated product from database against given product ID
product = fetch_product_from_db(1001)
# Cache key remains the same for this product
key = "Product:" + product.get_product_id()
# Create a new CacheItem
item = ncache.CacheItem(product)
# Add CacheItem to cache with new item version
version = cache.insert(key, item)
# Remove the item from the cache using the item version
cache.remove(key, version=version, objtype=Product)
value = cache.get(key, Product)
if value is None:
# Item has been removed from cache
print("Remove successful")
else:
# Item is still in cache
print("Remove failed")
// Precondition: Cache is already connected
// Get updated product from database against given product ID
var product = this.fetchProductByProductID(1001);
// Cache key remains the same for this product
var key = "Product:" + product.getProductID();
// Create a new CacheItem
// You also need to specify the FQN(Fully Qualified Name) of the class
var item = new ncache.CacheItem(product,"FQN.Product");
// Add CacheItem to cache with new item version
var version = await this.cache.insert(key, item);
// Remove the item from the cache using the item version
// lockHandle and writeThruOptions set to null
await this.cache.remove(key, ncache.JsonDataType.Object, null, version, null);
var value = await this.cache.get(key, ncache.JsonDataType.Object);
if (value == null)
{
// Item has been removed from cache
}
else
{
// Item is still in cache
}
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Get updated product from database against given product ID
Product product = FetchProductFromDB("1001");
// Cache key remains the same for this product
string key = $"Product:{product.ProductID}";
// Create a new CacheItem
var item = new CacheItem(product);
// Insert CacheItem to the cache
cache.Insert(key, item);
// Remove the item from the cache
cache.Remove(key);
토폴로지 현명한 동작
항목 버전 관리의 동작 방식은 사용 환경에 따라 약간씩 다릅니다. NCache 토폴로지입니다. 이를 통해 클러스터 전체의 일관성을 보장합니다.
. 복제 토폴로지에서 클라이언트는 하나의 노드에 연결되고, 항목 버전은 클라이언트의 업데이트/추가 작업을 수신하는 노드에서 생성됩니다. 이 버전과 항목은 데이터 일관성을 위해 다른 모든 노드로 복제됩니다.
. 파티션 된 토폴로지에서 항목 버전은 해당 항목을 포함하는 노드에 생성되어 존재합니다. 상태 전송 시 항목이 다른 노드로 이동하는 경우 버전도 항목과 함께 전송됩니다.
. 파티션-복제본 토폴로지에서 버전은 해당 항목을 포함하는 활성 노드에서 생성됩니다. 데이터 일관성을 유지하기 위해 동일한 버전과 항목이 복제본으로 복제됩니다. 상태 전송 시 항목이 다른 노드로 이동하는 경우 버전도 항목과 함께 전송됩니다.
. 클라이언트 캐시 토폴로지 및 모든 버전 관련 정보는 클러스터 캐시에 유지됩니다. 버전 관련 API가 호출될 때마다 클라이언트는 클러스터 캐시에서 해당 버전을 가져옵니다.
추가 자료
NCache 항목 잠금을 위한 샘플 애플리케이션을 제공합니다. GitHub의.
도 참조
.그물: Alachisoft.NCache.런타임.캐싱 네임 스페이스.
자바 : com.alachisoft.ncache.런타임.캐싱 네임 스페이스.
파이썬 : ncache.런타임.캐싱 기준 치수.
Node.js : 캐시 클래스입니다.