[REDIS] StackExchange.Redis - LockTake / LockRelease 사용
REDISStackExchange.Redis - LockTake / LockRelease 사용
나는 StackExchange.Redis와 레디 스를 사용하고 있습니다. 나는 데이터의 조작을 동기화해야합니다 어느 시점 액세스 및 편집 같은 키의 가치, 그래서 다중 스레드를 가지고있다.
사용 가능한 기능을 보면, 나는 두 가지 기능 TakeLock 및 ReleaseLock가있는 것을 알 수있다. 그러나 이러한 기능 키와 예상되는 하나의 키가 잠겨되는 것이 아니라 값 매개 변수를 모두 가져 가라. GitHub의에 intellisene 문서와 소스는 키와 값 매개 변수에 전달하는 LockTake 및 LockRelease 기능 또는 무엇을 사용하는 방법에 대해 설명하지 않습니다.
Q : StackExchange.Redis에 LockTake 및 LockRelease의 올바른 사용법은 무엇입니까?
내가하는 것을 목표로하고있는 무슨의 의사 코드 예제 :
//Add Items Before Parallel Execution
redis.StringSet("myJSONKey", myJSON);
//Parallel Execution
Parallel.For(0, 100, i =>
{
//Some work here
//....
//Lock
redis.LockTake("myJSONKey");
//Manipulate
var myJSONObject = redis.StringGet("myJSONKey");
myJSONObject.Total++;
Console.WriteLine(myJSONObject.Total);
redis.StringSet("myJSONKey", myNewJSON);
//Unlock
redis.LockRelease("myJSONKey");
//More work here
//...
});
해결법
-
==============================
1.잠금 3 개 부분이 있습니다 :
잠금 3 개 부분이 있습니다 :
다른 값이 떠오르는하지 않는 경우, GUID는 적절한 "값"을 만들 수 있습니다. 우리는 시스템 이름 (또는 여러 프로세스가 동일한 시스템에서 경쟁 할 수 있다면 컴퓨터 이름의 munged 버전)를 사용하는 경향이있다.
또한, 잠금을 복용 투기 차단되지 않습니다. 당신이 잠금을 얻기 위해 실패, 따라서이 테스트 할 필요가 있습니다 아마도 일부 재시도 논리를 추가하는 것이 전적으로 가능하다.
전형적인 예는 수 있습니다 :
RedisValue token = Environment.MachineName; if(db.LockTake(key, token, duration)) { try { // you have the lock do work } finally { db.LockRelease(key, token); } }
다시 (이 시간 초과 경우) 성공을 확인하기 위해 기억 - 작업이 (특히, 루프) 긴 경우, 당신은 중간에 일부 가끔 LockExtend 호출을 추가 할 수 있습니다.
당신은 경쟁이 신중한 작업에 대해 걱정할 필요가 없습니다 모든 개별 레디 스 명령은, 원자 있음을 유의하십시오. 더 다중 작업 단위 복합체를 들어, 거래 및 스크립팅 옵션이 있습니다.
-
==============================
2.에 대한 코드의 내 부분이 있습니다 자물쇠 -> GET-> 수정 (필요한 경우) -> 주석 해제 작업.
에 대한 코드의 내 부분이 있습니다 자물쇠 -> GET-> 수정 (필요한 경우) -> 주석 해제 작업.
public static T GetCachedAndModifyWithLock<T>(string key, Func<T> retrieveDataFunc, TimeSpan timeExpiration, Func<T, bool> modifyEntityFunc, TimeSpan? lockTimeout = null, bool isSlidingExpiration=false) where T : class { int lockCounter = 0;//for logging in case when too many locks per key Exception logException = null; var cache = Connection.GetDatabase(); var lockToken = Guid.NewGuid().ToString(); //unique token for current part of code var lockName = key + "_lock"; //unique lock name. key-relative. T tResult = null; while ( lockCounter < 20) { //check for access to cache object, trying to lock it if (!cache.LockTake(lockName, lockToken, lockTimeout ?? TimeSpan.FromSeconds(10))) { lockCounter++; Thread.Sleep(100); //sleep for 100 milliseconds for next lock try. you can play with that continue; } try { RedisValue result = RedisValue.Null; if (isSlidingExpiration) { //in case of sliding expiration - get object with expiry time var exp = cache.StringGetWithExpiry(key); //check ttl. if (exp.Expiry.HasValue && exp.Expiry.Value.TotalSeconds >= 0) { //get only if not expired result = exp.Value; } } else //in absolute expiration case simply get { result = cache.StringGet(key); } //"REDIS_NULL" is for cases when our retrieveDataFunc function returning null (we cannot store null in redis, but can store pre-defined string :) ) if (result.HasValue && result == "REDIS_NULL") return null; //in case when cache is epmty if (!result.HasValue) { //retrieving data from caller function (from db from example) tResult = retrieveDataFunc(); if (tResult != null) { //trying to modify that entity. if caller modifyEntityFunc returns true, it means that caller wants to resave modified entity. if (modifyEntityFunc(tResult)) { //json serialization var json = JsonConvert.SerializeObject(tResult); cache.StringSet(key, json, timeExpiration); } } else { //save pre-defined string in case if source-value is null. cache.StringSet(key, "REDIS_NULL", timeExpiration); } } else { //retrieve from cache and serialize to required object tResult = JsonConvert.DeserializeObject<T>(result); //trying to modify if (modifyEntityFunc(tResult)) { //and save if required var json = JsonConvert.SerializeObject(tResult); cache.StringSet(key, json, timeExpiration); } } //refresh exiration in case of sliding expiration flag if(isSlidingExpiration) cache.KeyExpire(key, timeExpiration); } catch (Exception ex) { logException = ex; } finally { cache.LockRelease(lockName, lockToken); } break; } if (lockCounter >= 20 || logException!=null) { //log it } return tResult; }
및 사용 :
public class User { public int ViewCount { get; set; } } var cachedAndModifiedItem = GetCachedAndModifyWithLock<User>( "MyAwesomeKey", () => { //return from db or kind of that return new User() { ViewCount = 0 }; }, TimeSpan.FromMinutes(10), user=> { if (user.ViewCount< 3) { user.ViewCount++; return true; //save it to cache } return false; //do not update it in cache }, TimeSpan.FromSeconds(10),true);
그 코드는 (예를 들어, 캐시 등 덜 카운트 통화 거래를 추가 할 수 있습니다)하지만 난 기꺼이 당신을 위해 도움이 될 것입니다 향상시킬 수있다.
from https://stackoverflow.com/questions/25127172/stackexchange-redis-locktake-lockrelease-usage by cc-by-sa and MIT license
'REDIS' 카테고리의 다른 글
[REDIS] 데이터 구조 스레드 안전에 레디 스 작업은인가 (0) | 2019.12.31 |
---|---|
[REDIS] 레디 스 : 저장하는 오프닝 .rdb 실패 : 권한이 거부 (0) | 2019.12.31 |
[REDIS] 어떻게 레디 스에서 "HSET"아이의 키를 "EXPIRE"로? (0) | 2019.12.31 |
[REDIS] 어떻게 핸들 세션 레디 스를 내놓고 만료? (0) | 2019.12.31 |
[REDIS] 레디 스 : 가능한 배열 또는 소트 세트의 요소를 만료하는? (0) | 2019.12.31 |