Active Recordでのヒント句の書き方について。
クエリの実行計画の最適化を RDBMS のオプティマイザ (プランナ) に任せずに、アプリケーション側で指定するのにヒント句というのがあります。通常 RDBMS のオプティマイザに任せたりしていますが、DBA からチューニングのアドバイスがあったりしたときに使ったりできます。
Oracle、MySQL 5.7.7 以上 (MariaDB 除く) 、PostgreSQL だと pg_hint_plan が使えるものあたりがサポートしているようです。ヒント句は既存のクエリ自体は書き換えることなく、コードコメントで指示を出せるのが売りのようです。
SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
Active Record で書く際もなるべく既存のクエリ API に手を入れない形で書けると嬉しいところ。
Rails 6.0 以上だと、kamipo さんのパッチによって optimizer_hints
メソッドを使った指定ができます。メソッドチェーンで足す形で書けるのが便利。以下 API ドキュメントから抜粋。
Example (for MySQL):
Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)") #=> SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
Example (for PostgreSQL with pg_hint_plan):
Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)") #=> SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
Rails 5.2 以下であれば、select
メソッドを使って指定するといった形式になると思います。
以下は select
の振る舞いを利用して、カラムの前にコメントとしてヒントを足す方法ですが、既存の select
メソッドの引数に手を加える必要があるのが難点です。
Topic.select("/*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.*")
他にも方法はあると思いますが、Rails 6.0 以上でのoptimizer_hints
メソッドが使えれば流れるようなインタフェースにできてクールでしょう。
2020-04-21 追記
PostgreSQL へのサポートについて kamipo さんからのアドバイスを追記しておきます。
アクティブレコードのoptimizer hints、ポスグレでも動いてそうなテスト書いてるけど手元にpg_hint_plan入れても全く動かなくてヒントが効こうが効かまいが結果に影響がないヒントをわざと書いてそれっぽい感じを演出してるだけなので、動作は君の目で確かめてほしい。
— Ryuta Kamizono (@kamipo) 2020年4月21日