SOQLの実行計画を取得してクエリのパフォーマンスを向上させよう

はじめに

皆さんクエリ投げてますか?

Salesforceのテーブル問合せ言語であるSOQLを実行する際に、パフォーマンスの向上やエラーの回避を目的として、開発者はインデックスの利用を検討し、セレクティブなクエリ(詳細は後述する早見表)を心がけます。 インデックスには標準項目に付いているものや、カスタム項目に対して外部IDに指定して設定するもの、またはサポートデスクに依頼して設定するカスタムインデックスがあります。

ただし、インデックスを使用していても、SOQLの書き方や対象オブジェクトのデータ量および抽出データ件数等の様々な要因によって、セレクティブなクエリとならない場合があります。

それらの条件についてはこちらの早見表に記載されていますが、「じゃあ実際にインデックスが有効に働いているのか?」というのを自由に確認することはできず、大量データを扱う開発者を苦悩させていました。 そんな悩める開発者にうれしいお知らせです。

Spring'14(API 30.0)からREST API経由でSOQLの実行計画を取得できるようになりました。 SQLの実行計画のように詳細まで表示されるものではありませんが、これを利用すると、そのSOQLを実行した時にインデックスが有効に働いているかのフィードバックを得ることが可能です。 この機能を利用することで、SOQLのパフォーマンスのチューニングやエラーを回避するために、そのSOQLがセレクティブなクエリであるか確認できます。

取得方法

さっそくクエリのパフォーマンスに関するフィードバックを取得してみましょう。 先にも述べましたがフィードバックパラメータはREST APIで取得することができます。 なお、SalesforceのREST APIについては、日本語版リファレンスがあるので、詳細はそちらを参照してください。 フィードバックは以下のようにexplainパラメータで取得することができます。

/services/data/v30.0/query/?explain=SELECT Name FROM Account

REST APIを実行する為にリファレンスではcURLというコマンドラインツールの利用が示されていますが、今回はApigeeという様々なAPIを実行することができるサービスのSalesforceコンソールを使用します。

Apigee Saesforce Console https://apigee.com/console/salesforce

コンソールを表示したら、クエリ実行先のSalesforce組織との認証を行います。

認証が成功したら、実際にリクエストを投げてフィードバックを取得してみましょう。 試しに単純な以下のSOQLを実行してみます。 これはAccountを全件取得するSOQLなので、セレクティブなクエリでも何でもありません。

SELECT Name FROM Account

APIが30.0になっている点に注意してください。

すると以下のようなJSON形式でレスポンスが返ってきます。

これが「SELECT Name FROM Account」に対するフィードバックの計画となります。 次にこの計画の各要素が何を示しているか確認しましょう。

cardinality

クエリの結果として取得されるレコード件数です。 例では85件取得します。

fields

クエリに使用されるインデックスの項目が配列で表示されます。 例ではインデックスを使用していないので何も表示されていません。

leadingOperationType

クエリの種別で次のいずれかが表示されます。

  • ■ TableScan - 全てのレコードがスキャンされ、インデックスは使用されない。
  • ■ Index - クエリに対してインデックスを使用する。
  • ■ Sharing - 実行ユーザの共有ルールに基づいたインデックスがクエリで使用されたもの。これは外部IDやカスタムインデックスでのIndexというよりは、共有ルールにより抽出が絞られた結果のようです。
  • ■ Other - 内部的な最適化が使用されたもの。詳細はよく分かりませんが、クエリオプティマイザが全スキャンをするのではなく、その他の条件によって最適化を行う時に表示されるようです。

relativeCost

セレクティブなクエリの閾値と比較した、このクエリのコスト。この値が1.0を超える場合はセレクティブなクエリにはならないことを示しているとのことです。 例では2.675...という値になっており、1.0を超えていますが、これは全件検索しているので当然の結果と言えます。

sobjectCardinality

対象のオブジェクトの組織内にある全レコードの概数。このカウントはタイムラグがあるようで、厳密な全レコード件数とはなりません。 例では85件となっています。

sobjectType

クエリを投げる対象オブジェクト名。 例ではAccountとなっています。

インデックスを使ったSOQLのフィードバック

先の例では「SELECT Name FROM Account」という全スキャンするSOQLでしたが、これにインデックスを使用するとどういった結果が返ってくるか見てみましょう。 次は以下のSOQLです。

SELECT Name FROM Account WHERE Id IN ('001A000000i9G2t','001A000000i9G2o')

これは標準でインデックスが適用されるId項目を条件にしています。 この結果は次の通りです。

今度はplansとして2件の結果が返ってきました。 leadingOperationTypeが1件は"Index"、もう1件は"TableScan"となっています。

これはId列でインデックスが使用されたプランと、フルスキャンとなるTableScanのそれぞれのプランです。 どちらもcaradinality(結果となるレコード件数)は"2"と同じ値ですが、Indexの方はrelativeCostが0.074..と1.0を大きく下回っています。 フルスキャンをしたtableScanの値も0.677..と一応は1.0を下回っていますが、これは全体のレコード件数が85と少ないからであり、これがセレクティブなクエリとは言えません。 実際にテストを行う場合は、少なくとも1万件以上はレコードが登録されていたほうが良いようです。

2件以上のプランがあった場合は、最初のプランがクエリとして使用される(効果が高い順に並んでるっぽい)ので、この例ではよりパフォーマンスの高いIndexのプランが適用されることになります。 つまり、この実行計画から分かるのは、Force.comクエリオプティマイザはIndexとTableScanのプランを検討し、Id項目を使用したプランを適用するということです。 また、IndexのプランはrelativeCostも1.0を下回っているので、セレクティブなクエリになっているということが分かります。

最後に

今回紹介したREST APIを使ったクエリパフォーマンスのフィードバック機能は本記事の公開時点でパイロットリリースとなっています。 API30.0を使えば特にサポートに依頼することなく利用することが出来ますが、パイロットのせいか、SOQLの取得レコード件数が期待する結果とならないこともありました。 ただ、クエリの実行計画を取得する機能は非常に有効なので、正式リリースが待ち遠しいところです。

また、Salesforce社のUSブログではこれらフィードバックパラメータの使い方の他に、VisualforceからREST APIを使ってこれらの実行計画を取得するためのサンプルコードも公開されています。 そちらを見ると、より理解が深まると思います。