7

私はいくつかのAPI呼び出しに送るカウンタを生成する必要があります。私のアプリケーションは複数のノードで動作しているので、独自のカウンタを生成したいと思っていました。 私はコードRedisはロック付きでインクリメントされています

public static long GetTransactionCountForUser(int telcoId) 
    { 
     long valreturn = 0; 
     string key = "TelcoId:" + telcoId + ":Sequence"; 
     if (Muxer != null && Muxer.IsConnected && (Muxer.GetDatabase()) != null) 
     { 
      IDatabase db = Muxer.GetDatabase(); 
      var val = db.StringGet(key); 
      int maxVal = 999; 
      if (Convert.ToInt32(val) < maxVal) 
      { 
       valreturn = db.StringIncrement(key); 
      } 
      else 
      { 
       bool isdone = db.StringSet(key, valreturn); 
       //db.SetAdd(key,new RedisValue) .StringIncrement(key, Convert.ToDouble(val)) 
      } 
     } 
     return valreturn; 
    } 

して実行するタスク並列librayを経由してそれをテストして、次の試してみました。 私の最後のロジックは

public static long GetSequenceNumberForTelcoApiCallViaLuaScript(int telcoId) 
    { 
     long valreturn = 0; 
     int maxIncrement = 9999;//todo via configuration 
     if (true)//todo via configuration 
     { 
      IDatabase db; 
      string key = "TelcoId:" + telcoId + ":SequenceNumber"; 
      if (Muxer != null && Muxer.IsConnected && (db = Muxer.GetDatabase()) != null) 
      { 
       valreturn = (int)db.ScriptEvaluate(@" 
        local result = redis.call('incr', KEYS[1]) 
        if result > tonumber(ARGV[1]) then 
        result = 1 
        redis.call('set', KEYS[1], result) 
        end 
        return result", new RedisKey[] { key }, flags: CommandFlags.HighPriority, values: new RedisValue[] { maxIncrement }); 
      } 
     } 
     return valreturn; 
    } 
+0

を通知を受けるだろう、挿入を行うと、返されたSCOPE_IDENTITY()を使用します。常にユニークなものを返すべきです。 –

+0

dbの挿入/ dbの往復を避けたかったのです。私はCache Via Redisをサポートしています。これは完全に –

+0

に調整したかったのです。@ KamranShahid 'string.Format'を使用してパラメータ化してください。私は好ましい方法を示すために私の例を編集します –

答えて

10

を以下の通りである:私は境界値を持っているときに私が見るものは、複数の時間0エントリは

に設定されていることを私は

アップデートを行うために必要なものを補正教えてくださいです確かに、 "get"の条件がまだ適用されていることを確認せずに、 "get"、(レイテンシと思考)、 "set"を行っているので、あなたのコードはロールオーバーの境界線では安全ではありません。

1 
2 
... 
999 
1000 // when "get" returns 998, so you do an incr 
1001 // ditto 
1002 // ditto 
0 // when "get" returns 999 or above, so you do a set 
0 // ditto 
0 // ditto 
1 

オプション:

  1. あなたのロジックの並行処理を行うためにトランザクションと制約のAPIを使用して、サーバーがアイテム約1000ビジー状態である場合、のようなものを含め狂気出力のすべての種類を取得することが可能であろう-safe
  2. 今すぐ ScriptEvaluate

を経由してのLuaスクリプトとして、あなたのロジックを書き換え

  • are hard(オプション1あたり)Redisの取引。個人的には、私は "2"を使用したいと思います - コードとデバッグを簡単にすることに加えて、 "get、watch、get、multi、incr/set、exec /破棄 "、および"再試行から開始 "ループがアボートシナリオを説明する。あなたが好きなら、私はあなたのためにLuaとしてそれを書こうとすることができます - それは約4行でなければなりません。


    はここでLuaの実装です:

    string key = ... 
    for(int i = 0; i < 2000; i++) // just a test loop for me; you'd only do it once etc 
    { 
        int result = (int) db.ScriptEvaluate(@" 
    local result = redis.call('incr', KEYS[1]) 
    if result > 999 then 
        result = 0 
        redis.call('set', KEYS[1], result) 
    end 
    return result", new RedisKey[] { key }); 
        Console.WriteLine(result); 
    } 
    

    注:あなたは最大をパラメータ化する必要がある場合は、あなたが使用します。

    if result > tonumber(ARGV[1]) then 
    

    と:

    int result = (int)db.ScriptEvaluate(..., 
        new RedisKey[] { key }, new RedisValue[] { max }); 
    

    (したがってARGV[1]は0から値を取ります)

    は、他のサーバーと競合していない)何ScriptEvaluateの呼び出しである(eval/evalshaことを理解する必要があるものはincrと可能setの間で変化しないので、を要求します。つまり、複雑なwatchなどのロジックは必要ありません。

    ここでトランザクション/制約APIを経由して(と思う!)と同じです:

    static int IncrementAndLoopToZero(IDatabase db, RedisKey key, int max) 
    { 
        int result; 
        bool success; 
        do 
        { 
         RedisValue current = db.StringGet(key); 
         var tran = db.CreateTransaction(); 
         // assert hasn't changed - note this handles "not exists" correctly 
         tran.AddCondition(Condition.StringEqual(key, current)); 
         if(((int)current) > max) 
         { 
          result = 0; 
          tran.StringSetAsync(key, result, flags: CommandFlags.FireAndForget); 
         } 
         else 
         { 
          result = ((int)current) + 1; 
          tran.StringIncrementAsync(key, flags: CommandFlags.FireAndForget); 
         } 
         success = tran.Execute(); // if assertion fails, returns false and aborts 
        } while (!success); // and if it aborts, we need to redo 
        return result; 
    } 
    

    えっ、複雑?ここ簡単な成功事例は次のようになります。

    GET {key} # get the current value 
    WATCH {key} # assertion stating that {key} should be guarded 
    GET {key} # used by the assertion to check the value 
    MULTI  # begin a block 
    INCR {key} # increment {key} 
    EXEC   # execute the block *if WATCH is happy* 
    

    ...仕事のかなりある、とマルチプレクサのパイプラインのストールを伴います。より複雑なケース(アサーションの失敗、ウォッチの失敗、ラップアラウンド)はわずかに異なる出力を持ちますが、動作するはずです。

  • +0

    stackexchange.redis内での取引にお手伝いできますか?ところで、出力ウィンドウで同じ値を見たことがあります:) –

    +0

    @KamranShahidは動作するLuaの例を追加しました。 –

    +0

    私は非常に初心者レベルです。私はこのルアスクリプトについて聞いたことがあります。私は私のロジックをこのLuaスクリプト評価にどのようにマップすることができるか少し調べてみましょう –

    1

    あなたはWATCH commandを使用することができます - この方法では、値が変更された場合、あなたはあなただけのID列を持つ単純なテーブルを使用していないのはなぜ

    +0

    どのように私はそれをstackexchange.redis APIの利点を得ることができますか? –

    +0

    @KamranShahid https://github.com/StackExchange/StackExchange.Redis/blob/master/Docs/Transactions.mdのようなものを試してください – chester89

    +0

    ありがとうございました。私はそれを試してみます –

    関連する問題