Have you ever watched a movie/series or experience your self going to a hotel where they have room service?, let's put that you go to one of these hotels, where the hotel’s restaurant is at the first floor, and you’re hosteling at the 10th floor. Now, when you are there, you decide to order something to eat for lunch, and imaging that the waitress instead of bringing you all the food with the compliments (drinks, desserts, etc.) at once, bring you every meal, drink, dessert and that, one by one, that will be so inefficient, because the waitress will have to do many runs before bringing you everything you asked for, going back and forth from floor 1 to 10. This is what the N+1 problem is, getting everything you request in many runs.
The ideal will be to carry what you ordered in a bussing cart like this, so the waitress can bring all at once:
In this article will see how this problem look in code and some solutions you can do to avoid this and ensure the best performance for your application.
Time to see some code
To show how the N+1 looks in your code, I’m going to build a simple console application that prints the available menus to order from a restaurant. For this, we’re going to have a database with a
drinks table, in the menu, each meal will come with a drink. Let's see the models for these tables:
Now let's see the N+1 in action, here we have some methods to query data from the database (you can find the full code on my repo)
And finally the method with the N+1 query problem
Yes, this simple logic can burn your database, because you are going back and forth to add the drinks to each meal, which is not efficient. The more records you have to query or the most users you have, the most this N+1 problem we’ll affect your application because the time complexity is O(N)/Linear time.
Now let's see two solutions to our problem.
- Join the authors in the SQL query This maybe the easier solution, in here you’ll just have to write a query like the following:
With this query, our code will look the following:
With this query, now we only have you query our database once and that’s it.
- Get the meals and then join the drinks with your programming language No, we won’t do the same as the example where we saw the N+1 problem, here, instead of querying the meals and then querying the drinks one by one, we’ll just do two queries to our database, let see how:
As you can see, we only have two queries to our database:
s.getDrinksByIDsIn, and if you read the
ListMenu method, you’ve noticed that we introduced to more methods, let see what they do and why we need them:
Now, you can see we don't query the database for every drink, instead, in one query we get all the meals, and in the other we query the drinks and then join them to the corresponding meals.
When to use one solution or the other?
Well, in this app, every meal includes just one drink, but what happened if a meal includes more than one drink?, in that scenario, the first solution can't help us, because the SQL query is going to repeat the record for every drink in a meal, so what we want to do, is use the second solution, when we first query the meals, and then get the drinks to join them to the corresponding meals
At work, we have a microservice that is in charge to cache a lot of data about the products we have, twice a day or at demand, and it used to take about 1 minute to cache all the data because of this problem, after we remove the N+1s, it went from 1 minute to 2 seconds!.
Don’t overestimate a simple logic like the N+1’s, you can fall into this problem easily, but also you can fix it easily, but if you don’t do it in time, your application performance will let you know overtime.
Something I didn’t mention is, the N+1 in ORMs like Gorm because I don’t have experience with these, but I will recommend if you are using ORMs, that you dig into the underlying code to see if you have this problem.
Homework If you are working on a project, or you already have projects in production, go check them out to get rid of any N+1 you find.
Did you find this article valuable?
Support Hernan Reyes by becoming a sponsor. Any amount is appreciated!