There are many ways of how you can boost performance of Ruby On Rails applications. Approaches might be different and depend on the application structure, size of the database and traffic intensity but a general recommendation can also be given.
In this article we will overview techniques and architectural solutions that will help you to improve performance of your applications.
Use Caching
Rails provides three types of caching mechanisms out of the box which you can start using immediately. These are:
- Page Caching
A cached page served by the webserver without going through the Rails stack. It’s super fast but can’t be applied to every situation.class ProductsController < ActionController caches_page :index def index @products = Products.all end def create expire_page :action => :index end end
The first time user requests /products, Rails will generate a file called products.html which will be passed to the next request by the webserver without invoking Rails.
- Action Caching
It’s similar to Page Caching but the incoming request always goes through the Rails stack. It allows us to use authentication and other restrictions you can’t do with page caching.class ProductsController < ActionController before_filter :authenticate caches_action :index def index @products = Product.all end def create expire_action :action => :index end end
- Fragment Caching
Unfortunately, caching the whole page is seldom possible when you’re developing dynamic web applications. But Rails provides a mechanism called Fragment Caching. It allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.<% Order.find_recent.each do |o| %> <%= o.buyer.name %> bought <% o.product.name %> <% end %> <% cache do %> All available products: <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %> <% end %>
Rails has different stores for the cached data created by action and fragment caches. Page caches are always stored on disk. The default cache stores are MemoryStore, FileStore, DrbStore and Memcached store.
Rails uses the bundled memcached-client gem by default for Memcached store. Since Memcached supports clustering and load balancing it’s a great solution for scaling your application.
Keep in mind that caching always brings more complexity to the application and makes it harder to debug.
Database Optimization
Interacting with database is usually the slowest part of the application. Hopefully, there many things you can do to improve the performance:
- Add all necessary indexes for primary and foreign keys and for fields used in conditions for filtering
- Remove unused or ineffective indexes
- Revise SQL queries and optimize them (use the EXPLAIN command)
- Use eager loading of associations in Rails models
- Don’t use transactions if they are not necessary (for example, in MySQL you can use MyISAM table engine which is much faster than InnoDB)
- Use stored procedures
- Denormalize some tables from 3-d form to 2-nd to avoid redundant joins
- Perform partitioning for large tables
Cutting down the number of SQL queries is one of the many ways to improve the performance of your Rails application, and eager loading is probably the most effective way to do that.
Eager loading comes into play when you need to eliminate “1+N” query problem: if you load N objects from class Article (table “articles”), which has a n-1 relationship to class Author (table “authors”), accessing the author of a given article using the generated accessor methods will cause N additional queries to the database. This, of course, puts some additional load on the database, but more importantly for Rails application server performance, the SQL query statements to be issued will be reconstructed for object accessed.
You can get around this overhead by adding :include => :author to your query parameters like so:
Articles.find(:all, :conditions => ..., :include => :author)
This will avoid all of the above mentioned overhead by issuing a single SQL statement and constructing the author objects immediately. This technique can also be used with other relationship types (such as 1-1, 1-n or n-m).
Scaling Out Your Database
There are two main approaches for addressing scalability through database clustering:
- Database Replication
It’s used to address concurrent access to the same data. Database replication enables us to load-balance the access to the shared data elements among multiple replicated database instances. In this way we can distribute the load across database servers, and maintain performance even if the number of concurrent users increases.

There is a plugin for Rails called Masochism, which provides an easy solution for applications to work in a replicated database environment. It works by replacing the connection object accessed by ActiveRecord models by ConnectionProxy that chooses between master and slave when executing queries. Generally all writes go to master. - Database partitioning/sharding

Database shards/partitions enable the distribution of data on multiple nodes. In other words, each node holds part of the data. This is a better approach for scaling both read and write operations, as well as more efficient use of capacity, as it reduces the volume of data in each database instance.

Use third party solutions such as Apache Lucene/Solr or Sphinx to do full-text search against your database. These are very fast search engines that index data and provide flexible ways of searching it.
Use Load Balancing
It can be good to use load balancing, a technique when incoming requests are distributed to other servers depending on their current load. There are 3 main approaches:
- Use load balancing solutions, e.g. HAProxy which supports a very high number of simultaneous incomming connections at very high speeds.
- Use partial processing on the main server and distribute workload to other servers after the initial processing. It can be done by 3 ways:
- Redirect requests to servers over HTTP by their URLs
- Use messaging systems such as Active MQ, RabbitMQ, MQSeries
- Use PgMQ client for PostgreSQL with other AMQP systems
- Redirect requests to servers over HTTP by their URLs
- Use dedicated servers for content distribution while logic is handled by the main server
- Combine all approaches mentioned above
Front-end optimization
Users spend a lot of time on waiting browser to finish downloading all page components such as images, style sheets, scripts, etc. Reducing the amount of components will minimize the number of HTTP requests which will lead to the faster page loading.
You can achieve this by combining style sheet files and JavaScript files as well as using CSS sprites and image maps.
To combine all CSS and JavaScript files into one in Rails, you can do by using the following commands:
<%= javascript_include_tag :all, :cache => true %> <%= stylesheet_link_tagi :all, :cache => true %>
You can go further and minimize combined files by using a gem called asset_packeger
It can also be a good idea to move images and videos to services like Amazon S3 or even use CDNs (Content Delivery Networks).

You can cache at the client side and use AJAX like Prototype and JQuery to stream in data to the browser on demand.
Yahoo developed a Firefox plug-in called YSlow which gives you tips on how to optimize your page loading.
Conclusion
We briefly looked at some of approaches you can use to improve performance of your Rails applications. These include using caching, optimizing and scaling your database, using load balancing and several techniques for front-end optimization.
A few pieces of advice we would like to add in the end:
- Always measure your application’s performance after steps you take to improve it
- Optimize only what’s slow
- You need to continually test performance
Resources
- Caching with Rails: an overview (http://guides.rubyonrails.org/caching_with_rails.html)
- MySQL Performance Blog (http://www.mysqlperformanceblog.com/)
- Sphinx (http://www.sphinxsearch.com/)
- Apache Lucene (http://lucene.apache.org/java/docs/index.html)
- HAProxy (http://haproxy.1wt.eu/)
- Best Practices for speeding up Your Web Site (http://developer.yahoo.com/performance/rules.html)

This is very clear and helpful. Thank you for these tips.
Thanks for the article it was quite useful for me.
Nicely covered all the options!