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
4
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
570
Featured
See All Featured
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.2k
Practical Orchestrator
shlominoach
190
11k
Git: the NoSQL Database
bkeepers
PRO
431
66k
Designing for Performance
lara
610
69k
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.2k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
2
74
GitHub's CSS Performance
jonrohan
1032
470k
A designer walks into a library…
pauljervisheath
209
24k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
31
2.7k
Bash Introduction
62gerente
615
210k
Learning to Love Humans: Emotional Interface Design
aarron
274
41k
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