20.7K Views
February 20, 23
スライド概要
主にSalesforceに関する資料をUPします
Salesforceのインデックスの話 ~データ量が増えてもあわてないために~ Mark Hammer Twitter: @Mar9Hammer https://sfblog.markhammer.net/
概要 ● Salesforceのオブジェクトに大量レコード(目安として100万超)があると、以下の ような問題が発生しやすくなる ○ ○ ○ レポートの表示が遅くなる ApexのSOQL実行が遅くなり、ガバナ制限に引っ掛かりやすくなる Non-selective query エラーが発生する ● 今回はこのような問題を避けるのに必要なインデックスについて説明する ● ちなみにこの内容はData Architect試験対象範囲なので受験予定者は知ってお くとよい 2
インデックスについて ● データベースでは、対象項目とその項目の位置(ポインタ)を格納することで検索 速度を向上する手法 ○ ○ 以下の図では、インデックスがなければ対象データを上から順番に探すしかないが、 インデックスがあればすぐに位置を探し出せる 一方、テーブルの他にインデックスも更新する必要があるので更新に時間がかかる 引用元:https://atmarkit.itmedia.co.jp/ait/articles/1703/01/news199.html 3
インデックスについて ● SQL例 ○ Select Field1, Field2 From Table1 Where Field3 = ‘Main’; ○ この時、Field3にインデックスがあれば使用される ■ 実際にはクエリオプティマイザにてインデックスを使ったほうが速いと判 断された場合は使用する ● インデックスには、単一項目を対象としたもののほかに複数項目を対象としたも のもある(複合インデックスと呼ぶ) ○ 例:Select Field1, Field2 From Table1 Where Field3 = ‘Main’ and Field4 = ‘Success’; この場合、Field3とField4両方を対象にした複合インデックスが使用される 4
Salesforceにおけるインデックス ● Salesforceのテーブル構造もデータベースのテーブル構造と同様のため、インデック スにより検索速度向上が望める ○ ○ ○ 特定の項目には標準でインデックスが付与されている ■ 例:レコードID、レコードタイプID、作成日、最終更新日、レコード名等 カスタム項目の場合、外部IDを有効にすることでインデックスが付与される インデックスがない項目は、Salesforceサポートに問い合わせることで付与できるが、付 与できない項目もある 付与できない例:複数選択リスト、ロングテキストエリア、リッチテキストエリア、非決 定性数式項目等 インデックスがある場合でも、検索演算子によっては使われない場合がある(後述) ■ ○ 5
数式項目へのインデックスの付与 ● 数式項目の場合、以下すべての条件を満たす必要がある ○ 数式に 1 つのオブジェクトの項目のみが含まれる (リレーション項目がない) 数式項目が非決定的関数を参照していない ■ 常に同じ値を返すとは限らない関数のこと。例:TODAY()、NOW() 数式項目が、インデックスに含める項目としてサポートされない項目を参照していな い ■ このリストはどこにも具体的に文書化されていない 数式項目に主キー (Id など) への参照項目が含まれていない ○ ○ 数式項目で TEXT([選択リスト項目]) 関数を使用していない 数式が参照項目を参照している場合、その項目のオプション [参照レコードが削除さ ○ ○ ○ れた場合の対処方法] を [この項目の値をクリアします] に設定することはできない 6
数式項目へのインデックスの付与 引用元:https://www.slideshare.net/DeveloperForceJapan/tips-30066265 7
Salesforceでインデックスが使用される条件(セレクティブクエリ) ● 前提 ○ ○ 標準インデックス:Salesforce側で自動付与するもの カスタムインデックス:外部IDによる付与、サポートによる付与 ● 演算子(カスタムインデックスの場合) ○ ○ ○ 検索条件の演算子が、EQUAL(=)、IN⇒使用する 検索条件の演算子が、NOT EQUAL TO (または !=)、NOT CONTAINS、NOT STARTS WITH などの否定演算子⇒使用しない 検索条件に CONTAINS 演算子が使用され、スキャンされる行数が 333,000 を超 える⇒使用しない ■ CONTAINS 演算子にはインデックスの完全スキャンが必要なため ○ ○ 空の値と比較している場合 (Name != '')⇒使用しない LIKE を使用した場合⇒実際のデータのレコードを抽出するサンプリングを行い、カ スタムインデックスを使用するかを決定する 8
Salesforceでインデックスが使用される条件(セレクティブクエリ) ● レコード数(条件の一部) ○ 標準インデックス付き項目:条件の一致率が、最初の100 万件のレコードで 30% 未満、その他 のレコードで 15% 未満(最大100万件) ○ カスタムインデックス付き項目:条件の一致率が、検索条件が合計レコード数の10% 未満 (最 大333,333 件) WHERE句にAND、ORを含む場合 ■ AND の場合:インデックスのいずれかから返されるレコード数が、オブジェクトのレコード数 の 20 % または合計レコード数 666,666 を超えない限り、インデックスを使用 ■ OR の場合:すべてのインデックスから返されるレコード数が、オブジェクトのレコード数の 10 % または合計レコード数 333,333 を超えない限り、インデックスを使用 ○ 9
Salesforceでインデックスが使用される条件(セレクティブクエリ) ● ANDの例 ○ ○ Select Field1 From Account Where Field2 = ‘Salesforce’ AND Field3 = ‘IT’; このSOQLではField2のみにインデックスがあったとしても、条件を満たせばインデッ クスは使用される ● ORの例 ○ ○ Select Field1 From Account Where Field2 = ‘Salesforce’ OR Field3 = ‘IT’; このSOQLでField2のみにインデックスがあった場合、インデックスは使用されない ■ 「OR句で連結されるすべての項目にカスタムインデックスが作成されている必 要があります」という但し書きがある 10
セレクティブクエリでない場合のエラー ● 「System.QueryException: Non-selective query against large object type (more than 1000000 rows) 」エラー ○ ○ インデックスを考慮せずにSOQLクエリを使用したままデータ量が増えた場合に発生 するエラー この場合はセレクティブクエリになるよう対応する必要あり ■ 否定演算子しか使用していない場合はEQUAL(=)やIN句を使えないか検討す る ■ 決定性数式項目を用いたWhere句にできないか検討する ■ ■ Where句で使用している項目に外部IDを付けられるなら付ける Salesforceサポートに問い合わせてインデックスを付与してもらう ■ どうしても無理な場合はロジックから検討し直す必要がある 11
レポートの場合 ● レポート・ダッシュボードの表示が遅い場合は、ノンセレクティブクエリ同様の対 応が可能 ○ ○ SOQLとは違い、該当レポートからSalesforceサポートが適切なインデックスを付与 してくれる ただし単一項目インデックスを付与してほしいのに複合インデックスを付与すること がある。この場合は単一項目インデックスにならないか交渉する必要がある ● 例 ○ ○ 取引先に400万件のレコードがある。レポートは通常「業種+その他の項目」で絞り込 まれる この時、できれば「業種」に単一項目インデックスを付与してほしいが、レポートの検 索条件が「業種+取引形態」の場合、「業種+取引形態の複合インデックス」が付与 される場合がある 12
クエリプランツール ● SOQLやレポートのクエリを評価するツール ○ ○ 開発者コンソールにて[Help]→[Preferences]から[Enable Query Plan]を有効にする 有効にするとQuery Editorに「Query Plan」ボタンが表示される ○ レポートの場合はレポートIDを入力すればOK ● 使用例 13
セレクティブクエリ改善例 ● カスタムオブジェクトにて以下の項目がある ○ ○ ○ ○ ○ オブジェクト名:Product__c 「種別」項目:Type__c(選択リスト) ■ 選択肢は10個、全てアルファベット1文字 「主番号」項目:MainNo__c(テキスト項目) 「枝番」項目:BranchNo__c(テキスト項目) 「商品番号」項目:ProductNo__c(数式(テキスト)項目) ■ 数式:TEXT(Type__c) & MainNo__c & BranchNo__c ■ 例:T10000100 ● 「商品番号」項目を用いたSOQLをApexコード内で使用している ○ Select XXX From Product__c Where ProductNo__c = :varNo ■ varNoは外部から来る変数 14
セレクティブクエリ改善例 ● 状況 ○ ○ ○ ある日、「商品番号」項目を用いた前述のSOQLに対しノンセレクティブクエリ起因の エラーが発生した Salesforceサポートに問い合わせたが、「ProductNo__cは非決定性数式項目のた めインデックスを付与できません。ロジックを見直してください」と回答され、解決でき なかった あなたはこのエラーをどう解決しますか? ● 制限 ○ 「種別」項目をテキスト項目に変更することはできない ○ 「主番号」、「枝番」項目は桁数が固定されていないため、varNo変数を「主番号」項 目部分と「枝番」項目部分に分割することはできない 15
セレクティブクエリ改善例 ● 解決方針 ○ ○ ProductNo__cにインデックスを付与できないのはTEXT(Type__c)があるためなの で、これを外したい TEXT(Type__c)を外しても同じレコードがヒットする条件を作る必要がある ● オブジェクト・項目再掲 ○ オブジェクト名:Product__c ○ 「種別」項目:Type__c(選択リスト) ■ 選択肢は10個、全てアルファベット1文字 ○ ○ 「主番号」項目:MainNo__c(テキスト項目、桁数不定) 「枝番」項目:BranchNo__c(テキスト項目、桁数不定) ○ 「商品番号」項目:ProductNo__c(数式(テキスト)項目) ■ 数式:TEXT(Type__c) & MainNo__c & BranchNo__c ■ 例:T10000100 16
セレクティブクエリ改善例 ● 解決方法 ○ ○ ○ 「種別」項目が1文字固定であり、今後も変更しないことを合意したうえで「インデック ス用番号」項目:IndexNo__c(数式(テキスト)項目)を作成 ■ 数式:MainNo__c & BranchNo__c ■ 「商品番号」項目が”T10000100”の場合、 「インデックス用番号」項目は”10000100”になる Salesforceサポートにて「インデックス用番号」項目にインデックス付与 ■ IndexNo__cはTEXT関数がないのでインデックス付与可能 SOQLを以下のように書き換え ■ Select XXX From Product__c Where ProductNo__c = :varNo AND IndexNo__c = :varIndexNo ● varIndexNoはvarNoから頭1文字取った変数 IndexNo__c = :varIndexNo は ProductNo__c = :varNo でヒットするレ コードは全部ヒットする AND句のため IndexNo__c = :varIndexNo のみでインデックス適用条件を達成す ればよいので、この変更でエラー回避できた ● ○ 17
まとめ ● Salesforceで1オブジェクト当たり100万件以上のデータを扱うと、いつかレポー トが遅くなったりノンセレクティブクエリエラーが起きたりする ● 基本的にデータは増加する一方なので、今まで問題なかったことがある日問題 になったりする ● その時にインデックスのことを知っていたのと知らないのでは大きな差につなが る ● 新規開発段階ではデータ量の問題は起こらないと思うが、データ量増加を見越 してインデックスが使えないロジックは極力避けましょう ● Data Architect受験者はこのあたりはガッツリ出るので勉強しましょう 18
終わり ご清聴ありがとうございました 19
参考資料 ● ● データベース性能を向上させる「インデックス」を理解する:「データベーススペシャリスト試験」戦略的 学習のススメ(26) - @IT 大量データを扱う際のクイックTips インデックス&スキニーテーブル編インデックス | 大量のデータを使用するリリースのベストプラクティス| Salesforce Developers ● ● ● ● ● ● SOQL クエリの処理速度への考慮事項について セレクティブ SOQL クエリを使用するカスタムインデックスによるパフォーマンスの向上 SOQL Error System.QueryException: 大規模なオブジェクト種別に対する非選択的クエリ [SOQL] カスタムインデックスが必要な場合の依頼方法について レポートやダッシュボードコンポーネント、リストビューのパフォーマンスチューニングについて クエリプランツールの FAQ ● 20