What is a N + 1 query?

Mastering N+1 Queries: Understanding and Avoiding a Common Performance Pitfall in Ruby on Rails

Patrick Karsh
4 min readMar 24, 2023

An N+1 query is a database query pattern where an initial query is made to retrieve a collection of objects, and then for each object in that collection, another query is made to retrieve additional related data. This results in N+1 database queries being executed for N objects, plus the initial query, hence the name “N+1 query”.

For example, let’s say you have a database of blog posts and each post has a corresponding author. If you retrieve a collection of 10 blog posts with a single query, and then make another query for each post to retrieve the corresponding author, this would result in 11 queries (1 initial query + 10 additional queries for authors) being executed.

N+1 queries can be inefficient and slow down the performance of your application, especially when dealing with large datasets. To avoid N+1 queries, you can use techniques such as eager loading or joining to retrieve all necessary data with a single query.

An example of an ActiveRecord query that could cause an N+1 query

Let’s say you have two models, User and Post, where a user has many posts, and a post belongs to a user. If you wanted to retrieve all the posts for a given user, you might write the following query:

@user = User.find(params[:id])
@posts = @user.posts

This query retrieves the user with the given id, and then retrieves all the associated posts for that user. However, if you later need to access data from the user object for each post, you might inadvertently cause an N+1 query. For example, if you want to display the username for the author of each post, you might write code like this:

@posts.each do |post|
puts "Author: #{post.user.username}"
end

This code would generate a separate query for each post to retrieve the associated user record, resulting in N+1 queries.

To avoid N+1 queries in this case, you can use eager loading to retrieve the user data along with the posts in a single query. For example, you could modify the original query like this:

@user = User.includes(:posts).find(params[:id])
@posts = @user.posts

This query uses the includes method to eager load the associated posts for the user, ensuring that all the necessary data is retrieved in a single query.

In a Ruby on Rails application how can you check if you have a N + 1 Query?

To use the bullet gem, first add it to your Gemfile:

gem 'bullet'

Then, run bundle install to install the gem and restart your Rails server.

Next, enable bullet by adding the following code to your config/environments/development.rb file:

config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
end

This will enable bullet in your development environment, and configure it to log alerts and output to the console.

Finally, run your application in development mode, and you should see bullet alerts in your logs or console when N+1 queries are detected.

Bullet will also suggest ways to optimize your code to avoid N+1 queries, such as using eager loading or including associated records in your initial query.

How does the Bullet Gem work?

The Bullet gem works by monitoring database queries and detecting cases where an N+1 query is being executed.

When Bullet detects an N+1 query, it raises an alert to notify you of the issue. The alert includes information about the specific query that is causing the problem, as well as details about the associations and objects involved.

In addition to detecting N+1 queries, Bullet can also help you identify other common performance issues, such as unused eager loading, unnecessary counter cache queries, and unnecessary database queries.

To detect N+1 queries, Bullet uses a technique called “query tracing”. Query tracing works by intercepting database queries as they are executed, and examining them to determine if they are part of an N+1 query pattern.

When an N+1 query is detected, Bullet uses reflection to determine the associations involved, and suggests ways to optimize the code to avoid the issue. This might involve using eager loading, including associated records in the initial query, or restructuring the code to avoid the N+1 query pattern altogether.

Overall, the Bullet gem is a powerful tool for identifying and addressing performance issues in Ruby on Rails applications, and can help you optimize your code for maximum speed and efficiency.

What are some alternatives to the Bullet gem for detecting N + 1 queries?

There are several alternatives to the Bullet gem for detecting N+1 queries in Ruby on Rails applications. Here are a few:

Scout APM: Scout APM is a performance monitoring tool that includes N+1 query detection as one of its features. It automatically detects N+1 queries and provides suggestions for how to fix them, along with other performance issues.

Rack-mini-profiler: Rack-mini-profiler is a middleware gem that provides a performance dashboard for Ruby on Rails applications. It includes N+1 query detection as one of its features, along with a range of other performance metrics.

QueryTrack: QueryTrack is a simple gem that logs all SQL queries executed by your application. It includes an option to log N+1 queries, which can be useful for detecting and fixing performance issues.

BulletTrain: BulletTrain is an alternative to the Bullet gem that provides similar functionality. It includes N+1 query detection, as well as other features like unused eager loading detection and the ability to detect inefficient queries.

Skylight: Skylight is a performance monitoring tool that provides detailed insights into the performance of your Ruby on Rails application. It includes N+1 query detection as one of its features, along with other performance metrics and analytics.

Each of these tools has its own strengths and weaknesses, so it’s worth evaluating them carefully to determine which one is the best fit for your specific needs.

--

--

Patrick Karsh
Patrick Karsh

Written by Patrick Karsh

NYC-based Ruby on Rails and Javascript Engineer leveraging AI to explore Engineering. https://linktr.ee/patrickkarsh

No responses yet