Rails 6でマルチDBの設定を実運用に乗せてみました

ベトナムから来て、2年間GA technologiesで働いている24歳のアンと申します。RailsエンジニアとしてRENOSYのバックエンドを担当しています。今回仕事に関係があるためRails 6でマルチDBの対応方法を勉強しましたので理解できたものをおまとめさせていただきたいと思います。

1. マルチDBとは

一つのアプリケーションから、複数DBにアクセスしてデータを読み書きをできるようにする仕組みです。

f:id:a_tran:20190624164534p:plain:w400
複数DB画像

2. なぜマルチDBを使うのか

プロジェクトの規模が大きくなって、複数データベースを対応する必要が発生することがあります。主に以下の2つのユースケースがあると思います。

  • 新しいシステムで既存のDBも使いたい
  • スピード改善またはデータ安全性を向上するため、読み取り専用のレプリカを使いたい

今回、開発しているシステムは以下のような構成になっています。

  • 管理者向けのCMSシステムで配信するコンテンツの登録を行います。
  • 一般ユーザー向けのWebアプリケーション(登録されたコンテンツを表示する) が複数あります。

このシステムを実現するために、マルチDBを使用しました。以下のようなシステム構成になります。

f:id:a_tran:20190624165005p:plain:w400
RENOSYでのマルチDBの活用方法

3. なぜ Rails 6

Rails 6から公式にマルチDBに対応するようになりました。その機能を活用すると簡単に実装できます。

Railsガイドも共有です。https://edgeguides.rubyonrails.org/active_record_multiple_databases.html

4. マルチDBを使うための手順

では、マルチDBを使うための手順を、ローカル環境におけるサンプルの構築を例として、下記に説明します

4.1 設定ファイルの編集

仮に今の データベース(config/database.yml)はこんな感じです。

default: &default
  adapter: mysql2

development:
  <<: *default
  database: primary_development

やりたいのは、以下の2つです。

4.1.1 primaryのレプリカを追加
  • 以下の感じでdatabase.ymlファイルを編集します。レプリカの場合replica: trueを指定します。
default: &default
  adapter: mysql2

development:
 # developmentの環境でマルチDB情報を書きます。
 primary:
   <<: *default
   database: primary_development
   migrations_paths: "db/migrate/primary"

 primary_replica:
   <<: *default
   database: primary_development
   # この場合、MasterとSlave DBは同じホストですが、別のポートです。
   port: 3307
   # 違うユーザー名で読み取りのみ権限であるべき。
   username: my_readonly_user
   password: my_readonly_password
   replica: true
  • application_record.rbを更新します
class ApplicationRecord < ActiveRecord::Base
 self.abstract_class = true
 # 接続情報を指定
 connects_to database: { writing: :primary, reading: :primary_replica }
end
4.1.2 secondのデータベース追加
  • database.ymlにsecond DBを追加します
default: &default
  adapter: mysql2
  
development:
  primary:
    <<: *default
    database: primary_development
    migrations_paths: "db/migrate/primary"

  primary_replica:
    <<: *default
    database: primary_development
    # この場合、MasterとSlave DBは同じホストですが、別のポートです。
    port: 3307
    # 違うユーザー名で読み取りのみ権限であるべき。
    username: my_readonly_user
    password: my_readonly_password
    replica: true

  # secondDBの接続情報を書きます。
  second:
    <<: *default
    migrations_paths: "db/migrate/second"
    database: second_development
  
  second_replica:
    <<: *default
    database: second_development
    # この場合、MasterとSlave DBは同じホストですが、別のポートです。
    port: 3307
    # 違うユーザー名で読み取りのみ権限です。
    username: my_readonly_user
    password: my_readonly_password
    replica: true
  • SecondBaseApplicationRecordを作ります。Secondのモデルだったら、このSecondBaseApplicationRecordを継承します。もちろん、secondの接続情報も指定する必要があります
class SecondBaseApplicationRecord < ActiveRecord::Base
 self.abstract_class = true
 # secondの接続情報も指定する
 connects_to database: {reading: :second_replica, writing: :second}
end
4.2 コマンドの実行

Railsのマイグレーションの実行もマルチDBに対応していて、以下のように切り替えたり同時に実行できたりします。

4.2.1 データベースを作る
  • primaryだけデータベースを作る場合: rails db:create:primary
  • secondだけデータベースを作る場合: rails db:create:second
  • 全てのデータベースを作る場合 : rails db:create
4.2.2 モデルを作る

例えば、primaryにUserモデルを作ったら下記のコマンドで実行できます。

rails g model User name:string --database primary

--databaseオプションでデータベースを指定する必要あります。migrateファイルは相当のフォルダーに分けられます。この場合db/migrate/primaryのようになります。Secondデータベースのモデルを作るのも同じようにやりますが、SecondBaseApplicationRecordを継承するように変更忘れないようにご留意ください。

フォルダ構造は画像のようです。

f:id:a_tran:20190621162730p:plain
4.2.3 migrateを実行する

データベースの作成と同じように DBを指定できます

  • primaryだけmigrateする場合: rails db:migrate:primary
  • secondだけmigrateする場合: rails db:migrate:second
  • 全てmigrateする場合 : rails db:migrate

5. Rails 6のマルチDB対応をしてみた感想

Rails 6以前はマルチDBを対応できないわけではないですが、公式にサポートされたので、簡単に実装できました。さらに、下記のPRから推測できるように、今後、公式のドキュメントも整備されることが期待されるので安心で実装できると思います。

Document multiple databases in Rails by eileencodes · Pull Request #36389 · rails/rails · GitHub