tyoshikawa1106のブログ

- Force.com Developer Blog -

SFDC:LDV(大量データ)のベストプラクティス日本語版読んでみました

LDV(大量データ)のベストプラクティス日本語版が公開されたので読んでみました。
大量データをどのようにSalesforce上でパフォーマンス良く扱うか等について記載されています。

LDV(大量データ)のベストプラクティス日本語版

http://developerforcejp.s3.amazonaws.com/developer/docs/ldv_deployments/salesforce_large_data_volumes_bp.pdf

LDVの事例

事例を元にした対応例についてメモ。

データ集計

状況

顧客は標準レポートを使用して毎月および毎年の総計値を集計する必要がありました。
顧客の毎月および毎年の詳細は、それぞれ400万件と900万件のレコードを含むカスタムオブジェクトに保存されていました。
レポートは、これら2つのオブジェクトにまたがる数百万件のレコードを集計していたため、パフォーマンスは最適ではありませんでした。

解決方法

この問題は、毎月、毎年の値を必要なレポートの必要な形式に集計する集計カスタムオブジェクトを作成することで解決されました。
レポートはこの集計カスタムオブジェクトから実行され、集計オブジェクトはApex一括処理を使用して入力されました。

カスタム検索機能

状況

顧客は、特定の値とワイルドカードを使用して、複数のオブジェクトにまたがってLDV内を検索する必要がありました。
そのため顧客は、ユーザが1~20個の異なる項目を入力し、それらの項目の組み合わせに対してSOQLを使用して検索できるカスタムVisualforceページを作成しました。
その結果、検索の最適化が次の理由で困難になりました。

  • 多数の値が入力されるとWHERE句が大きくなり調整が難しい。
  • ワイルドカードが使用されるとクエリの処理時間が長くなる。
  • 検索クエリ全体の結果を返すために、複数のオブジェクトにまたがるクエリの実行が必要になる場合がある。
  • SOQLは、すべてのクエリタイプに使用するのは必ずしも適切ではない。
解決方法

この問題は次のように解決されました。

  • 不可避な検索項目のみを使用して、検索対象の項目数を削減する。
  • 1回の検索で同時に使用できる項目を一般的な使用例に制限することで、Salseforceがインデックスを使用して調整できるようにした。
  • 複数のオブジェクトのデータを単一のカスタムオブジェクトに非正規化し、複数のクエリコールを実行する必要が生じないようにする。
  • OQL またはSOSL の使用を動的に判断し、検索される項目数と入力された値の型の両方に基づいて検索を実行する。

補足
先頭にワイルドカードを含む検索は、SOQL よりもSOSL で実行したほうがパフォーマンスが良くなります。

null を使用したインデックス付け

状況

顧客は、項目で null を許可し、null 値を使用してその項目をクエリできるようにする必要がありました。
選択リストおよび外部キー項目の単一列インデックスでは、インデックス列が null の行が除外されるため、null クエリにインデックスを使用できませんでした。

解決方法

最初から null 値を使用しないことがベストプラクティスです。
同じような状況にある場合は、NULLの代わりに、N/A など他の文字列を使用してください。

null 値を含むオブジェクトにレコードがすでに存在するなどの理由で他の文字列を使用できない場合は、null の代わりにテキストを表示する数式項目を作成し、この数式項目にインデックス付けします。

重要

たとえば、[Status] 項目にインデックスが付けられていて、null が含まれているとします。
次のようなSOQL クエリを発行すると、インデックスは使用されなくなります。

SELECT Name from Object where Status__c=’’

代わりに、Status_Value という数式を作成できます。

Status_Value__c=IF(ISBLANK(Status__c),”blank”,Status__c)

この数式項目は、インデックス付けが可能で、null 値をクエリするときに使用できます。

SELECT Name from Object where Status_Value__c =’blank’

この概念を拡張し、複数の項目を含めることができます。

SELECT Name from OBJECT where Status_Value__c =’’ OR Email=’’

LDV を含む関連リストの表示

状況

顧客には、数十万件の取引先レコードと 1,500 万件の請求書があり、これらは取引先と主従関係にあるカスタムオブジェクト内に保存されていました。
請求書関連リストの表示時間が長いため、各取引先レコードが表示されるのに時間がかかっていました。

解決方法

請求書関連リストの表示の遅れは、データスキューに関連していました。
ほとんどの取引先レコードの請求書レコードは数件でしたが、なかには数千件もの請求書を含むレコードがありました。
遅延を軽減するために、顧客はこれらの親の請求書レコード数を減らし、子オブジェクトのデータスキューを最小限に抑えることを試みました。
[関連リストの別途読み込みを有効化]機能を使用することで、関連リストのクエリが完了するのを待つ間に取引先詳細が表示できるようになりました。

API パフォーマンス

状況

顧客は、外部の顧客のアプリケーションとSalesforce データを同期するカスタムインテグレーションを設計しました。
インテグレーションプロセスは、次の手順で実行されました。

  • 特定のオブジェクトのすべてのデータを対象にSalesforce でクエリを実行する
  • このデータを外部システムに読み込む
  • どのデータがSalesforce から削除されたのかをインテグレーションプロセスで判断できるように、Salesforce で

再度クエリを実行してすべてのデータの ID を取得する

オブジェクトには数百万のレコードが含まれていました。
このインテグレーションでは、取得されるレコード数を制限するために、共有階層に含まれる特定のAPI ユーザも使用しました。
クエリは数分で完了しました。

Salesforce では、共有は特定のレコードを特定のユーザに表示されるようにする非常に強力なメカニズムであり、UI 操作に使用すると効果的です。
ただし、SOQL クエリで大量データの検索条件として使用する場合、共有を検索条件として使用するとデータアクセスの処理がより複雑で困難であるため、パフォーマンスが低下する可能性があります。
これは LDV を使用している状況で、レコードを除外しようとする場合に特に当てはまります。

解決方法

この問題は、クエリにすべてのデータへのアクセス権を与え、セレクティブ検索条件を使用して適切なレコードを取得することで解決しました。
たとえば、システム管理者をAPIユーザとして使用すると、すべてのデータへのアクセス権が与えられ、クエリで共有が考慮されなくなります。
また、デルタ抽出を作成し、処理が必要なデータ量を削減することでも解決できたはずです。

クエリの並び替えの最適化

状況

顧客は次のようなクエリを使用していました。

Select Id,Product_Code__c FROM Customer_Product__c WHERE CreatedDate = Last_N_Days:3

過去 3 日間に作成されたすべてのレコードをクエリで検出しましたが、オブジェクトのデータ量が、合計レコード数の 30% (最大で 100 万まで) という標準インデックスのしきい値を超えてしまいました。
クエリのパフォーマンスはよくありませんでした。

解決方法

クエリは次のように書き換えられました。

Select Id,Product_Code__c FROM Customer_Product__c WHERE CreatedDate = Last_N_Days:3 order
by createddate limit 99999

このクエリでは、しきい値チェックを実行せず、レコードの検出にはcreateddate インデックスを使用しました。
このようなクエリでは、過去 3 日間に作成されたレコード数が 99,999 以下であると仮定して、最大で 99,999件のレコードが過去 3 日以内に作成された順に返されます。

※補足
通常、Last_N_Days の間に追加されたデータをクエリするときには、レコード数を 100,000 件未満に制限してインデックス付き項目にorder by を指定すると、クエリの実行にはorder by インデックスが使用されます。

複数結合レポートのパフォーマンス

状況

顧客は、取引先 (314,000)、販売注文 (769,000)、販売詳細 (230 万)、取引先会社形態 (120 万) という 4 つの関連オブジェクトを使用してレポートを作成しました。
このレポートでは条件検索があまり行われず、最適化する必要がありました。

解決方法

レポートを最適化するために、顧客は次を行いました。

  • 検索条件を追加してクエリの選択度を高め、できるだけ多くの検索条件にインデックス付けができるようにした。
  • 可能な場合には、各オブジェクトのデータ量を削減した。
  • ごみ箱を空の状態に保った。

※補足
ごみ箱にデータが入っていると、クエリのパフォーマンスに影響します。

4 つの関連オブジェクトに複雑な共有ルールが存在しないようにした。
※補足
複雑な共有ルールは、パフォーマンスに顕著な影響を及ぼす可能性があります。

まとめ

データ集計

2つのオブジェクトにまたがる数百万件のレコードを集計するのは、パフォーマンスが良くない。
集計用のカスタムオブジェクトを作成してApexなどで集計処理を行い1つにまとめる。

カスタム検索機能

複数のオブジェクトにまたがって検索する場合はSOQLではなくSOSLでの実装も検討する。
検索条件や取得項目、取得件数は出来る限り少なくする。

null を使用したインデックス付け

検索条件にnullを指定するとインデックスが使用されなくなってしまう。
数式等で判定するようにすると大丈夫。

LDV を含む関連リストの表示

[関連リストの別途読み込みを有効化]機能を使用することで、関連リストのクエリが完了するのを待つ間に取引先詳細が表示できる。

API パフォーマンス

クエリで大量データにアクセスするときに共有を検索条件として使用するとデータアクセスの処理がより複雑で困難でパフォーマンスが低下する。
クエリにすべてのデータへのアクセス権を与えるとパフォーマンスが向上する。

クエリの並び替えの最適化

検索条件に過去何日分の判定をする場合は、作成される件数の予測をLimitで指定するとパフォーマンスが向上する。

複数結合レポートのパフォーマンス

次の方法でレポートを最適化できる

  • 検索条件を追加してクエリの選択度を高め、できるだけ多くの検索条件にインデックス付けができるようにする。
  • 可能な場合には、各オブジェクトのデータ量を削減する。
  • ごみ箱を空の状態に保つ。