Slide 1

Slide 1 text

出張!Railsウォッチ
 in 銀座Rails#23
 森 雅智 / @morimorihoge 1
 特集:~ActiveRecordのSELECTベンチマーク~


Slide 2

Slide 2 text

About Me
 ● 森 雅智: @morimorihoge
 ● BPS株式会社でRailsの受託開発チームをやってたり、週1大学非常勤で Web開発を教えてたりします
 ● Ruby/Rails歴は11年くらい。Web開発は17年くらい
 ● 銀座Ralis #10でActiveRecordでVIEWを使おうという話をしました
 About BPS & TechRacho
 ● Web受託開発や電子書籍製品開発をやっている会社です
 ● TechRachoという自社技術Blogを運営しています
 ○ 3年半ほど前から平日毎日更新してます
 ○ https://techracho.bpsinc.jp/ ● お仕事相談、転職相談、TechRachoへのご意見など気軽にどうぞ
 ○ https://www.bpsinc.jp/ 2


Slide 3

Slide 3 text

Railsウォッチとは?
 技術ブログTechRachoで毎週連載しているRails / Ruby界隈を中 心とした雑多な情報を提供する技術雑談マガジン
 3


Slide 4

Slide 4 text

これまでの出張Railsウォッチのピックアップテーマ
 ● Rails6新機能特集
 ○ 銀座Rails#12: 複数DB対応
 ○ 銀座Rails#13: ActionText、Trix
 ○ 銀座Rails#14: ActionMailbox
 ● Railsアプリケーション開発に関する雑多なテーマ
 ○ 銀座Rails#15: production、development、staging環境について
 ○ 銀座Rails#16: 機能開発の設計レビューについて
 ○ 銀座Rails#17: リソース管理スコープについて
 ● 開発一般な話
 ○ 銀座Rails#19: 開発チームの冗長化について
 ○ 銀座Rails#20: Excelと仲良くしよう
 ○ 銀座Rails#21: 標準仕様を読むためのABNF
 ○ 銀座Rails#22: CLIプログラムにOptionParserを使う
 4
 ※過去のスライドはTechRachoにて公開しています。「TechRacho 銀座Rails」あたりで 検索するとヒットしますので興味のある方はどうぞ


Slide 5

Slide 5 text

ActiveRecordのSELECTベンチマーク
 5


Slide 6

Slide 6 text

話のきっかけ
 ● 以前やたらと遅いActiveRecordの#each処理を#selectでSELECTするカラムを絞り 込んだら高速化できた、という話をふと思い出した
 ● 当時は試行錯誤の中フィーリングで試した高速化施策の一つだったが、ちゃんと調 べてみようと思い立った
 ○ Railsのベンチマークをきちんとやってみようという気持ち 
 ○ フィーリングではなく定量的に計測してみることで高速化方法に根拠を持てるようになる 
 6
 本発表で使った動作環境
 ● Hyper-V上のManjaro(Arch Linux系)環境
 ● ruby 2.7.1p83(rbenv)
 ● Rails 6.0.3.1
 ● PostgreSQL 12.3(Docker Container、同一VM上)


Slide 7

Slide 7 text

Railsコードのベンチマーク方法について
 ● Rails Guidesの「Ruby on Rails に貢献する方法」の項でbenchmark-ips gemにつ いて触れられている。
 ● 今回はメモリ使用量についても計測したかったため、benchmark-memory gemも 併用した
 7
 https://railsguides.jp/contributing_to_ruby_on_rails.html#%E3%83%99%E3%83%B3%E3%83%81%E3%83%9E %E3%83%BC%E3%82%AF%E3%82%92%E8%A1%8C%E3%81%86


Slide 8

Slide 8 text

実際のベンチマーク方法
 ● Rails Guidesから参照されているテンプレートを以下のように改変し、rails runner経 由で呼び出すことで動かす
 ○ Rails一式が読み込まれる分起動が遅いが、その分既存アプリのModelなども全部読み込まれるの でアプリケーションベンチマークとしては使いやすい(私見です) 
 8


Slide 9

Slide 9 text

ActiveRecordの発行するSELECT文について
 ● ActiveRecordはデフォルトでSELECT *なSQLを発行することが知られている
 
 
 ● 特定のカラムのみ取得したければActiveRecord#selectを使えば良い
 
 
 ● #pluck などはSELECT *しない実装になっている(ので早い)
 9


Slide 10

Slide 10 text

なぜSELECT *したくないのか
 10
 仮説1
 SELECT結果が大きいとデータサイズが大き くなる分転送に時間がかかるはず 
 仮説2
 ARオブジェクトを作る際、カラムが多ければ よりメモリやCPUを消費しているはず 
 ※今回のベンチマークではRDB(PostgreSQL)は同一VM上のDocker Containerで実行しているため、仮 説1の差は有意に出ない可能性あり 


Slide 11

Slide 11 text

検証準備
 ● 以下のようなusersテーブルを作成(極端なケース)
 ○ 20個のintegerカラム 
 ○ 20個のvarchar(string)カラム 
 ○ 20個のtextカラム
 ● 作成したusersテーブルのデータをやりくりしつつベンチマークを取っていく
 11


Slide 12

Slide 12 text

#selectありなしで有意差はあるかを確認
 ● 1000件でeach&カラムを一つ参照してみて計測
 12
 SELECTすると3.8倍高速 
 SELECTすると1/7程度のメモリ消費 
 ※極端なケースでテストしているので鵜呑みにし過ぎないように注意 


Slide 13

Slide 13 text

#selectするカラム数でどの程度変化するか?(1)
 ● 1000件でeach&カラムを参照してみて計測
 13
 (以下略)
 ※生データだと分かりにくいので次ページチャートへ


Slide 14

Slide 14 text

#selectするカラム数でどの程度変化するか?(2)
 14
 x: #selectしたカラム数
 y: instruction/sec(大きければ高速)


Slide 15

Slide 15 text

#selectするカラム数でどの程度変化するか?(3)
 15
 x: #selectしたカラム数
 y: memsize(kb)


Slide 16

Slide 16 text

メンバ参照の有無による差はあるか?(1)
 #each内でselectしたカラムを参照するかどうかでlazy load度合いが変わる気がしたので 計測してみた。
 ※使わないカラムをSELECTしても参照しなければそこまで悪影響がないかもしれない 仮説
 16


Slide 17

Slide 17 text

メンバ参照の有無による差はあるか?(2)
 17
 x: #selectしたカラム数


Slide 18

Slide 18 text

さらなる検証の余地
 ● integer以外の型について、どの程度メモリ使用量に差異が出るのか?(varcharや text型などの可変データはどうなる?)
 ● integer以外のRubyオブジェクトに変換するのが重そうな型の場合、さらに実行速度 に有意な差が出るかもしれない(JSONやtimestamp型など)
 ● 最近 @kamipo さんがimmutable_strings_by_defaultやLazyAttributeHash 周りの更新をmasterに入れているので、メンバ参照しないケースはRails 6.0.4 or 6.1.0では劇的に早くなる気がする
 ○ https://github.com/rails/rails/pull/39599
 ○ https://github.com/rails/rails/pull/39612
 18


Slide 19

Slide 19 text

まとめ
 ● ActiveRecordで#selectによるカラム絞り込みの効果を計測した
 ● カラム絞り込みの有無により、メモリ使用量削減だけでなく実行速度にも有意な差 異が見られることが分かった
 ● まだまだ試してみたいシナリオはあるので、好評そうならまた追加で調べてまとめ たいと思います
 19


Slide 20

Slide 20 text

次回以降もブラッシュアップしていきます
 感想・リクエストなどあればTwitter
 #ginzarails
 @morimorihoge
 @hachi8833
 までお声かけください
 20