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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Yasuhiro Sugawara
August 21, 2025
9
0
Share
N+1と待ち時間にサヨナラ -- load_ async × strict _ loading でRailsを速くする--
Yasuhiro Sugawara
August 21, 2025
More Decks by Yasuhiro Sugawara
See All by Yasuhiro Sugawara
「Hono遍歴」と「HonoXでブログ作成」
yasu551
0
610
Featured
See All Featured
HDC tutorial
michielstock
1
610
The Cult of Friendly URLs
andyhume
79
6.8k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.4k
The Cost Of JavaScript in 2023
addyosmani
55
9.8k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.1k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
340
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
270
Making Projects Easy
brettharned
120
6.6k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.1k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Between Models and Reality
mayunak
3
260
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
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