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

Sam*_*Sam 2 asp.net-mvc nosql ravendb

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

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

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

Mat*_*int 8

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

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

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

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

所以,在RavenDB中:

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

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

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

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

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

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

在客户对目录的视图中,您希望添加该自定义行.

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

  • 从"添加产品"页面返回"列表产品"页面时,传递刚刚添加的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);
    
    Run Code Online (Sandbox Code Playgroud)
  • 这将确保您只需等待绝对必要的时间,即可获得结果列表中包含的一个产品的更改以及索引中的其他结果.

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

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

  • 购物车将完整地存在于单个文档中.你只需加载它.看来你有一个共同的心理障碍来自于使用关系数据库.RavenDB最适合*结构化*数据.购物车及其订单项或订单及其订单项在一个文档中保存在一起.在DDD术语中,文档代表整个"聚合实体",包括所有非聚合实体和值对象. (3认同)