home

Boost Blog

Choosing which database indexes to add

Posted by jeremy on August 20th, 2009

When writing a Rails application, how do you decide on the best indexes to add to your database? It might seem obvious, especially if you work on a project from scratch. The problem is a little harder when you come to optimize an existing codebase.

New Relic

Recently I’ve been using two methods to work out where to put indexes. Firstly I’d strongly recommend using New Relic RPM in development mode. When running your application you can visit /newrelic to get all kinds of useful information. Here you can see the most recent rails calls:

Picture 4

And you can drill down to see the database calls:

Picture 8

And then you can drill down further to see a statement analysis. This will give you information like so:

Query SELECT * FROM `projects` WHERE (`projects`.account_id = 3) ORDER BY position
Select type SIMPLE
Possible Keys
Key
Extra Using where; Using filesort

So this will tell you that there are no keys used, and no possible keys to use. If you now create a migration as follows:

class CreateIndexes < ActiveRecord::Migration
  def self.up
    add_index :projects, :account_id
  end
end

Now run the query again after migrating:

Query SELECT * FROM `projects` WHERE (`projects`.account_id = 3) ORDER BY position
Select type SIMPLE
Possible Keys index_projects_on_account_id
Key index_projects_on_account_id
Extra Using where; Using filesort

But we could do better. Notice that in the extra field it says “Using filesort”. This means that MySQL is doing a normal sort using the row data. If projects are usually selected by account_id and ordered by position then we have a good case for sorting by index. We can’t just add an index on the position column as this won’t be used (try it and you’ll see exactly the same results). Instead we have to add an index that covers both account_id and position:

add_index :projects, [:account_id, :position]

Now looking in RPM again you’ll see things change to:

Query SELECT * FROM `projects` WHERE (`projects`.account_id = 3) ORDER BY position
Select type SIMPLE
Possible Keys index_projects_on_account_id_and_position
Key index_projects_on_account_id_and_position
Extra Using where

Overriding execute

I talked about using two tools to work out where to put indexes. New Relic is great and gives lots of useful information, but I wanted to see what queries were running without indexes as the application was used, in order decide if they required optimizing. I placed the following (ugly) snippet of code into the development or staging environment file:

config.after_initialize do
  unless ActiveRecord::Base.connection.respond_to?(:old_execute)
    ActiveRecord::Base.connection.class.class_eval do
      alias_method :old_execute, :execute
 
      def log_explain(str)
        if str =~ /^SELECT.*WHERE/i
          results = old_execute('EXPLAIN %s' % str)
 
          while row = results.fetch_row
            if row[5].blank?
              File.open(RAILS_ROOT + '/log/sql.log', 'a') do |f|
                f.puts str
                f.puts row.inspect
              end
            end
          end
        end
      end
 
      def execute(*args)
        log_explain(args.first)
        old_execute(*args)
      end
    end
  end
end

This performs a similar task to RPM in that it runs MySQL’s EXPLAIN statement on each SELECT statement that runs. However, I’ve set it up to filter out any statements that are already using keys (if row[5].blank?). Now by tailing the file log/sql.log I’ll be able to see any unindexed queries that are running on the database. I can use this information to quickly run through the whole application to find any problem areas.

What not to index

Most of the database calls from a rails application are going to be quite generic, the same thing over and over, and indexing is good in this situation. Some database calls are going to be a little different – think search, statistical count, etc. You’ll need to think careful if these require indexes or not, considering how often they’re run, and how fast they need to be. Caching can be a good replacement in these areas. Also take into account that indexes slow down database writes, and indexes on string columns are going to be much more intensive that indexes on integer columns.

Load testing

Of course you might not find all problem areas, or you might not be optimizing the correct queries. These methods should give you a good idea, but they’re no replacement for doing good load testing on your application. We’ll be posting more about load testing a rails application in the future.

Tags: mysql, rails

This entry was posted on Thursday, August 20th, 2009 at 6:24 pm and is filed under Development, Ruby on Rails. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

2 Responses to “Choosing which database indexes to add”

  1. Rob says:
    September 29, 2009 at 6:49 pm

    Nice article. Might also want to touch on index selectivity.
    http://www.mysqlperformanceblog.com/2006/06/02/indexes-in-mysql/

  2. jeremy says:
    September 29, 2009 at 10:44 pm

    Hi Rob. Index selectivity is a difficult one because the performance of an index might change as data fills the tables. It’s good to be aware of it though. You can, I think, go way overboard in performance tuning the application and database. These things should be half thought about half reactive, otherwise we’d never get any products actually launched.

Leave a Reply

Click here to cancel reply.

CommentLuv Enabledshow more

  • Categories

    • Agile (3)
    • assessment (2)
    • consumer trends (8)
    • Cool tools (5)
    • Curriculum (2)
    • cybersafety (1)
    • Design (6)
    • Development (14)
    • devices (2)
    • Drupal (1)
    • e-Learning (7)
    • e-learning research network (1)
    • informal learning (5)
    • inquiry (4)
    • key competencies (6)
    • learning communities (3)
    • magic and delight (5)
    • Maori achievement (2)
    • multiliteracies (1)
    • professional learning (6)
    • Publishing (3)
    • Random thoughts (3)
    • research (7)
    • Ruby on Rails (8)
    • Sarah's top ten (11)
    • Social media (7)
    • Social software: practices (5)
    • social software: tools (9)
    • software (4)
    • Software for Learning website (4)
    • student work (7)
    • teacher-learner roles (5)
    • teaching practice (9)
    • the curriculum (6)
    • transformation (10)
    • Usabilty (3)
    • Writing (1)
  • Archives

    • August 2010 (4)
    • July 2010 (6)
    • June 2010 (2)
    • April 2010 (1)
    • March 2010 (1)
    • February 2010 (1)
    • January 2010 (3)
    • December 2009 (1)
    • November 2009 (1)
    • October 2009 (4)
    • September 2009 (2)
    • August 2009 (3)
    • July 2009 (6)
    • June 2009 (3)
    • May 2009 (1)
    • April 2009 (6)
    • March 2009 (6)
    • February 2009 (11)
    • December 2008 (4)
    • November 2008 (6)
    • October 2008 (12)
    • September 2008 (7)
    • August 2008 (7)
    • July 2008 (4)
  • Boost Loves Design

    • I love Typography
    • IntuitionHQ | easy website usability
    • OMG It even has a watermark
    • Follow me on Twitter
    © Boost Limited.
    All rights reserved.
    CONTACT US
    info@boost.co.nz
    tel. (04) 939 0062
    fax. (04) 939 0063

    Level 6, 175 Victoria Street
    PO Box 11504, Wellington
    New Zealand