Redisのに基づいてCAS操作を実装

Redisのに基づいてCAS操作を実装

イントロ

私たちが使用できる.NET同時実行ではInterlocked.CompareExchange、我々は、分散シナリオでのRedisを使用するCAS(コンペア・アンド・スワップ)操作を、何度も実装するため、最近、スタンドアロン動作の前に、ゲームのマイクロチャネルのプロジェクトの前に変更を行いましたいくつかのデータ記憶装置は、メモリベースのオブジェクトベースの直接操作、最近分散をサポートするように変更するのでのRedisの導入であり、元のメモリベースのデータは、元のコードに格納されているRedisのに移行する、いくつかの場所での使用がありますInterlocked.CompareExchange達成CAS動作は、そのように実装されCAS動作Redisのベース募集、同様の機能を必要にマイグレーションのRedis。

CAS

CAS(比較およびスワップ)は、一般的にオブジェクトの同時操作に更新値で使用することができるが、全くロック操作CASなく、CASは楽観的ロック、ロックと直接等価悲観的ロックと等価であり、CAS動作が比較的ですそれはロックされ、直接よりも効率的になります。

RedisのLuaの

Luaスクリプトをサポートするために、最初からバージョン2.6.0をRedisの我々はCAS操作の実現を達成したりする際のRedis次分散リリースロックを導入するために、複数の操作を実行するので、Luaのスクリプトの実行は、アトミックですが、操作はアトミック操作を実現することが可能であるとき、我々は(も行うためにトランザクションを使用することができます)のLuaスクリプトで願っています

Redisのルアに基づいてCASを実装

文字列CASのLuaスクリプト:

比較される値に対応する文字列Redisのキャッシュ・キー、ARGVの種類に応じた操作するKEYS [1] [1]、同じ値をARGV [2]に更新され、そして1を返し、そうでなければ0

if redis.call(""get"", KEYS[1]) == ARGV[1] then
    redis.call(""set"", KEYS[1], ARGV[2])
    return 1
else
    return 0
end

ハッシュCASのLuaスクリプト:

KEYS [1]は、同一の値をハッシュフィールド、ARGV [2]比較値に対応するに対応するハッシュタイプRedisのキャッシュ・キー、ARGV [1]の動作に対応ARGV [3]に更新、及び、他され0を返します

if redis.call(""hget"", KEYS[1], ARGV[1]) == ARGV[2] then
    redis.call(""hset"", KEYS[1], ARGV[1], ARGV[3])
    return 1
else
    return 0
end

StackExchange.Redisを達成するために基づきます

便宜上、ベースIDatabase、以下を達成するために使用いくつかの方法の拡張容易性を提供します。

public static bool StringCompareAndExchange(this IDatabase db, RedisKey key, RedisValue newValue, RedisValue originValue)
{
    return (int)db.ScriptEvaluate(StringCasLuaScript, new[] { key }, new[] { originValue, newValue }) == 1;
}

public static async Task<bool> StringCompareAndExchangeAsync(this IDatabase db, RedisKey key, RedisValue newValue, RedisValue originValue)
{
    return await db.ScriptEvaluateAsync(StringCasLuaScript, new[] { key }, new[] { originValue, newValue })
        .ContinueWith(r => (int)r.Result == 1);
}

public static bool HashCompareAndExchange(this IDatabase db, RedisKey key, RedisValue field, RedisValue newValue, RedisValue originValue)
{
    return (int)db.ScriptEvaluate(HashCasLuaScript, new[] { key }, new[] { field, originValue, newValue }) == 1;
}

public static async Task<bool> HashCompareAndExchangeAsync(this IDatabase db, RedisKey key, RedisValue field, RedisValue newValue, RedisValue originValue)
{
    return await db.ScriptEvaluateAsync(HashCasLuaScript, new[] { key }, new[] { field, originValue, newValue })
        .ContinueWith(r => (int)r.Result == 1);
}

実際の使用

次のテストコードを参照して使用することができます。

[Fact]
public void StringCompareAndExchangeTest()
{
    var key = "test:String:cas";
    var redis = DependencyResolver.Current
        .GetRequiredService<IConnectionMultiplexer>()
        .GetDatabase();
    redis.StringSet(key, 1);

    // set to 3 if now is 2
    Assert.False(redis.StringCompareAndExchange(key, 3, 2));
    Assert.Equal(1, redis.StringGet(key));

    // set to 4 if now is 1
    Assert.True(redis.StringCompareAndExchange(key, 4, 1));
    Assert.Equal(4, redis.StringGet(key));

    redis.KeyDelete(key);
}

[Fact]
public void HashCompareAndExchangeTest()
{
    var key = "test:Hash:cas";
    var field = "testField";

    var redis = DependencyResolver.Current
        .GetRequiredService<IConnectionMultiplexer>()
        .GetDatabase();
    redis.HashSet(key, field, 1);

    // set to 3 if now is 2
    Assert.False(redis.HashCompareAndExchange(key, field, 3, 2));
    Assert.Equal(1, redis.HashGet(key, field));

    // set to 4 if now is 1
    Assert.True(redis.HashCompareAndExchange(key, field, 4, 1));
    Assert.Equal(4, redis.HashGet(key, field));

    redis.KeyDelete(key);
}

リファレンス

おすすめ

転載: www.cnblogs.com/weihanli/p/redis-based-cas.html