How the N+1 query problem can burn your database

How the N+1 query problem can burn your database

Find out how a simple logic may be burning out your database.

Overview

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:

image.png 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 meals and drinks table, in the menu, each meal will come with a drink. Let's see the models for these tables:

Restaurant Tables

Restaurant Models

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) Waitress usecase

And finally the method with the N+1 query problem N 1 problem code

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.

Solution

Now let's see two solutions to our problem.

  1. 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:

image.png

With this query, our code will look the following: Go sql solution

With this query, now we only have you query our database once and that’s it.

  1. 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:

image.png As you can see, we only have two queries to our database: s.getMeals() and 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:

image.png 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

Personal experience

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!.

Conclusion

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.

References

  1. N+1 in Laravel: Here you can see the N+1 in Eloquent (an ORM for Laravel)
  2. Figma Jam and IconDuck: To make the illustrations
  3. Article repository: You can find the examples in my GitHub

Did you find this article valuable?

Support Hernan Reyes by becoming a sponsor. Any amount is appreciated!