Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
N+1と待ち時間にサヨナラ -- load_ async × strict _ loading...
Search
Yasuhiro Sugawara
August 21, 2025
0
2
N+1と待ち時間にサヨナラ -- load_ async × strict _ loading でRailsを速くする--
Yasuhiro Sugawara
August 21, 2025
Tweet
Share
More Decks by Yasuhiro Sugawara
See All by Yasuhiro Sugawara
「Hono遍歴」と「HonoXでブログ作成」
yasu551
0
560
Featured
See All Featured
Building Adaptive Systems
keathley
43
2.7k
Docker and Python
trallard
46
3.6k
[RailsConf 2023] Rails as a piece of cake
palkan
57
5.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
332
24k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
A Tale of Four Properties
chriscoyier
160
23k
Making Projects Easy
brettharned
117
6.4k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Being A Developer After 40
akosma
90
590k
Practical Orchestrator
shlominoach
190
11k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.5k
Transcript
N+1と待ち時間にサヨナラ load_async × strict_loading でRailsを速くする 1
自己紹介 名前: 菅原康滉 X: @yasu551_ 仕事: ソニックガーデン Rails 趣味: 登山、読書(漫画、SF小説(プロジェクト・ヘイル・メアリー))
2
N+1と待ち時間にサヨナラ load_async × strict_loading でRailsを速くする 3
遅い画面の正体 ダッシュボードのレスポンス 1.6秒 並列化できるクエリが 直列で2本 実行 テンプレートで N+1 発生 攻めるべきは
並列化 と N+1封じ 4
並列化できるクエリが 直列で2本 実行 # app/controllers/dashboards_controller.rb def show @posts = Post.published
# SQLは未実行 @authors = Author.where(featured: true) # SQLは未実行 end <!-- app/views/dashboards/show.html.erb --> <!-- @postsのSQLが遅延実行 --> <% @posts.each do |post|> <%= post.content %> <% end %> <!-- 上の処理が終わった後に、@authorsのSQLが遅延実行 --> <!-- 上の処理が終わる前に、@authorsのSQLを実行すれば処理全体にかかる時間を削減可能 --> <% @authors.each do |author|> <%= author.name %> <% end %> 5
load_async でクエリを非同期実行して、並列化 # app/controllers/dashboards_controller.rb def show @posts = Post.published.load_async #
SQLが非同期実行 @authors = Author.where(featured: true).load_async # SQLが非同期実行 # 結果的に、@postsと@authorsの両方のSQLが並列化 end <!-- app/views/dashboards/show.html.erb --> <!-- SQL結果が未取得なら待機し、取得済みなら以下を実行 --> <% @posts.each do |post|> <%= post.content %> <% end %> <!-- SQL結果が未取得なら待機し、取得済みなら以下を実行 --> <% @authors.each do |author|> <%= author.name %> <% end %> 6
設定とログ確認 config.active_record.async_query_executor = :global_thread_pool # or multi_thread_pool tail -f log/development.log
| grep ASYNC ASYNC タグが出れば非同期実行 7
strict_loading でN+1を検出して止める class Article < ActiveRecord has_one :author end #
irb articles = Article.all.strict_loading articles.map { it.name } => ActiveRecord::StrictLoadingViolationError articles = Article.all.includes(:author).strict_loading articles.map { it.name } => ['Alice', 'Bob'] 注意: includes なしで多発 / 全体ONは段階的に 8
適用モードの段階拡大 1. 局所オン Article.all.strict_loading 2. モデル単位 class Article < ActiveRecord
self.strict_loading_by_default = true end 3. 全体適用 config.active_record.strict_loading_by_default = true config.active_record.action_on_strict_loading_violation = :log 9
効果の定量化 p95応答時間: ある期間に来た全リクエストのうち、95%がそれ以下で返っている 応答時間 p95応答時間が850msだったら、95%のリクエストは850ms以内に完了。遅 い5%だけが850ms超 DB待ち(p95): アプリのスレッドがActiveRecordの接続プールからコネクションを 取得できるまで待った時間 DB待ち(p95)が120msだったら、コネクション取得の95%が120ms以内に完
了。遅い5%だけ120ms超 N+1検出数/分: 1分あたりにN+1が起きた回数 load_async の効果は、p95応答時間、DB待ち(p95)を見ればわかる strict_loading の効果は、N+1検出数/分でわかる 10
まとめ 重い画面に load_async N+1多発領域に strict_loading 小さく入れて → ログ確認 → 段階拡大
コードを少し変えるだけで済むので簡単に試せていい 11