草庐IT

redis - StackExchange Redis 的分区键空间

coder 2023-07-17 原文

在开发使用 Redis 的组件时,我发现为该组件使用的所有键添加前缀是一种很好的模式,这样它就不会干扰其他组件。

例子:

  • 管理用户的组件可能使用前缀为 user: 的键管理日志的组件可能使用前缀为 log: 的键.

  • 在 Multi-Tenancy 系统中,我希望每个客户在 Redis 中使用单独的 key 空间,以确保他们的数据不会相互干扰。前缀将类似于 customer:<id>:。与特定客户相关的所有 key 。

使用 Redis 对我来说还是新鲜事物。对于这种分区模式,我的第一个想法是为每个分区使用单独的数据库标识符。然而,这似乎不是一个好主意,因为数据库的数量是有限的,而且它似乎是一个即将被弃用的功能。

另一种方法是让每个组件都获得一个 IDatabase实例和一个 RedisKey它应该用来为所有键添加前缀。 (我正在使用 StackExchange.Redis )

我一直在寻找 IDatabase自动为所有键添加前缀的包装器,以便组件可以使用 IDatabase按原样使用界面,而不必担心其键空间。不过我什么也没找到。

所以我的问题是:在 StackExchange Redis 之上使用分区键空间的推荐方法是什么?

我现在正在考虑实现我自己的 IDatabase为所有键添加前缀的包装器。我认为大多数方法只会将它们的调用转发给内部 IDatabase实例。然而,有些方法需要更多的工作:例如 SORTRANDOMKEY .

最佳答案

我创建了一个 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 之前传递之前的论点。

CreateBatchCreateTransaction方法只是为这些接口(interface)创建包装器,但使用相同的基本包装器类(因为大多数包装方法由 IDatabaseAsync 定义)。

KeyRandomAsyncKeyRandom不支持方法。调用将抛出 NotSupportedException .这不是我关心的问题,引用@Marc Gravell:

I can't think of any sane way of achieving that, but I suspect NotSupportedException("RANDOMKEY is not supported when a key-prefix is specified") is entirely reasonable (this isn't a commonly used command anyway)

我还没有实现ScriptEvaluateScriptEvaluateAsync因为我不清楚我应该如何处理 RedisResult返回值。这些方法的输入参数接受 RedisKey这应该是前缀,但脚本本身可以返回键,在这种情况下,我认为取消前缀这些键是(最)有意义的。目前,这些方法将抛出 NotImplementedException。 ...

排序方法( SortSortAsyncSortAndStoreSortAndStoreAsync )对 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并且不再需要用于重写条件的反射 hack,因为新代码可以访问 Condition 的内部结构。类。

关于redis - StackExchange Redis 的分区键空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26031778/

有关redis - StackExchange Redis 的分区键空间的更多相关文章

  1. Ruby rpartition 与分区? - 2

    rpartition和partition有什么区别?我已经阅读了文档,但我认为它们是一样的。只是那些出现在后来的ruby​​版本中吗? 最佳答案 以下示例将有助于识别差异:"abccba".partition("b")#=>["a","b","ccba"]"abccba".rpartition("b")#=>["abcc","b","a"]所以区别在于rpartition搜索最右边的匹配项,而不是最左边的匹配项。 关于Rubyrpartition与分区?,我们在StackOverflow

  2. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  3. Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理 - 2

    文章目录一基础定义二创建逻辑卷2-1准备物理设备2-2创建物理卷2-3创建卷组2-4创建逻辑卷2-5创建文件系统并挂载文件三扩展卷组和缩减卷组3-1准备物理设备3-2创建物理卷3-3扩展卷组3-4查看卷组的详细信息以验证3-5缩减卷组四扩展逻辑卷4-1检查卷组是否有可用的空间4-2扩展逻辑卷4-3扩展文件系统五删除逻辑卷5-1备份数据5-2卸载文件系统5-3删除逻辑卷5-4删除卷组5-5删除物理卷六LVM逻辑卷缩容6-1缩容注意事项6-2标准缩容步骤一基础定义LVM,LogicalVolumeManger,逻辑卷管理,Linux磁盘分区管理的一种机制,建立在硬盘和分区上的一个逻辑层,提高磁盘分

  4. python - 用于从 Python 到 Ruby 查找集合的所有分区的翻译函数 - 2

    我有以下python函数来递归查找集合的所有分区:defpartitions(set_):ifnotset_:yield[]returnforiinxrange(2**len(set_)/2):parts=[set(),set()]foriteminset_:parts[i&1].add(item)i>>=1forbinpartitions(parts[1]):yield[parts[0]]+bforpinpartitions(["a","b","c","d"]):print(p)有人可以帮我把它翻译成ruby​​吗?这是我目前所拥有的:defpartitions(set)ifnots

  5. ruby 认为我在引用一个顶级常量,即使我指定了完整的命名空间 - 2

    在我的应用程序中我有classUserincludeUser::FooendUser::Foo定义在app/models/user/foo.rb现在我正在使用一个定义了自己的Foo类的库。我收到此错误:warning:toplevelconstantFooreferencedbyUser::FooUser仅引用具有完整路径的Foo,User::Foo,而Foo实际上从来没有指的是Foo。这是怎么回事?更新:才想起我之前遇到过同样的问题,在问题1中看到这里:HowdoIrefertoasubmodule's"fullpath"inruby? 最佳答案

  6. Ruby 命名空间与类还是模块? - 2

    考虑Ruby类Foo::Bar。惯例是将“Foo”命名空间作为一个模块,但它也可以很容易地作为一个类:moduleFoo;classBar;end;end对比:classFoo;classBar;end;end在第二种情况下,Bar不是Foo的内部类,它只是在Foo的单例上定义的另一个常量。在这两种情况下,父类(superclass)都是Object并且它们只包含Kernel模块。它们的祖先链是相同的。因此,除了您可以根据其类使用Foo进行的操作(如果是类则实例化,如果是模块则扩展/包含),命名空间的性质是否对有任何影响酒吧?是否有令人信服的理由选择其中一个名称间距而不是另一个?我看到

  7. ruby - 如何在命名空间类中包含模块? - 2

    我在将模块包含在命名空间类中时遇到问题。下面的示例抛出错误uninitializedconstantBar::Foo::Baz(NameError)。我在这里缺少哪些基本的Ruby知识?moduleFoomoduleBazdefhelloputs'hello'endendendmoduleBarclassFooincludeFoo::Bazendendfoo=Bar::Foo.new 最佳答案 使用::强制查找到顶层:moduleBarclassFooinclude::Foo::Bazendend

  8. ruby - 如何为命名空间类定义 Fabricator - 2

    我想为类定义Fabricator,其命名空间类似于“Foo::Bar”。告诉我它的工作方式。这是我的代码。模型/foo.rbclassFooincludeMongoid::Documentembedded_in:foo_container,polymorphic:truefield:xxx....end模型/foo/bar.rbclassFoo::Bar数据/制造商/foo_bar_fabricator.rbFabricator(:foo_bar,class_name:'Foo::Bar')doyyy'MyString'zzz'MyString'end当我尝试在parino控制台上创建

  9. ruby-on-rails - Rails 中带后备功能的动态命名空间 Controller - 2

    我对新的Rails应用程序有一个有点奇怪的要求。我需要构建一个应用程序,其中所有路由都在多个命名空间中定义(让我解释一下)。我想要一个应用程序,其中学校科目(数学、英语等)是namespace:%w[mathenglish].eachdo|subject|namespacesubject.to_symdoresources:studentsendend这很棒而且有效,但它需要我为每个主题创建一个命名空间StudentsController,这意味着如果我添加一个新主题,那么我需要创建一个新Controller。我想创建一个Base::StudentsController,如果Math:

  10. ruby - 在 ruby​​ 中使用索引分区数组 - 2

    我正在寻找一种通过在ruby​​中使用索引来对数组进行分区的优雅方法例如:["a","b",3,"c",5].partition_with_index(2)=>[["a","b",3],["c",5]]到目前为止,我能想到的最好的方法是使用下面的["a","b",3,"c",5].partition.each_with_index{|val,index|index[["a","b",3],["c",5]]还有其他优雅的方法可以实现吗?谢谢! 最佳答案 你可以这样做:["a","b",3,"c",5].partition.with_i

随机推荐