Tanaka Soft

Railsプロダクトの成長ロードマップ

Rails、ActiveRecord、RDBを使ったプロダクトが成長するとき、どの層をどの順番で拡張するかをL0〜L6で整理します。

Railsプロダクトは、最初から複雑な構成にする必要はありません。一つのアプリケーションと一つのデータベースで始め、利用者、データ量、チーム、機能が増えた段階で構成を広げます。

このロードマップでは、Railsプロダクトの成長をL0〜L6の段階で整理します。段階の境界を厳密なユーザー数やRPSで決めるのではなく、現在のボトルネックと次に必要な構成を考えるために使います。

flowchart LR
    L0[L0 単一アプリ・単一DB] --> L1[L1 WebとJobの拡張]
    L1 --> L2[L2 Rails取得・SQL改善]
    L2 --> L3[L3 DB読み取り改善]
    L3 --> L4[L4 DB分割・Replica]
    L4 --> L5[L5 Partition・Shard]
    L5 --> L6[L6 サービス境界]

L0:単一アプリケーション・単一データベース

Rails、ActiveRecord、単一の主要データベースを中心に開発する段階です。業務概念、ユースケース、テーブル、モデル、画面やAPIの関係を一つのアプリケーション内で把握できます。

業務データとユースケースを整理し、RailsのModel、Controller、Jobを基本構成として使います。RDBの制約、基本的なIndex、ページングを設計し、主要な処理時間とQueryを計測できる状態にします。

L1:Web・Job・Cacheの拡張

Webリクエスト、バックグラウンド処理、Cacheを分け、アプリケーション層を水平に増やす段階です。

  1. Webプロセスを複数化する
  2. Job WorkerをWeb処理から分離する
  3. Cache、Load Balancer、静的ファイル配信を分離する
  4. Web、Job、DBの負荷を個別に観測する

L2:Railsの取得処理とSQLを改善する

画面やAPIの処理が遅くなったときに、ActiveRecordの取得方法とSQLの形を見直す段階です。Railsプロジェクトで最初に調べやすい改善領域でもあります。

ActiveRecordの取得戦略、N+1、JOIN、Rails側メモリの詳しい説明は、Railsの取得戦略を、N+1・JOIN・アプリメモリの負荷から考えるを参照してください。

L2-A:ActiveRecordの取得戦略

N+1を確認し、preload、eager_load、includes、joinsを使い分けます。selectやpluckで不要な列とオブジェクト生成を減らし、ページングで一度に扱うレコード数を抑えます。

L2-B:SQLと画面取得の形

JOIN前に対象範囲を絞り、JOINによる行の増加を確認します。集計、Sort、ページングの処理場所を見直し、重い集計はAjaxやJobへ分けます。変更後はEXPLAINと実測値で結果を確認します。

L3:DB側の読み取りを改善する

Rails側の取得方法とSQLを改善したうえで、DB側の読み取り経路を見直す段階です。候補にはIndex、View、Materialized View、Read Modelがあります。

  1. IndexをQueryの条件、JOIN、Sort、集計と照合する
  2. 複雑なJOINを通常Viewへまとめる
  3. Materialized Viewや集計テーブルで読み取り用の結果を保持する
  4. 検索IndexやRead Modelを用途別に作る

L3では、読み取りの速さだけでなく、更新遅延、Refresh、データ整合性、Indexの書込みコストも確認します。

L4:DBの読み書き分離・機能分割

読み取り量や機能ごとのデータ量が増え、一つのDBに集中する負荷を分ける段階です。

Read Replicaを導入し、Railsの接続先を読み取りと書込みで分けます。必要に応じて機能単位でDBを分け、集計、検索、ログなどを専用基盤へ移します。

L5:Partition・Shard・Pod

一つのDBクラスタで容量、書込み量、IOPS、バックアップ時間を処理しきれなくなり、データを複数の単位へ分ける段階です。

時間や状態によるPartition、Tenant・User・Projectなどを基準にしたShard、アプリケーション・DB・Cache・Jobをまとめて分けるPodを検討します。

L6:サービス境界の分離

独立デプロイ、独立スケール、独立可用性、独立したデータ所有が必要な領域をサービスとして分けます。

検索、ファイル操作、Git操作、決済など、独立性の高い領域から境界を作ります。

このロードマップの使い方

  1. どの処理が遅いのかをユースケース単位で確認する
  2. Web、Job、Rails、SQL、DBのどこが詰まっているかを分ける
  3. 現在の段階のサブ段階から、変更コストに合った施策を選ぶ
  4. 変更後の処理時間、Query数、DB負荷、メモリを再計測する
  5. 改善後のボトルネックに応じて次の段階を選ぶ

このロードマップを使うと、Railsを中心に、必要な層から段階的に拡張できます。