概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Don't index the kitchen sink!
 - 原文公開日: 2015/07/09
 - 著者: Jeroen Weeink
 - サイト: Crafting Ruby
 
Rails: 闇雲にインデックスを付けてはいけない(翻訳)
最近、使われてないインデックスを外す作業を行ってたのですが、やってみると出るわ出るわ。そのほとんどは、逆関連付けの不要なモデルのJOINの一部でした。次の例で考えてみましょう。
class ShoppingCart < ActiveRecord::Base
  has_many :shopping_cart_products
  has_many :products, through: :shopping_cart_products
end
class ShoppingCartProduct < ActiveRecord::Base
  belongs_to :shopping_cart
  belongs_to :product
end
class Product < ActiveRecord::Base
end
商品を含むショッピングカートとしてはごく普通の設計です。
このShoppingCartProductモデルはRailsのモデルジェネレータで作成されました。このとき、マイグレーションの他に関連付けを含むモデルクラスが作成されました。
$ bin/rails g model CartProduct quantity:integer{1} user:belongs_to product:belongs_to --no-test-framework
class CreateShoppingCartProducts < ActiveRecord::Migration
  def change
    create_table :shopping_cart_products do |t|
      t.integer :quantity, limit: 1, null: false
      t.belongs_to :shopping_cart, index: true, foreign_key: true
      t.belongs_to :product, index: true, foreign_key: true
      t.timestamps null: false
    end
  end
end
適切な処置として、このquantityカラムはnull値を許さないよう設定されていました。このとき、shopping_cart_productsに2つのインデックスがデフォルトで作成されていたことにご注目ください。
実際には、このインデックスはショッピングカートに特定の商品を含むショッピングカートを表示するうえでほとんど意味がありません。つまり、product_idごとにShoppingCartProductのクエリをかける必要などないので、このインデックスは使われていませんでした。Railsが生成するモデルやマイグレーションに、このインデックスが息を殺して潜んでいたのです。
使われてないインデックスは、データベースリソースの無駄使いです。ストレージ容量を消費し、UPDATEやINSERTの速度も落ちます。このときはshopping_cart_productsがそうでした。
使われていないインデックスを取り除くのは簡単です。
class RemoveIndexShoppingCartProductsOnProductId < ActiveRecord::Migration
  def up
    remove_index(:shopping_cart_products, :product_id)
  end
  def down
    add_index(:shopping_cart_products, :product_id)
  end
end
データベースにインデックスを追加するときは、よく考えてからにしましょう。本当に必要であることを確認できてから追加すべきです。
アプリの要件は時とともに変わりますので、そのうちインデックスが必要になるかもしれません。使われてない可能性のあるインデックスを検出するために、データベースツールで定期的に監視しましょう。production環境でも最良の結果を得るためにこの点を監視しておきましょう。