ユーザーの大幅な増加に向けた Ruby on Rails のスケーリング
今日は、Ruby on Rails アプリケーションを巨大なユーザー ベースに拡張するために使用できるいくつかの戦略について詳しく説明します。
アプリケーションをスケーリングする明白な方法の 1 つは、より多くの資金を投入することです。そして、それは驚くほどうまく機能します。さらにいくつかのサーバーを追加し、データベースサーバーをアップグレードすると、ほら、 パフォーマンスの問題の多くがうんざりするだけです。 !
ただし、多くの場合、サーバーを追加せずにアプリケーションを拡張することも可能です。それが今日私たちが議論することです。
さあ、始めましょう!
Rails アプリケーションに AppSignal を使用する
スケーリングとパフォーマンスの最適化に入る前に、 まずどうかを特定する必要があります。 これを行う必要があるか、アプリケーションのボトルネックは何か、どのリソースを拡張できるかなどを確認します。
これを行う簡単な方法の 1 つは、AppSignal の Ruby のパフォーマンス モニタリングとメトリクスを使用することです。
パフォーマンス ダッシュボードは、平均して遅いコントローラ アクションとバックグラウンド ジョブを正確に特定するのに役立ちます。
たとえば、パフォーマンス ダッシュボードで ActiveRecord がどのように表示されるかを次に示します。

これは、サーバーを追加する場合でも、コードを通じてパフォーマンスを最適化する場合でも、あらゆるスケーリングの良い出発点となります。
次に、Rails アプリを拡張するために使用できる最も簡単なテクニックの 1 つであるキャッシュに移りましょう。
Ruby on Rails でのキャッシュ
キャッシュを使用すると、同じことを何度も計算するのをやめることができます。
たとえば、ソーシャル メディア プラットフォームを運営していて、非常に人気のある投稿があるとします。キャッシュを使用すると、各ユーザーの投稿のレンダリングに費やしたすべての CPU サイクルをすぐに取り戻すことができます。これは、キャッシュが役立つことの一部にすぎません。
キャッシュできるすべてのリソースを見てみましょう。
ビューのキャッシュ
ビューのレンダリングは、特にビューにレンダリングするデータが大量にある場合、コストのかかる操作になることがあります。操作にコストがかからない場合でも、同じビューを 100 万回レンダリングするよりも、事前レンダリングされたビューを使用すると、パフォーマンスが向上します。
Rails は、cache を使用してこれをすぐにサポートします。 ビューヘルパー。たとえば、リストをレンダリングするときに各投稿をキャッシュする方法は次のとおりです。
このため、Rails は、テンプレートの HTML コンテンツ、投稿 ID、更新タイムスタンプに応じて、特定のキーの下に各投稿を自動的にキャッシュします。
この手法の詳細については、「Rails のフラグメント キャッシュ」および「Rails コレクション キャッシュ」の投稿をご覧ください。
ただし、留意すべき点の 1 つは、キャッシュ キーにはネストされたテンプレートのコンテンツが含まれていないということです。したがって、キャッシュ呼び出しを 1 レベルより深くネストしている場合、結果が古い可能性があります。これについて詳しくは、Rails でのロシアン ドールのキャッシュを参照してください。
応答のキャッシュ
ビュー/フラグメントのキャッシュに加えて、GET の完全な応答をキャッシュすることも選択できます。 リクエスト。これは、If-None-Match を通じてサポートされます。 と If-Modified-Since ブラウザによって送信されるヘッダー。
If-None-Match の場合 ヘッダーがリクエストに存在する場合、サーバーは 304 Not Modified を返すことができます。 応答に変更がない場合、コンテンツのない応答。サーバーが計算した Etag ヘッダー内の値と比較されます。
同様に、If-Modified-Since の場合、 ヘッダーが If-None-Match なしで存在します。 、サーバーは 304 Not Modified を返すことができます。 内容のない応答 (応答がその日付以降変更されていない限り)。
Rails は、コントローラーのアクション内でこれを行う簡単な方法を提供します。単純に次のように書くことができます。
Rails は、キャッシュをサポートし、受信ヘッダーを処理し、データが変更されていない場合は 304 で応答するために必要なヘッダーをすべて送信します。状況が変わらない限り、サーバーは完全なビューのレンダリングを再度スキップできます。この戦略の高度な構成の詳細については、「Rails のクライアント側キャッシュ:条件付き GET リクエスト」を参照してください。
値のキャッシュ
最後に、生の値 (キャッシュ ストアにシリアル化できるもの) をキャッシュすることもできます。これは通常、リソースを大量に消費する操作や低速な操作の結果をキャッシュし、再度実行することを避けるのに役立ちます。
このキャッシュから恩恵を受ける値を特定することはアプリケーションに大きく依存しますが、通常は最も遅いイベントを確認することで正しい方向を示すことができます。
最後に、何をキャッシュするかを特定する場合、Rails がそのために提供する API は非常に簡単に使用できます。
上記のコードは perform_the_slow_computation になります。 一度だけ、cache_key_with_version の下に値をキャッシュします。 鍵。次回同じコードが呼び出されるとき、Rails はまずキャッシュされた値があるかどうかを確認し、perform_the_slow_computation をトリガーする代わりにそれを使用します。
このキャッシュ戦略の最も重要な部分は、値の計算に使用されるすべての入力に応じて適切なキャッシュ キーを計算することです。これは、古い値を使い続けないようにするためです。
キャッシュ ストア
何をキャッシュするか、そして Rails がキャッシュに保存するために提供する技術がわかったので、次の当然の疑問は、このデータをどこにキャッシュするかということです。 Rails には、いくつかの組み込みキャッシュ ストア アダプターが付属しています。運用ユースケースで最も人気のあるキャッシュ ストアは、Redis と Memcached です。ファイル ストアとメモリ ストアなど、他にもいくつかのオプションがあります。これらのストアの詳細については、「Rails の組み込みキャッシュ ストア:概要」の記事を参照してください。
ファイル ストアとメモリ ストアは、開発を迅速に開始して実行するために最適です。ただし、通常、特に複数のサーバーを使用した分散セットアップで作業している場合は、運用には適していません。 Redis と memcached はどちらも本番環境での使用に適しています。通常、どれを使用するかはアプリケーションによって異なります。
Ruby on Rails のバックグラウンド ワーカー
ほとんどのアプリケーションでは、メーラー、定期的なクリーンアップ、またはユーザーの立ち会いを必要としないその他の時間のかかる操作のためのバックグラウンド ジョブが必要です。おそらく、すでにバックグラウンド ワーカーがセットアップされていると思われます。
コントローラー アクション内で実行するのに 1 秒以上かかることに気付いた場合は、代わりにそれをバックグラウンド ワーカーに移動できるかどうかを確認してください。これは、大きなテーブル内のデータの検索などのユーザー向けの操作から、大量のデータを取り込む API メソッドまで多岐にわたります。
実装例
カスタム ジョブを実行するために、Rails は Active Job フレームワークを提供します。これを使用して、非常に複雑なフィルタリング ロジックをバックグラウンド ジョブに移動する方法を見てみましょう。まず、バックグラウンド ジョブを作成しましょう。
このジョブは次のようにコントローラから実行できます。
ジョブがデータを計算して結果を提供するのを待つ間に、テンプレートに読み込みインジケーターをレンダリングする必要があります。
しかし、仕事の結果をビューに反映するにはどうすればよいでしょうか?ターボを使用すると、これが非常に簡単になります。たとえば、ビュー内で、turbo_stream_from を使用して特定の通知チャネルでターボ ストリーム イベントをサブスクライブできます。 .
これを使用して、テンプレートを作成しましょう。
最初のコントローラー アクションではデータが定義されていないため、読み込みインジケーターのみをレンダリングします。ここで、ジョブの結果を提供しましょう。
ここで重要な部分は notify_completed です。 方法。 Turbo::StreamsChannel を使用し、置換イベントを [user, :huge_datasets] にブロードキャストします。 ビューからサブスクライブした通知ストリーム。
複雑な操作をコントローラーからバックグラウンド ジョブに移行するために必要なのはこれだけです。タスクをバックグラウンドに移動する主な利点は、バックグラウンド ワーカーを Web サーバーから独立して拡張できることです。これにより、Web サーバー側のリソースが大幅に解放されます。また、ユーザーにとって、このようなインターフェースは迅速に応答し、段階的に結果を提供できるため、応答性がはるかに高く感じられます。
注意 :バックグラウンド ジョブ ワーカーのどちらを選択するかについてサポートが必要な場合は、「遅延ジョブと Sidekiq:どちらが優れていますか?」 をお読みください。
Ruby on Rails アプリケーションでのデータベースのスケーリング
この投稿で説明する最後のスケーラブルなリソースはデータベースです。データベースは、ほとんどのアプリケーションの中核を形成します。データとそのデータにアクセスするサーバーの数が増加すると、データベースに負荷がかかり始めます。
データベースを拡張する最も簡単な方法は、データベース サーバーに処理能力とメモリを追加することです。 Web サーバーのスケーリングとは対照的に、データベースでこれを行うと、特に大容量のストレージがある場合、通常、操作が非常に遅くなります。
データベースを拡張する 2 番目のオプションは、複数のデータベースを使用するか、データベースをシャーディングすることによって水平に拡張することです。詳細については、「アクティブ レコードを含む複数のデータベース」を参照してください。
代わりに、PostgreSQL を見てデータベースのパフォーマンスを最適化することに重点を置きます。
PostgreSQL で時間のかかるクエリを見つける
まず、最も時間のかかるクエリを特定する必要があります。これを行う方法は、pg_stat_statements をクエリすることです。 サーバー上で実行されたすべての SQL ステートメントに関する統計を含むテーブル。実行時間が最も長い上位 100 のクエリを見つける方法を見てみましょう。
これにより、クエリ、呼び出し数、およびこれらのクエリの平均実行時間が返されます。もっと速くなりそうなものを見つけて、なぜ遅かったのか分析してみてください。
EXPLAIN を実行することもできます。 または EXPLAIN ANALYZE クエリでクエリ プランと実際の実行の詳細をそれぞれ確認します。
結果で注目すべき最も重要なことの 1 つは Seq Scan です。 これは、Postgres がクエリを実行するためにすべてのレコードを順番に処理する必要があることを示します。この問題が発生した場合は、フィルタリングした列にインデックスを追加して、順次スキャンをバイパスしてみてください。
最も連続したスキャンが行われたテーブル
私が実行したいもう 1 つの便利なクエリは、テーブルに対して実行される連続スキャンの合計数を見つけることです。
この結果から、非常に大きなテーブル (行数が多い) と高いカウント値が見つかった場合は、問題があります。そのテーブルに対してすべてのクエリをチェックし、順次スキャンを実行できるクエリを見つけて、インデックスを追加してそれを要約してみてください。
インデックスの使用法
次のクエリを実行すると、インデックスの使用状況に関する統計を見つけることもできます。
これにより、各テーブルのインデックス使用率が返されます。数値が低い場合は、そのテーブルにいくつかのインデックスが欠落していることを意味します。
まとめ
この投稿では、キャッシュやバックグラウンド ワーカーなど、Ruby on Rails アプリケーションをスケーリングするためのいくつかの戦略を検討しました。また、PostgreSQL データベースのパフォーマンスの最適化についても検討しました。
Rails を使用すると、アプリケーションにパフォーマンスを最適化する複数のレイヤーを非常に簡単に追加できます。
スケーラビリティに関して最も重要な考慮事項は、アプリケーションのボトルネックに対処する前にそれを特定することです。優れたパフォーマンス監視ツールが役に立ちます。必要な場合は、AppSignal for Ruby をチェックしてください。
コーディングを楽しんでください!
追記Ruby Magic の投稿を報道後すぐに読みたい場合は、Ruby Magic ニュースレターを購読して、投稿を 1 つも見逃さないようにしてください。
-
AppSignal 3.4.1 によるシームレスな Rails 例外レポート
アプリケーションの例外についての洞察が必要だが、コードに冗長なロジックを追加することに投資する時間がない場合は、 いくつかの例外的なものを用意しています。 ニュース:AppSignal for Ruby gem 3.4.1 が Rails エラー レポーターをサポートするようになりました! この変更により、AppSignal の統合を簡素化し、コードを合理化し、エラー ページを表示してユーザー フローを中断することなく、その場でエラーの報告を開始できるようになります。 このブログ投稿では、Rails Error Reporter を使用して AppSignal にエラーの報告を開始する方法
-
Ruby on Rails でアクション ポリシーをマスターする:安全な認可のための実践ガイド
アプリを安全に保つには、アプリにアクセスできるユーザーと内容を制御する必要があります。アクセス制御は、「誰に」アクセスを許可する認証と、「何に」アクセスできる認可に分類できます。 認証については別の日に取り上げますが、ユーザーの承認に関しては、通常、ロールベースの戦略を使用するか、リソースベースの戦略を使用するかの 2 つの方法があります。 この 2 部構成のシリーズでは、Ruby on Rails ブログ アプリケーションでの Action Policy gem の使用について詳しく説明します。 このパートでは、アクション ポリシーの基本について説明します。 始めましょう! 前提条件