RavenDB - 如何在许多插入的生产中使用?

时间:2013-09-12 16:40:23

标签: asp.net-mvc nosql ravendb

在听说NoSQL几年后,我终于开始在.Net MVC应用程序(简单博客)中使用RavenDB。启动并运行嵌入式数据库非常快速且轻松。

但是,我发现在将对象插入文档存储区后,当后续页面刷新时,它们并不总是存在。当我刷新页面时,它们会显示出来。我在某处读到这是由于陈旧的索引。

我的问题是,你应该如何在一直有插件发生的网站上使用它(例如:电子商务)。这不会导致陈旧的索引和不可靠的查询结果吗?

2 个答案:

答案 0 :(得分:8)

想想像SQL Server这样的传统数据库实际发生了什么。

  • 从表中创建,更新或删除项目时,还必须更新与表关联的所有索引。
  • 您在表上的索引越多,写入操作就越慢。
  • 如果在现有表上创建新索引,则在完全构建之前不会使用它。如果没有其他索引可以回答查询,则会发生慢速表扫描。
  • 如果其他人在修改现有索引时尝试从现有索引进行查询,则读者将阻止直到修改完成,因为C onsistency要求优先级高于A vailability。
  • 这通常会导致读取速度慢,超时和死锁。

NoSQL的“最终一致性”概念旨在缓解这些问题。通过优先A可用性高于C onsistency,优化读取。 RavenDB在这方面并不是唯一的,但它有点特别之处在于它仍然具有一致性。如果您要检索单个文档,例如查看订单或查看其配置文件的最终用户,则这些操作符合ACID,并且不受“最终一致性”设计的影响。

要了解“最终一致性”,请考虑一下查看网站上产品列表的典型用户。与此同时,贵公司的销售人员正在修改目录,添加新产品,更改价格等。有人可能会说,列表与这些变化完全一致可能并不是非常重要。毕竟,几秒前访问该站点的用户无论如何都会收到数据而不进行任何更改。最重要的是快速提供产品结果。由于正在进行写入而阻止查询意味着对客户的响应时间较慢,从而导致您的网站体验较差,并且可能导致销售损失。

所以,在RavenDB中:

  • 针对文档商店进行写入。
  • 单个Load操作直接转到文档商店
  • 索引商店
  • 进行查询
  • 在编写文档时,数据正在从文档存储区复制到索引存储区,用于那些已定义的索引。
  • 在查询索引的任何时候,无论后台进行的复制状态如何,您都将获得该索引中已有的内容。这就是有时索引“陈旧”的原因。
  • 如果您在未指定索引的情况下进行查询,并且Raven需要一个新索引来回答您的查询,它将立即开始构建索引并立即返回某些这些结果。它只会阻塞足够长的时间来为您提供一页结果。然后它继续在后台构建索引,以便下次查询时您将获得更多数据。

现在让我们举一个例子来说明这种方法的不足之处。

  • 销售人员会转到按字母顺序排序的“产品列表”页面。
  • 在第一页上,他们看到“苹果”目前尚未售出。
  • 所以他们点击“添加产品”,然后转到他们输入“Apples”的新页面。
  • 然后他们返回到“产品列表”页面,他们仍然看不到任何苹果,因为索引是陈旧的。 WTF - 对吗?

解决这个问题需要理解并非所有数据查看者都应该被认为是平等的。那个特定的销售人员可能会要求看到新添加的产品,但客户不会以同样的紧急程度了解或关心它。

因此,在销售人员正在查看的“产品列表”页面上,您可能会执行以下操作:

var results = session.Query<Product>()
                     .Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
                     .OrderBy(x=> x.Name)
                     .Skip((pageNumber-1) * pageSize).Take(pageSize);

在客户对目录的视图中,您想要添加该自定义行。

如果你想要超精确,你可以使用稍微优化的策略:

  • 从“添加产品”页面返回“列表产品”页面时,请传递刚刚添加的ProductID。
  • 在您查询该页面之前,如果传入了ProductID,则将您的查询代码更改为:

    var product = session.Load(productId);
    var etag = session.Advanced.GetEtagFor(product);
    
    var results = session.Query<Product>()
                     .Customize(x => x.WaitForNonStaleResultsAsOf(etag))
                     .OrderBy(x=> x.Name)
                     .Skip((pageNumber-1) * pageSize).Take(pageSize);
    
  • 这将确保您只需等待绝对必要的时间,即可获得结果列表中包含的一个产品的更改以及索引中的其他结果。

  • 您可以通过向后传递etag而不是ProductId来稍微优化它,但这可能不再可以从应用程序中的其他位置重复使用。

但请记住,如果列表按字母顺序排序,并且我们添加了“Plums”而不是“Apples”,那么您可能无法立即看到这些结果。当用户跳过包含该产品的页面时,它可能已经存在。

答案 1 :(得分:3)

您正在遇到过时的查询。 这是RavenDB的设计部分。您需要区分查询(BASE)和按ID(ACID)加载。