Shared Keyspace for StackExchange Redis

When developing a component using Redis, I found a good template for prefixing all the keys used by this component so that it does not interfere with other components.

Examples:

  • A user managing components can use keys with a prefix user:, and a component managing logs can use keys with a prefix log:.

  • On a tiered system, I want each client to use a separate key space in Redis to ensure that their data does not interfere. The prefix will then be similar customer:<id>:to all keys associated with a particular client.

Using Redis is still new to me. My first idea for this partitioning template was to use separate database identifiers for each partition. However, this seems like a bad idea, because the number of databases is limited, and it seems to be a feature that should be deprecated.

An alternative to this would be to allow each component to get an instance IDatabaseand a RedisKey, which it should use to prefix all keys. (I am using StackExchange.Redis )

I was looking for a wrapper IDatabasethat automatically prefixes all keys so that the components can use the interface IDatabaseas is, without worrying about its key space. I haven't found anything though.

, : StackExchange Redis?

IDatabase, . , IDatabase. : SORT RANDOMKEY.

+4
2

IDatabase, .

IDatabase

    ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect("localhost");
    IDatabase fullDatabase = multiplexer.GetDatabase();
    IDatabase partitioned = fullDatabase.GetKeyspacePartition("my-partition");

:

public bool SetAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
{
    return this.Inner.SetAdd(this.ToInner(key), value, flags);
}

RedisKey, .

CreateBatch CreateTransaction , - ( IDatabaseAsync).

KeyRandomAsync KeyRandom . NotSupportedException. , @Marc Gravell:

- , , NotSupportedException ( "RANDOMKEY , " ) ( )/p >

ScriptEvaluate ScriptEvaluateAsync, , RedisResult. RedisKey, , script , , () . NotImplementedException...

(Sort, SortAsync, SortAndStore SortAndStoreAsync) by get. , : nosort by # get.

, ITransaction.AddCondition, :

internal static class ConditionHelper
{
    public static Condition Rewrite(this Condition outer, Func<RedisKey, RedisKey> rewriteFunc)
    {
        ThrowIf.ArgNull(outer, "outer");
        ThrowIf.ArgNull(rewriteFunc, "rewriteFunc");

        Type conditionType = outer.GetType();
        object inner = FormatterServices.GetUninitializedObject(conditionType);

        foreach (FieldInfo field in conditionType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
        {
            if (field.FieldType == typeof(RedisKey))
            {
                field.SetValue(inner, rewriteFunc((RedisKey)field.GetValue(outer)));
            }
            else
            {
                field.SetValue(inner, field.GetValue(outer));
            }
        }

        return (Condition)inner;
    }
}

:

internal Condition ToInner(Condition outer)
{
    if (outer == null)
    {
        return outer;
    }
    else
    {
        return outer.Rewrite(this.ToInner);
    }
}

ToInner , RedisKey, :

internal RedisKey ToInner(RedisKey outer)
{
    return this.Prefix + outer;
}

:

https://github.com/StackExchange/StackExchange.Redis/pull/92

WithKeyPrefix, , Condition.

+3

. , redis , :

// note: default database is 0
var logdb = muxer.GetDatabase(1);
var userdb = muxer.GetDatabase(2);

StackExchange.Redis - , logdb, 1.

:

  • .
  • KEYS, SCAN, FLUSHDB, RANDOMKEY, SORT ..
  • INFO

:

  • redis-cluster
  • , twemproxy

:

  • - ; IIRC 16 ( 0-15), :

    databases 400 # moar databases!!!
    

, ( ) redis multi-tenancy; 0 "", 1 - "stackoverflow" .. , - node SCAN MIGRATE ( : SCAN, DUMP, PTTL RESTORE - ).

redis-, , , redis , : redis ( ..), , concurrency ( ).


, , ; "" ... , , ( ) redis: , redis (pub/sub). - StackExchange.Redis ChannelPrefix ConfigurationOptions, , PUBLISH . , ChannelPrefix - foo:, bar, foo:bar; : , bar. , , , , : , ISubscriber. , IDatabase.

, . , ...

+3

All Articles