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
5
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
580
Featured
See All Featured
Designing for humans not robots
tammielis
254
26k
Are puppies a ranking factor?
jonoalderson
1
2.7k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.9k
Evolving SEO for Evolving Search Engines
ryanjones
0
120
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
1
430
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
760
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
55
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
89
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
640
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
62
49k
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