Rails and How to Index Your DB Tables

Posted by Chris Blackburn Thu, 06 Nov 2008 22:48:00 GMT

Between the time your migration files get generated and deploying to production with your new application you will likely need to add indexes to one or more of your tables. How do you know which tables need indexes and which fields in those tables to index?

Here are some tips to help figure out where the likely bottlenecks will occur with regard to your database tables:

1. Always index *_id foreign_key fields. Let’s say you have 2 tables, users and addresses. You may have migration file code in ./db/migrate that looks like this:

  ...
  def self.up
    create_table "users", :force => true do |t|
      t.column :login, :email             :string
    end
  end
  ...
  ...
  def self.up
    create_table "addresses", :force => true do |t|
      t.column :user_id       :integer
      t.column :address       :string
    end
  end
  ...

You will always want to index the foreign_key user_id, with few exceptions. So simply add the add_index line like this:

  ...
  def self.up
    create_table "addresses", :force => true do |t|
      t.column :user_id       :integer
      t.column :address       :string
    end
    add_index "addresses", :user_id
  end
  ...

This will facilitate much faster associations when you lookup related models like this view code:

  <%=h user.address.address %>

  # OR as the case may be...

  <%=h address.user.email %>

2. As a rule of thumb index any column for which you will be using to lookup a row or set of rows. For example, if you will be doing something like this:

  u = User.find_by_email('test@example.com')

... then you will want to index the email field.

3. Run explain plans on your associations to determine where indexes would benefit the queries. Now this one is more tricky unless you know about the query_analyzer plugin. This nifty little tool will automatically dump out explain plans into your logfiles in development mode that look like this example from the README file:

# development.log

P Load (0.008669)
 => SELECT p.* FROM p INNER JOIN d ON p.id = d.p_id WHERE (d.p_id = 2 AND ((d.type = 'P')))

Analyzing P Load

select_type | key_len | type   | Extra       | id | possible_keys | rows | table  | ref    | key
 ----------------------------------------------------------------------------------------------------
SIMPLE      |         | ALL    | Using where | 1  |               | 74   |    d   |        |
SIMPLE      | 4       | eq_ref | Using where | 1  | PRIMARY       | 1    |    p   | d.p_id | PRIMARY

Now what this tells you is that the table d is missing an index because MySQL is having to look at “ALL” the records. Whenever you see a type of ALL and/or possible_keys is empty it should be a red flag telling you to index something.

If we had followed tips 1 and 2 this explain plan would be much different, showing reference types of ref and eq_ref which is typically a good thing.

For more information see the README file here: http://agilewebdevelopment.com/plugins/query_analyzer.

Posted in  | Tags , , , , , ,  | 2 comments | 1 trackback

Hack to fix "undefined method" error when running rake on a gem not yet installed

Posted by Chris Blackburn Thu, 03 Jul 2008 21:15:00 GMT

Recently, while trying to install the ultrasphinx gem for one of our projects, I ran into this little bonus:

(in /Users/cblackburn/Source/ruby/bols)
These gems that this application depends on are missing:
 - ultrasphinx
Run "rake gems:install" to install them.
rake aborted!
undefined method `validates_email_address' for #<Class:0x1f577f0>
/Users/cblackburn/Source/ruby/bols/rakefile:11
(See full trace by running task with --trace)

As usual I won’t bore you with why it happens other than to say the loader doesn’t work properly in this instance. To hack around it find out which models are using the ‘undefined method’ and place the following inside the class definition, before using the method.

# Hack to fix "undefined method" error when running rake on a gem not yet installed
require File.join(RAILS_ROOT, 'config', 'initializers', 'custom_validations')

Comment if you need more help or if this is not clear.

Posted in  | Tags , , , , , , , ,  | no comments | no trackbacks

The status_update Plugin

Posted by Chris Blackburn Wed, 25 Jun 2008 17:41:00 GMT

I’ll be publishing the status_update plugin for Rails in the coming weeks. The plugin is an interface to Ping.fm, the cool new service that allows you to update all of your social network statuses with a single API.

Coming Soon…

Posted in  | Tags , , , , ,  | no comments | no trackbacks

State Selector From Collection or State Model

Posted by Chris Blackburn Thu, 19 Jun 2008 01:01:00 GMT

schema.rb for states table
create_table "states", :force => true do |t|
  t.string   "name"
  t.string   "abbreviation", :limit => 2
  t.datetime "created_at"
  t.datetime "updated_at"
end
cities_controller.rb
# GET /cities/new
# GET /cities/new.xml
def new
  @city = City.new
  @states = State.find(:all, :order => :abbreviation)

  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render :xml => @city }
  end
end
new.html.erb view for cities_controller.rb
<% form_for(@city) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :state_id %><br />
    <%= f.collection_select(:state_id, @states, "id", "abbreviation") %>
  </p>
  <p>
    <%= f.submit "Create" %>
  </p>
<% end %>

Posted in  | Tags , , , , , ,  | no comments | no trackbacks

Older posts: 1 2 3 ... 5