mongoid 中的 N+1 问题
我正在使用 Mongoid 在 Rails 中使用 MongoDB。
我正在寻找类似活动记录 include
的内容。目前我在 mongoid orm 中找不到这样的方法。
任何人都知道如何在 mongoid 或 mongmapper 中解决这个问题,这被认为是另一个不错的选择。
I'm using Mongoid to work with MongoDB in Rails.
What I'm looking for is something like active record include
. Currently I failed to find such method in mongoid orm.
Anybody know how to solve this problem in mongoid or perhaps in mongomapper, which is known as another good alternative.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
现在一段时间过去了,Mongoid 确实增加了对此的支持。请参阅此处的“预加载”部分:
http://docs.mongodb.org/ecosystem/tutorial /ruby-mongoid-tutorial/#eager-loading
我想指出:
:include
不执行连接。
看起来 @amrnt 发布的链接已合并到 Mongoid 中。
Now that some time has passed, Mongoid has indeed added support for this. See the "Eager Loading" section here:
http://docs.mongodb.org/ecosystem/tutorial/ruby-mongoid-tutorial/#eager-loading
I'd like to point out:
:include
does not do a join.
Looks like the link that @amrnt posted was merged into Mongoid.
更新:自从我发布这个答案以来已经两年了,事情已经发生了变化。有关详细信息,请参阅 tybro0103 的回答。
旧答案
根据两个驱动程序的文档,它们都不支持您正在寻找的内容。可能是因为它不能解决任何问题。
ActiveRecord 的
:include
功能解决了 SQL 数据库 的 N+1 问题。通过告诉 ActiveRecord 要包含哪些相关表,它可以使用JOIN
语句构建单个 SQL 查询。无论您要查询的表数量如何,这都将导致一次数据库调用。MongoDB 只允许您一次查询一个集合。它不支持诸如
JOIN
之类的东西。因此,即使您可以告诉 Mongoid 它必须包含哪些其他集合,它仍然必须为每个附加集合执行单独的查询。Update: it's been two years since I posted this answer and things have changed. See tybro0103's answer for details.
Old Answer
Based on the documentation of both drivers, neither of them supports what you're looking for. Probably because it wouldn't solve anything.
The
:include
functionality of ActiveRecord solves the N+1 problem for SQL databases. By telling ActiveRecord which related tables to include, it can build a single SQL query, by usingJOIN
statements. This will result in a single database call, regardless of the amount of tables you want to query.MongoDB only allows you to query a single collection at a time. It doesn't support anything like a
JOIN
. So even if you could tell Mongoid which other collections it has to include, it would still have to perform a separate query for each additional collection.尽管其他答案是正确的,但在当前版本的 Mongoid 中,includes 方法是实现所需结果的最佳方法。在以前的版本中,包含不可用,我找到了一种摆脱 n+1 问题的方法,并认为值得一提。
就我而言,这是一个 n+2 问题。
控制器操作:
此 as_json 响应会导致 Judge 记录出现 n+2 查询问题。在我的例子中,给开发服务器的响应时间为:
在 816 毫秒内完成 200 OK(视图:785.2 毫秒)
解决此问题的关键是在单个查询中加载用户和照片,而不是每位法官 1 比 1。
您可以利用 Mongoid IdentityMap Mongoid 2 和 Mongoid 3 支持此功能。
首先在 mongoid.yml 配置文件中打开身份映射:
现在更改控制器操作以手动加载用户和照片。注意:Mongoid::Relation 记录将延迟评估查询,因此您必须调用 to_a 来实际查询记录并将它们存储在 IdentityMap 中。
这导致总共只有 3 个查询。 1 名评委,1 名用户,1 名照片。
在 559 毫秒内完成 200 OK(观看次数:87.7 毫秒)
这是如何工作的?什么是 IdentityMap?
IdentityMap 有助于跟踪已加载的对象或记录。因此,如果您获取第一个用户记录,IdentityMap 将存储它。然后,如果您尝试再次获取同一用户,Mongoid 会在再次查询数据库之前查询该用户的 IdentityMap。这将在数据库上节省 1 个查询。
因此,通过加载所有用户和照片,我们知道在手动查询中需要法官 json,我们将数据一次性预加载到 IdentityMap 中。然后,当法官需要用户和照片时,它会检查 IdentityMap,而不需要查询数据库。
Although the other answers are correct, in current versions of Mongoid the includes method is the best way to achieve the desired results. In previous versions where includes was not available I have found a way to get rid of the n+1 issue and thought it was worth mentioning.
In my case it was an n+2 issue.
controller action:
This as_json response results in an n+2 query issue from the Judge record. in my case giving the dev server a response time of:
Completed 200 OK in 816ms (Views: 785.2ms)
The key to solving this issue is to load the Users and the Photos in a single query instead of 1 by 1 per Judge.
You can do this utilizing Mongoids IdentityMap Mongoid 2 and Mongoid 3 support this feature.
First turn on the identity map in the mongoid.yml configuration file:
Now change the controller action to manually load the users and photos. Note: The Mongoid::Relation record will lazily evaluate the query so you must call to_a to actually query the records and have them stored in the IdentityMap.
This results in only 3 queries total. 1 for the Judges, 1 for the Users and 1 for the Photos.
Completed 200 OK in 559ms (Views: 87.7ms)
How does this work? What's an IdentityMap?
An IdentityMap helps to keep track of what objects or records have already been loaded. So if you fetch the first User record the IdentityMap will store it. Then if you attempt to fetch the same User again Mongoid queries the IdentityMap for the User before it queries the Database again. This will save 1 query on the database.
So by loading all of the Users and Photos we know we are going to want for the Judges json in manual queries we pre-load the data into the IdentityMap all at once. Then when the Judge requires it's User and Photo it checks the IdentityMap and does not need to query the database.
ActiveRecord
:include
通常不会执行完全连接来填充 Ruby 对象。它执行两次调用。首先获取父对象(例如帖子),然后第二次调用以拉取相关对象(属于帖子的评论)。Mongoid 对于引用关联的工作方式本质上是相同的。
在控制器中,您获得帖子:
在您的视图中,您迭代评论:
Mongoid 将在集合中找到该帖子。当您点击评论迭代器时,它会执行单个查询来获取评论。 Mongoid 将查询包装在游标中,因此它是一个真正的迭代器,并且不会使内存过载。
Mongoid 延迟加载所有查询以默认允许此行为。
:include
标签是不必要的。ActiveRecord
:include
typically doesn't do a full join to populate Ruby objects. It does two calls. First to get the parent object (say a Post) then a second call to pull the related objects (comments that belong to the Post).Mongoid works essentially the same way for referenced associations.
In the controller you get the post:
In your view you iterate over the comments:
Mongoid will find the post in the collection. When you hit the comments iterator it does a single query to get the comments. Mongoid wraps the query in a cursor so it is a true iterator and doesn't overload the memory.
Mongoid lazy loads all queries to allow this behavior by default. The
:include
tag is unnecessary.这可能会有所帮助 https://github.com/flyerhzm/mongoid-eager-loading
This could help https://github.com/flyerhzm/mongoid-eager-loading
你需要更新你的模式来避免这种 N+1,MongoDB 中没有解决方案来进行一些联合操作。
You need update your schema to avoid this N+1 there are no solution in MongoDB to do some jointure.
将详细记录/文档嵌入主记录/文档中。
Embed the detail records/documents in the master record/document.
就我而言,我没有整个集合,但有一个导致 n+1 的对象(项目符号是这么说的)。
没有在下面写导致 n+1 的内容,而是
因此,我
写了这并没有造成问题,但让我思考是否重复自己或检查 n+1 对于 mongoid 来说是不必要的。
In my case I didn't have the whole collection but an object of it that caused n+1 (bullet says that).
So rather than writing below which causes n+1
I wrote
That didn't cause a problem but left me thinking if I repeated myself or checking n+1 is unnecessary for mongoid.