【Rails】DBのカラムにインデックスを付与する/しない場合での速度比較をしてみた

## はじめに
RailsでDBにインデックスを付与するメリットを体感するために、試験的に1万件のレコードを作成し、**インデックスの有無**で実行速度の比較をしてみました。

今回のケースでは**およそ18%の速度UP**という結果が出ています:point_up:
※サクッと作成するため、DBはRailsのデフォルトである**SQLite3**を使用しました。

> 2020/02/07追記
10万件のレコードだと20倍の差が出ていることを確認し、結果を追記しました。
@error_401 さんありがとうございます。

## 環境
```yaml
OS: macOS Catalina 10.15.3
zsh: 5.7.1
Ruby: 2.6.5
Rails: 6.0.2.1
```

## 事前準備

```shell:rails_newする
$ rails new sampleapp
```

```shell:sampleappディレクトリに移動
$ cd sampleapp
```

```shell:db_createする
$ rails db:create
```

```shell:Postモデルの作成
$ rails g model Post name:string description:string
```

```shell:db_migrateする
$ rails db:migrate
```

```ruby:事前に1万件のデータを登録
irb(main):001:0> 10000.times do |p|
  Post.create(name: "sample#{p}", description: "this is sample!#{p}")
end
```

準備が整ったら、実験開始です！

## 実行するコマンド
nameカラムを検索対象にし、`find_by`メソッドでランダムな名前のレコードを1万回変数`post`に代入するという内容です。

`test.rb`を作成します。

```ruby:sampleapp/test.rb
require 'benchmark'

num_iteration = 10000

Benchmark.bm 10 do |r|
  r.report "no-index" do
    num_iteration.times do
      post = Post.find_by(name: "sample#{rand(1..1000)}")
    end
  end
end
```

## 1.インデックスなしの場合

```shell:rails_consoleで実行
irb(main):001:0> require './test.rb'
```

```shell:インデックスなし
       user     system      total        real
  2.400283   0.353093   2.753376 (  2.840555) # 1回目
  2.456253   0.352958   2.809211 (  2.914810) # 2回目
  2.527288   0.383071   2.910359 (  2.999960) # 3回目
```

測定の対象は**user**の数値にします。

3回の平均はおよそ**2.46秒**でした。


## 2.インデックスありの場合

```shell:ターミナル
$ rails g migration add_index_to_post
```

```ruby:XXXXXXXXXXXXXXXX_add_index_to_post.rb
class AddIndexToPost < ActiveRecord::Migration[6.0]
  def up
    add_index :posts, :name
  end
  def down
    remove_index :posts, :name
  end
end
```

```shell:ターミナル
$ rails db:migrate
```

これでインデックスが`name`カラムに付与されたので、効果測定です。

再度`test.rb`を実行します。

```shell:rails_consoleで実行
irb(main):001:0> require './test.rb'
```

```shell:レコードあり
       user     system      total        real
  2.016621   0.349745   2.366366 (  2.455184) # 1回目
  2.037209   0.333974   2.371183 (  2.465041) # 2回目
  2.039167   0.327804   2.366971 (  2.471504) # 3回目
```

3回の平均はおよそ**2.03秒**！

## 結論：およそ18%の速度UP
今回の単純なケースでは、**およそ18%**検索速度が上がりました！

もちろん`test.rb`の内容によって異なることもあると思いますが、指標の一つとして使えるのではないかと思います。

以上です！

## 2020/02/07追記　レコード10万件の場合
@error_401 さんよりコメントを頂き、早速レコードを増やした場合にどうなるか検証してみました！

### 検索条件の変化
- レコード数：**10万件**
- 検索条件：`name`カラムの検索条件を`sample1`〜`sample100000`へ変更。（回数は1万回で変更なし）

### 結果：インデックスなし
```shell:インデックスなし
       user     system      total        real
  39.969663  14.484736  54.454399 ( 54.855391) # 1回目
  40.167824  14.695771  54.863595 ( 55.215927) # 2回目
  40.057479  14.542640  54.600119 ( 54.955958) # 3回目
```
3回の平均はおよそ**40.05秒**

### 結果：インデックスあり
```shell:インデックスあり
       user     system      total        real
  2.007433   0.391915   2.399348 (  2.546632) # 1回目
  1.989761   0.376209   2.365970 (  2.450146) # 2回目
  2.029753   0.386575   2.416328 (  2.566732) # 3回目
```
3回の平均はおよそ**2.00秒**！

10万件のレコード数だとなんと**20倍**速度が早くなりました！

ビックリしたのは、インデックスありだと**１万件のときとほとんど数字が変わらない**こと。
これはすごい:dizzy_face:

## おわりに
最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

## 参考にさせて頂いたサイト（いつもありがとうございます）
- [データベースにindexを張る方法 - Qiita](https://qiita.com/seiya1121/items/fb074d727c6f40a55f22)
- [Ruby でベンチマークを取る方法 - Qiita](https://qiita.com/scivola/items/c5b2aeaf7d67a9ef310a)