22
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

エムスリーAdvent Calendar 2015

Day 16

Rails でシーケンシャルスキャンしているクエリを見つけて捗りたい - PostgreSQL 限定

Posted at

こちらの記事この章に触発されて、active_record-postgresql_analyzer という名前で雑に gem を作って公開してみました。まさに Advent Calendar Driven Development ですね。それと思い出で終わらすにはもったいない機能だなと思ったので。

概要

元記事にありますように index を使わないクエリを見つけてログに書き出してくれるものです。index つけ忘れ防止に便利そうだったので、試しに社内で作って適用、実際便利だったので gem として切り出しました。
コードも github で公開しています。

ちなみにエムスリーは PostgreSQL 環境がほとんどであるため、それ以外の環境は考慮していません。

作る前に Rails にもそんな機能があったのでは?と思ったのですが、実は少し前にあまり使われないということで本体から機能削除されていました。その辺りの経緯について気になる方はこちらを参照してください。

使い方

Gemfile
gem 'active_record-postgresql_analyzer', group: :development

development 指定を忘れずに。

あとは勝手にログを出力してくれるようになります。
以下は user_id に index が貼られていない場合のログ出力例です。explain 結果をそのままログに載っけているので、一目で内容がわかり便利です。

------------ find Seq Scan query ------------
SELECT  "todo".* FROM "todo" WHERE "todo"."user_id" = $1 LIMIT 1
                                         QUERY PLAN
---------------------------------------------------------------------------------------------
 Limit  (cost=10000000000.00..10000000001.67 rows=1 width=81)
   ->  Seq Scan on todo  (cost=10000000000.00..10000000001.67 rows=1 width=81)
         Filter: (user_id = 13)
(3 rows)

内部の仕組み

簡単に機能実現のための実装を見ていきます。先に言っておきますとコード量も少ないので(1ファイル)、そっち見た方が早いです。

sequential scan をしているかを判断するためには、explain を行う必要があります。
そのために必要となるものは実行するクエリです。ということで以下の流れを実装しています。

  • クエリ取得
  • explain 実行
  • explain 結果が sequential scan か判定
  • ログ出力

まずはクエリ取得部分です。Rails には ActiveSupport::Notifications という通知を受けるための便利な仕組みがあり、この機能を使うことで Rails 内部で発生する各種イベントを取得することができます。このイベントの中にはクエリ発行時に発生する sql.active_record というイベントがあり、このイベントを購読するコールバックには sql の情報として sql 文、bind されるパラメータが渡されます。この情報が使えそうです。ちなみにその他にも渡されるパラメータがありますが詳しくは公式を参照してください。

次は explain の実行です。sql 文と bind するパラメータを渡されてもどう実行すればよいやら。。という感じですが、ActiveRecord::Base.connection.explain にそのまま渡せるようになっているので単に渡せば実行結果を取得できます。

後は残りの部分ですが、もし sequential scan になっていれば、Seq Scan という文字列が出力されるので、この文字列が存在していればログを出力するというコードを書けば一通りの機能を実現できます。

ちなみに実装にあたり参考(いわゆる inspire?)にしたものは activerecord-cause、rails のコード(この辺り)などです。

今後

現状は全てのクエリに対して出力されてしまうので filter する機能とかあると便利かもしれません。でもログなのでそのままでもいいかなとも思って手をつけていません。
後は nested loop の検知をするとかもありかもしれませんね。

所感

勢いで作ったものの意外と便利だった。それと社内で先に公開したところ、native check、命名もチェックしてもらって勉強になった。

参考リンク

22
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?