ServiceStack.Net Redis:存储相关对象与相关对象ID

Zac*_*eth 39 .net c# redis servicestack

我的团队决定通过ServiceStack.net Redis Client与Redis合作,作为我们正在开发的新的高容量网站的底层存储库.我不确定在哪里查找这个问题的文档(对于一般的Redis文档或特定的ServiceStack.Net文档或两者) - 实际上是否有关于如何通过ServiceStack.Net实现Redis的文档的权威来源,包括您需要了解Redis概念和ServiceStack.Net概念,还是需要分别整合两个方面的文档才能全面了解?

我正在努力解决如何在模型的对象图中存储相关对象.这是一个我想要使用的简单场景:

系统中有两个对象:UserFeed.在RDBMS术语中,这两个对象具有一对多关系,即a User具有Feed对象集合,而feed只能属于一个User.Redis将始终通过用户访问Feed,但有时我们会希望通过Feed实例访问用户.

所以我的问题是我们是应该将相关对象存储为属性还是应该存储Id相关对象的值?为了显示:

方法A:

public class User
{
    public User()
    {
        Feeds = new List<Feed>();
    }

    public int Id { get; set; }

    public List<Feed> Feeds { get; set; }

    // Other properties
}

public class Feed
{
    public long Id { get; set; }

    public User User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

方法B:

public class User
{
    public User()
    {
        FeedIds = new List<long>();
    }

    public long Id { get; set; }

    public List<long> FeedIds { get; set; } 

    public List<Feed> GetFeeds()
    {
        return repository.GetFeeds( FeedIds );
    }
}

public class Feed
{
    public long Id { get; set; }

    public long UserId { get; set; }

    public User GetUser()
    {
        return repository.GetUser( UserId );
    }
}
Run Code Online (Sandbox Code Playgroud)

以上哪种方法效果最好?我已经看到了各种示例中使用的两种方法,但我得到的印象是,我看到的一些示例可能不是最佳实践.

一些简单的相关问题:

  • 如果我对某个对象进行了更改,它会自动反映在Redis中还是需要保存?我假设后者,但需要绝对清楚.
  • 如果我(可以)使用方法A,用户对象X的更新是否会在整个对象图中反映在哪里,或者是否需要在图表中保存更改?
  • 通过它的界面存储对象是否有问题(即使用IList<Feed>而不是List<Feed>

很抱歉,如果这些问题有点基础 - 直到2周前我甚至都没有听说过Redis - 更不用说ServiceStack了 - (我的团队中也没有人)所以我们真的从头开始......

myt*_*thz 66

我会列出一些关于Redis + ServiceStack的Redis客户端的一些背景信息,而不是重新散列很多其他文档.

没有魔法 - Redis是一块空白画布

首先,我想指出,使用Redis作为数据存储只提供一个空白画布,并且本身没有任何相关实体的概念.即它只提供对分布式comp-sci数据结构的访问.如何存储关系最终取决于客户端驱动程序(即ServiceStack C#Redis客户端)或应用程序开发人员,使用Redis的原始数据结构操作.由于所有主要数据结构都是在Redis中实现的,因此您基本上可以完全自由地构建和存储数据.

想一想如何在代码中构建关系

因此,考虑如何在Redis中存储内容的最佳方法是完全忽略数据如何存储在RDBMS表中,并考虑它如何存储在代码中,即在内存中使用内置的C#集合类 - Redis使用服务器端数据结构反映行为.

尽管没有相关实体的概念,但Redis的内置SetSortedSet数据结构提供了存储索引的理想方法.例如,Redis的Set集合仅存储一个元素的最多1次.这意味着您可以安全地向其添加项目/键/ ID,而不关心项目是否已经存在,因为如果您将其称为1或100次,最终结果将是相同的 - 即它是幂等的,并且最终只有1个元素仍然存储在集.因此,一个常见的用例是,每次保存模型时,存储对象图(聚合根)都是将子实体ID(也称为外键)存储到Set中.

可视化您的数据

为了更好地可视化实体在Redis中的存储方式,我建议安装Redis Admin UI,它与ServiceStack的C#Redis客户端配合使用,因为它使用下面的密钥命名约定来提供一个很好的分层视图,将您键入的实体分组在一起(尽管所有密钥都是如此)存在于同一个全局密钥空间中).

要查看和编辑实体,请单击" 编辑"链接以查看和修改所选实体的内部JSON表示.希望一旦您能够看到它们的存储方式,您就能够更好地决定如何设计模型.

如何存储POCO /实体

在C#Redis的客户与具有一个主键的任何波苏斯工作-在默认情况下预计Id(虽然这个约定重写与ModelConfig).基本上,POCO作为序列化的JSON存储到Redis中,typeof(Poco).Name并且Id用于形成该实例的唯一键.例如:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'
Run Code Online (Sandbox Code Playgroud)

C#Client中的POCO通常使用ServiceStack的快速Json Serializer进行序列化,其中只有具有公共getter的属性被序列化(并且公共setter被反序列化).

默认值可以使用[DataMember]attrs 覆盖但不建议使用,因为它会使您的POCO更新.

实体是blobbed

因此,要知道Redis中的POCO只是blobbed,您只想将POCO上的非聚合根数据保留为公共属性(除非您有意存储冗余数据).一个很好的约定是使用方法来获取相关数据(因为它不会被序列化),但也告诉你的应用程序哪些方法进行远程调用来读取数据.

因此,关于Feed是否应该与用户一起存储的问题是它是否是非聚合根数据,即您是否想要访问用户上下文之外的用户提要?如果不是,则将List<Feed> Feeds属性保留在User类型上.

维护自定义索引

但是,如果您希望保持所有Feed可以独立访问,redisFeeds.GetById(1)那么您将希望将其存储在用户之外并维护链接2个实体的索引.

正如您已经注意到,存储实体之间关系的方法有很多,您如何这样做主要取决于偏好.对于父>子关系中的子实体,您总是希望将ParentId与子实体一起存储.对于Parent,您可以选择将ChildIds集合与模型一起存储,然后对所有子实体执行单次提取以重新保持模型.

另一种方法是将父dto之外的索引维护在每个父实例的自己的Set中.这样的一些很好的例子是在C#源代码中的Redis的StackOverflow上演示其中的关系Users > Questions,并Users > Answers存储在:

idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]
Run Code Online (Sandbox Code Playgroud)

虽然C#RedisClient确实通过其TParent.StoreRelatedEntities()支持默认的父/子约定,TParent.GetRelatedEntities<TChild>()并且TParent.DeleteRelatedEntities()API支持在场景后面维护索引,如下所示:

ref:Question/Answer:{QuestionId} => [{answerIds},..]
Run Code Online (Sandbox Code Playgroud)

实际上,这些只是您可能的一些选择,其中有许多不同的方法可以实现相同的目标,并且您也可以自由选择.

应该接受NoSQL的无模式,松散类型的自由,并且您不应该担心尝试遵循使用RDBMS时可能熟悉的严格的预定义结构.

总而言之,没有真正正确的方法在Redis中存储数据,例如,C#Redis客户端做了一些假设,以便在POCO周围提供高级API,并在Redis的二进制安全字符串值中填充POCO - 尽管还有其他客户更愿意在Redis Hashes(Dictionaries)中存储实体属性.两者都有效.

  • 是的,这是一个非常好的答案.我非常感谢你花时间给我这个非常全面的答案. (3认同)
  • 感谢@mythz的回复 - 我还没有读过这篇文章(刚刚开始)但是从我看到的有关ServiceStack的其他SO回答中我确信这将是一个很好的答案.当操作系统开发人员参与帮助他们的用户时,请喜欢它. (2认同)