4 ms·
Can you give some concrete examples on the N+1 problem? I’ve heard this before but never experienced it.
by gabereiser 6y ago
Can you give some concrete examples on the N+1 problem? I’ve heard this before but never experienced it.
- dudul 6y agoa query that lets you fetch a list of posts, each post has an author field. So if you ask for these, a naive backend will "resolve" the author for each post individually (so N queries to the DB to fetch 1 author each time) as opposed to a single resolution for the batch of authors.
- tynorf 6y agoI think the classic example is article list with author details. You load the articles (one query), then for each article you load its author (N queries). Naive use of an ORM causes this: articles = Article.load(limit=10) for article in articles: # .author implicitly runs a query author_name = article.author.name This can be avoided in ORMs providing prefetching (e.g. `Article.load(eager_load='author')`).
- baumandm 6y agoImagine you're working on a bespoke blog engine for your company, so you have a GraphQL API that lets you query for all articles or just a single article. Now the product folks want to add comments, so you add a new comments field on your article, with a new field resolver that queries the database for comments like SELECT * FROM comments WHERE article_id = ?. This works great when viewing a single article: the frontend makes a single query to the GraphQL API, which makes 2 database queries: one for the article details and one for the comments But then the product folks want to update the article search page with a expandable comment section for each article, so you can preview the comments for an article. Easy enough, all you have to do is add the comment field to the GraphQL query for the article search page, it's a GraphQL success story! But now when the article search page loads, it runs 1 GraphQL query that retrieves 20 articles, and for each one it runs the SQL query to load comments. So instead of making 2 database queries it's 21. Now we have performance scaling along with the number of results on the page, which is not ideal.
- zachrip 6y agoThe solution for this is to use something like dataloader btw. It essentially waits for all these queries (I believe it uses queueMicrotask under the hood) and batches them. Not unlike other db batching proxies.
- valeness 6y agoWouldn't it be more resource efficient to traverse the graph ahead of time and prefetch all related resources with a single query?
- zachrip 6y agoThis is more or less what dataloader accomplishes. Generally speaking when creating a gql service, graph traversal is not app code but library code.
- zanellato19 6y agoDataloader avoids the N+1 but I wouldn't call it a good solution, you have to twist your app code to make it work. Its an inelegant solution, imo.
- Aeolun 6y agoNot more than you’re already twisting it using resolvers? I’ll admit it will look like magic though.
- valenterry 6y agoThis problem exists in REST APIs in the exact same way though, unless you specifically optimize for this way of querying. But then you can do the same thing with GraphQL, so in the end GraphQL is not worse off, actually rather better because at least the problem exists only between backend and database, not between frontend and backend and database.