データベースレベルでのスケールに対応するため、Railsには複数データベース対応がされている。
<主な機能>
- 複数の「writer」データベースと、それぞれに対応する「replica」データベース
- 作業中のモデルでのコネクション自動切り替え
- HTTP verbや直近の書き込みに応じたwriterとreplicaの自動スワップ
- 複数のデータベースの作成、削除、マイグレーション、各種操作を行うRailsタスク
※replicaのロードバランシングはまだ未対応
データベースへの接続設定について
まずActiveRecord経由でデータベースに接続するため、/config/database.yml
に対して、接続設定を定義する。
# 定義例
# 環境共通の設定
default: &default
adapter: # 接続するDBの種類(sqlite3,mysql2,postgresqlなど)
encoding: # 文字コード
collation: # 照合順序
pool: # 確保する接続プールの個数
timeout: # 接続のタイムアウト時間
socket: # ソケット
host: # ホスト名/IPアドレス
username: # ユーザー名
password: # パスワード
# 開発環境の設定
development:
primary:
<<: *default # defaultの設定の共有
database: # データベース名
# テスト環境の設定
test:
primary:
<<: *default
database:
# 本番環境の設定
production:
primary:
<<: *default
database:
アプリケーションのセットアップ
新しいテーブルを追加するケース
接続情報の定義
animal
という名前の第2のデータベースを追加して、両方のデータベースにそれぞれreplica
を追加した場合の例
production:
primary:
database: my_primary_database
username: root
password: <%= ENV['ROOT_PASSWORD'] %>
adapter: mysql2
primary_replica:
database: my_primary_database
username: root_readonly
password: <%= ENV['ROOT_READONLY_PASSWORD'] %>
adapter: mysql2
replica: true
animals:
database: my_animals_database
username: animals_root
password: <%= ENV['ANIMALS_ROOT_PASSWORD'] %>
adapter: mysql2
migrations_paths: db/animals_migrate
animals_replica:
database: my_animals_database
username: animals_readonly
password: <%= ENV['ANIMALS_READONLY_PASSWORD'] %>
adapter: mysql2
replica: true
複数のデータベースを用いる場合の重要な設定
-
primary
とprimary_replica
のデータベース名は同じにする- 2つは同じデータを持つため
-
writer
とreplica
では異なるデータベースユーザー名を使い、かつreplica
のパーミッションは(writeではなく)readのみにする-
replica: true
というエントリがないと、どちらがreplica
でどちらがwriter
かをRailsが区別できなくなる
-
- 新しい
writer
データベース用のマイグレーションを置くディレクトリをmigrations_paths
キーに設定する
コネクションモデルのセットアップ
- データベースへの接続は抽象クラスで定義する
-
connects_to
メソッドを使用して、接続を定義する
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to database: { writing: :primary, reading: :primary_replica }
end
- ApplicationRecordを別のクラス名に変えている場合は、
primary_abstract_class
を設定する- これにより、RailsはコネクションをどのクラスのActiveRecord::Baseと共有すべきかを認識できる
class PrimaryApplicationRecord < ActiveRecord::Base
primary_abstract_class
connects_to database: { writing: :primary, reading: :primary_replica }
end
- 追加したデータベースへの接続は別の抽象クラスを定義する
class AnimalsRecord < ApplicationRecord
self.abstract_class = true
connects_to database: { writing: :animals, reading: :animals_replica }
end
- 上記のコネクションモデルを継承したモデルクラスにて、それぞれのDBへクエリが発行される
class Car < ApplicationRecord
# primaryデータベースに自動的に話しかける
end
- ポイント
- データベースへの接続を「単一のモデル内」で行うこと
- そのモデルを継承してテーブルを利用すること
- データベースクライアントがオープンできるコネクション数には上限がある。Railsはコネクションを指定する名前に
モデル名
を用いるので、同じデータベースに複数のモデルから接続するとコネクション数が増加する