我的团队决定通过ServiceStack.net Redis Client与Redis一起使用,作为我们正在处理的新的高流量网站的基础存储库。我真的不确定在哪里可以找到有关此问题的文档(对于一般的Redis文档还是特定的ServiceStack.Net文档,或两者兼而有之)-实际上是否存在有关如何通过ServiceStack.Net实现Redis的文档的权威来源,其中包括您需要了解有关Redis概念和ServiceStack.Net概念的全部知识,还是我们需要分别集成这两个方面的文档才能获得完整的概览?
我只是在努力如何在模型的对象图中准确存储相关对象。这是我要使用的简单方案:
系统中有两个对象:User和Feed。用RDBMS术语来说,这两个对象具有一对多的关系,即,一个User具有Feed对象的集合,而提要只能属于一个User。Feed始终会通过Redis的用户进行访问,但有时我们希望通过feed实例来访问该用户。
User
Feed
因此,我的问题是我们应该将相关对象存储为属性还是应该存储Id相关对象的值?为了显示:
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; } }
方法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 ); } }
以上哪种方法最有效?我已经在各种示例中看到了这两种方法,但是我得到的印象是,我看到的某些示例可能不是最佳实践。
一些简单的相关问题:
IList<Feed>
List<Feed>
抱歉,如果这些问题有点基本-直到2周前,我什至没有听说过Redis-更不用说ServiceStack了(我的团队中也没有任何人),所以我们真的是从零开始。
与其重新散布大量的其他文档,我不如列出一些有关Redis + ServiceStack的Redis Client的背景信息:
首先,我想指出的是,将Redis用作数据存储仅提供了一个空白画布,并且本身没有任何相关实体的概念。即,它仅提供对分布式comp- sci数据结构的访问。通过使用Redis的原始数据结构操作,如何存储关系最终取决于客户端驱动程序(即ServiceStack C#Redis Client)或应用程序开发人员。由于所有主要数据结构都是在Redis中实现的,因此您基本上可以完全自由地构造和存储数据。
因此,考虑如何在Redis中存储内容的最好方法是完全不考虑数据在RDBMS表中的存储方式,而考虑数据在代码中的存储方式,即在内存中使用内置的C#集合类- Redis通过其服务器端数据结构来反映行为。
尽管没有相关实体的概念,Redis的内置 Set 和 SortedSet 数据结构还是存储索引的理想方法。例如,Redis的 Set 集合最多只能存储1个元素。这意味着您可以安全地向其中添加项目/键/标识,而不必关心该项目是否已经存在,因为如果您将其调用1或100次,最终结果将是相同的- 即它是幂等的,最终仅将1个元素保留在其中集合。因此,在存储对象图(聚合根)时,一个常见的用例是每次保存模型时将子实体ID(又称为外键)存储到Set中。
为了更好地可视化实体在Redis中的存储方式,我建议安装与ServiceStack的C#Redis Client配合使用的Redis Admin UI,因为它使用下面的键命名约定提供了一个很好的层次结构视图,将键入的实体分组在一起(尽管所有键)存在于同一全局键空间中)。
要查看和编辑实体,请单击“ 编辑” 链接以查看和修改所选实体的内部JSON表示形式。希望一旦看到模型的存储方式,您将能够对如何设计模型做出更好的决策。
C#Redis客户端可与具有单个主键的任何POCO一起使用- 默认情况下应为主键Id(尽管此约定可通过ModelConfig覆盖)。本质上,POCO作为序列化JSON存储在Redis中,同时使用typeof(Poco).Name和Id来为该实例形成唯一密钥。例如:
typeof(Poco).Name
urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'
C#客户端中的POCO通常使用ServiceStack的快速Json序列化器进行序列化,在序列化器中,仅对具有公共获取器的属性进行序列化(并通过公共获取器反序列化)。
默认值可以用[DataMember]attrs 覆盖,但不建议使用,因为它会使您的POCO变得丑陋。
[DataMember]
因此,知道Redis中的POCO只是泛滥了,您只想将非聚合的根数据保留在POCO上作为公共属性(除非您有意存储冗余数据)。一个好的约定是使用方法来获取相关数据(因为它不会被序列化),但还告诉您的应用哪些方法进行了远程调用以读取数据。
因此,关于 Feed 是否应与 用户 一起存储的问题是,它是否是非聚合的根数据,即您是否要在用户上下文之外访问用户feed?如果否,则将List<Feed> Feeds属性保留在User类型上。
List<Feed> Feeds
但是,如果您想保持所有提要独立访问,即使用,redisFeeds.GetById(1)则需要将其存储在用户外部并维护一个链接这两个实体的索引。
redisFeeds.GetById(1)
正如您已经注意到的,有很多方法可以存储实体之间的关系,并且如何存储很大程度上取决于偏好。对于 parent > child关系中的子实体,您总是希望将 ParentId 与该子实体一起存储。对于父级,您可以选择与模型一起存储 ChildId 的集合,然后对所有子实体进行一次提取以重新 补充 模型。
另一种方法是为每个父实例在其自己的 Set 中的父dto外部维护索引。这样的一些很好的例子是在C#源代码中的Redis的StackOverflow上演示其中的关系`Users
Questions,并Users > Answers`存储在:
,并
idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc] idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]
虽然C#RedisClient不包括通过其默认的父/子惯例支持TParent.StoreRelatedEntities() ,TParent.GetRelatedEntities<TChild>()以及TParent.DeleteRelatedEntities()API的其中一个指标保持在幕后,看起来像:
TParent.GetRelatedEntities<TChild>()
TParent.DeleteRelatedEntities()
ref:Question/Answer:{QuestionId} => [{answerIds},..]
实际上,这些只是您可能的选择中的一些,在其中有许多种不同的方法可以达到相同的目的,并且您还可以自由滚动自己的目标。
应该拥抱NoSQL的无模式的,松散的自由,并且您不必担心遵循遵循在使用RDBMS时可能熟悉的刚性,预定义的结构。
总之,没有真正 正确的方法 在Redis中存储数据,例如,C#Redis Client进行了一些假设,以提供围绕POCO的高级API,并且在Redis的二进制安全字符串值中使POCO斑点化- 尽管还有其他方法客户将更喜欢将实体属性存储在Redis哈希(字典)中。两者都会起作用。