Rails開発者向けのPostgresのヒント

4月にフェニックスのRailsConfで、PostgresをRailsで使用するための多くのヒントについて議論し、それらを記録してより多くの聴衆と共有することが役立つと考えました。 ここでは、Railsアプリケーションのデータベースパフォーマンスのデバッグと改善に関するいくつかを紹介します。







タイムアウトで長いリクエストを管理する



長いクエリは、データベースにあらゆる種類の悪影響を与える可能性があります。 リクエストが数時間またはほんの数秒間機能するかどうかに関係なく、ロックを保持したり、WALを圧倒したり、または単に大量のシステムリソースを消費したりできます。 Postgresは、リクエストのタイムアウトを設定することで、より大きな安定性を簡単に実現します。 次の例に示すように、デフォルト値(5秒など)を設定すると便利です。5秒より長く続くリクエストはすべて強制終了されます:







production: url: <%= DATABASE_URL %> variables: statement_timeout: 5000
      
      





セッション内でリクエストをより長く実行したい場合は、現在の接続にのみ有効なタイムアウトを設定できます。







 class MyAnalyticsJob < ActiveJob::Base queue_as :analytics def perform ActiveRecord::Base.connection.executeSET statement_timeout = 600000” # 10  # ... ensure ActiveRecord::Base.connection.execute “SET statement_timeout = 5000” # 5  end end
      
      





「悪い」クエリを検索する



Railsは、データベースとやり取りするときに多くのことを抽象化します。 良いことも悪いこともあります。 Postgres自体で長いリクエストを追跡できますが、Railsアプリケーションが成長するにつれて、これだけでは不十分な場合があります。 リクエストの発信元を見つけるために、リクエストの発信元をログに記録する非常に便利なmarginalia gemがあります。 これで、間違った要求、遅すぎる、または単に削除できる要求が表示された場合、コード内でこれを修正する場所を正確に知ることができます。







 Account Load (0.3ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`queenbee_id` = 1234567890 LIMIT 1 /*application:BCX,controller:project_imports,action:show*/
      
      





コメントへの注意-約。







データベースクエリを使用した状況の概要



多くの場合、データベースで行われていることの概要が必要です。 pg_stat_statementsは、Citus Cloudなどのクラウド環境にプリインストールされることが多いPostgres拡張機能です。 最後の統計情報のリセット以降に実行された要求とその動作を確認できます。







たとえば、実行時間が最も長い10個のクエリとその平均時間を表示するには、次の手順を実行します。







 SELECT query, total_time / calls AS avg_time FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10;
      
      





Postgres統計コレクターで「track_io_timing」を有効にすると、ボトルネックが何であるかを理解できます-プロセッサーまたはI / O pg_stat_statementsの詳細については、 こちら (またはハブに関する別の okmeter.ioの記事 -約Per。)を参照してください







高度な機能を使用する



デフォルトでは、Railsは「schema.rb」ファイルを使用して、通常はテストを実行する前にデータベースを初期化するために使用されるデータベーススキーマのコピーを保存します。 残念ながら、機能インデックスや部分インデックス、複合主キーなどの多くの高度なPostgres機能は、このDSLでは表現できません。







代わりに、Railsによって生成および使用されるdb / structure.sqlファイルに切り替えることは理にかなっています。これは次のように実行できます。







 # Use SQL instead of Active Record's schema dumper when creating the database. # This is necessary if your schema can't be completely dumped by the schema dumper, # like if you have constraints or database-specific column types config.active_record.schema_format = :sql
      
      





内部では、Postgres形式の「pg_dump」が使用されます。これは詳細すぎる場合がありますが、完全に復元されたデータベース構造を保証します。 diffが長すぎるという問題に遭遇した場合は、 activerecord-clean-db-structureご覧ください。







複雑なトランザクションが互いにブロックしないようにしてください



Railsは、特にbefore_saveフックとモデル間のマルチレベルリレーションシップを使用する場合、すべてをトランザクションに入れることを好みます。 スケーリングの問題を引き起こす可能性のあるトランザクションについて留意すべき重要な注意事項が1つあります。 たとえば、次のようなトランザクションでは:







 BEGIN; UPDATE organizations SET updated_at = '2017-04-27 11:31:03 -0700' WHERE id = 123; SELECT * FROM products WHERE store_id = 456; ---   statement'  COMMIT;
      
      





最初のUPDATEステートメントは、最初からトランザクションCOMMITまで、ID「123」のストリング「organizations」のロックを保持します。







たとえば、別のユーザーからの同じ「組織」に別のリクエストを送信し、同様のトランザクションを実行します。 原則として、この他のリクエストは実行するために、最初のトランザクションのコミットを待つ必要があり、応答時間が長くなります。 これを修正するには、UPDATEが最後に近づくようにトランザクションの順序を変更し、メイン作業が完了した後、トランザクション外のタイムスタンプフィールドへの更新をグループ化することも検討します。







このような問題を事前に検出するには、PostgreSQLで「log_lock_waits = on」を設定します。







データベース接続を監視する



Railsはデフォルトでデータベース接続プールをサポートしています。 新しい要求が到着すると、プールから1つの接続を取得し、それをアプリケーションに渡します。 Railsアプリケーションの規模が拡大すると、何百ものオープンデータベース接続が発生する可能性がありますが、実際にはほんの一部だけが作業を行います。 これの鍵は、接続マネージャーを使用して、たとえばpgBouncerなどのアクティブなデータベース接続を減らすことです。 接続マネージャは、トランザクションがアクティブなときに接続を開き、作業を行わないアイドル接続をスキップしません。







翻訳者から-また、PostgresやpgBouncerへの接続数などを追跡する監視機能を使用して、問題の発生を防止および解決することができます。








All Articles