概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: 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環境でも最良の結果を得るためにこの点を監視しておきましょう。