【问题标题】:RavenDB data model efficient scalability design choicesRavenDB 数据模型高效可扩展性设计选择
【发布时间】:2013-01-28 18:35:28
【问题描述】:

我在一个正在开发中的项目上使用 RavenDB,因此还没有用户。在这个项目之前,我的背景一直是关系数据库,但总的来说我更喜欢 NoSQL 方法。但是,我还没有任何工作或管理构建在 NoSQL 数据库之上的网站的经验,该数据库的流量很大。我开始了解 Map/Reduce 索引,并在我的解决方案中包含了一些索引,但我想知道:

关于何时创建 Map/Reduce 索引以及何时不创建,我是否应该遵循任何设计经验法则?

我知道这非常依赖于我系统中的业务对象以及它们之间的交互方式。我想我只是在努力了解我可能进行的哪些查询应该使用索引,以及我可以简单地直接查询对象。

以下是我的部分业务领域以及我已经创建索引的位置的快速概览:

我的系统主要由品牌和消费者组成。每个人都有许多社交媒体帐户。当用户通过他们的社交媒体帐户登录时,我有索引BrandsBySocialAccountConsumersBySocialAccount,它们将这些集合展平并将它们与品牌或消费者的UserId 相关联。一旦我有了UserId,我就可以检索相关的品牌或消费者记录,然后我就走了。

一个品牌可以制作许多广告系列。我这里有另一个索引,CampaignsByBrand。还需要跟踪消费者如何与活动交互,因此活动可以有许多跟踪条目,用于他们可以与活动执行的不同交互。例如,他们可以从外部跟踪到活动页面的链接,也可以从网站本身中发现一个。正如我解释的那样,我在这里需要索引似乎很清楚。每次交互都有一个索引(ClickLinkTrackingEntriesByCampaignViewDetailsTrackingEntriesByCampaign),或者一个包含交互的索引(TrackingEntriesByCampaign)。多个索引在这里过分吗?可能是。目前有 4 种交互类型,以后可能还会介绍其他类型。当我有几条记录时,这些查询非常快。但是当有数十万甚至数百万条记录时,它们仍然会尽可能快吗?

从整体设计来看,似乎对于每个具有可能需要由该集合上的属性查询的集合属性的对象,我应该创建 Map/Reduce 索引。这是一个很好的经验法则吗?还有其他的吗?“如果你有这些类型的对象交互,你应该考虑创建这些类型的索引”

【问题讨论】:

    标签: database-design mapreduce scalability ravendb


    【解决方案1】:

    首先,请务必查看static indexes 上的文档(如果您还没有)。

    你需要清楚的要点是:

    1. 直接从文档存储中检索文档不需要需要索引,应尽可能使用。这是使用以下任何方法完成的:
      • session.Load()
      • session.Advanced.LoadStartingWith()
      • documentStore.DatabaseCommands.Get()

    2. 每当您使用session.Query()session.Advanced.LuceneQuery() 进行查询时,您总是使用索引。如果您未指定静态索引索引,则会为您创建 dynamic index。在许多情况下,创建动态索引所涉及的延迟并不理想 - 因此,将动态索引替换为静态索引通常是一个好主意。

    3. 您拥有的索引越多,服务器必须完成的工作就越多,您将消耗的存储空间也就越多。因此,您将希望尽可能合并索引。很多时候,同一个索引可以用于多种用途。您应该谨慎地制作索引 - 不要让它们太窄而无用,也不要让它们变得过于宽泛和昂贵。

      假设我有一个对象,有时需要通过字段 A 查询,其他时候通过字段 B 查询。当然,我可以创建两个不同的索引,但这会很浪费。拥有一个同时映射AB 字段的索引会更有效。现在两个不同的查询可以由同一个索引提供服务。我敦促您尽可能合并索引。

      一个典型的示例是映射文档中的每个字段并为所有字段打开字段存储,只是因为您认为您可能希望在某个时候从索引中投影它们。在大多数情况下,您不需要走这么远。有几个地方是合适的,但你会非常谨慎地这样做。

    4. 所有索引都有一个Map,但我们不称它们为“map/reduce”索引,直到它们也有一个Reduce部分。您将创建的大多数索引不会是 map/reduce 索引。

      Map/Reduce 索引几乎总是保留用于某种类型的聚合计算。例如,您的域中可能有 SocialAccountsCountByBrand 的 m/r 索引,或者在销售域中您可能有更复杂的索引,例如 TopCustomersByTotalSalesPerMonth

    5. 我不同意您的评估,即如果对象具有集合属性,则它需要对该集合的索引。在许多情况下,您的域中的其他地方会有类似的数据可以用于相同的目的。当然,具体情况会根据您想要做什么而有所不同。但总的来说,如果您发现您正在创建大量此类索引 - 将这些数据重构到自己的文档中可能会更好。

      例如,如果我有一个像下面这样的类:

      (故意的坏例子 - 不要真的这样做)

      public class Customer
      {
          public string Id { get; set; }
          public string Name { get; set; }
          public List<Order> Orders { get; set; }
      }
      

      很明显,如果每个订单都嵌入到 Customer 对象中,我会非常频繁地查询该集合。将每个Order 放入自己的文档中,并通过CustomerId 引用返回给客户,我会很多得到更好的服务。

    6. 最后,尽量避免根据您希望结果的形状来考虑索引。相反,请根据您要查询的内容来考虑它们。换句话说,您希望在查询中的WhereOrderBySearch 子句中指定哪些字段?

      当然,有诸如live projectionsTransformResults 之类的技术 - 但同样,应该谨慎使用这些技术。既然我们有更强大的功能,比如indexing related documents,我们几乎可以反对所有的转换需求。一些次要的索引投影可能很有用,但通常您可以在自己的代码中操纵结果并让乌鸦远离它。仅当您实际需要结果中的索引数据时才使用预测。如果您需要的所有数据都在文档中,则无需投影。

      之所以提出这一点,是因为我见过很多人根据 UI 中的 ViewModel 来设计索引。这很糟糕,因为它要求为 UI 问题制作索引。相反,应该考虑结果本身的形状。如果它具有回答查询的所有信息,那么它可以用于多种方式 - 包括但不限于 UI。

    我希望这能回答您的问题。如果您有其他人,请在 cmets 中回复。谢谢。

    【讨论】:

    • 这是一个奇妙而有见地的答案,它为我解决了很多问题。谢谢马特
    猜你喜欢
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    相关资源
    最近更新 更多