Apex CPU time limit exceededとは
Apexで負荷の高い実装をすると、こういうエラーが発生します。
System.LimitException: Apex CPU time limit exceeded
CPUに負荷かかかっているために起こるエラーです。
今回は、このエラーを回避するためのベストプラクティスについてです。
CPU時間の対象となるもの/ならないもの
ナレッジによるとCPU時間の対象となるもの、ならないものはこのようになります。
CPU時間の対象となるもの
- すべてのApexコード
- Apexで公開されているライブラリ関数
- ワークフロー
対象とならないもの(CPU時間としてカウントされていないもの)
- データベース操作
- SOSL
- コールアウト操作
つまり、対象とならないものを駆使すれば、CPU時間を軽減させることができるわけです。
タイムアウト削減のためのコーディング
では、実際にCPU時間を削減するためのコーディング方法を具体例を交えて。
マップベースクエリを使用する
まずは、SOQLの取り方について。
リストを取得し、IDとデータを取得する方法について。
// SOQLをリストにセット List<Account> lstacc=[Select Id, Name from Account limit 10000]; Set<Id> setIds=new Set<Id>(); for(Account a:lstacc){ //リストから1件ずつIdをセット setIds.add(a.id); }
こちらのコード実行でのパフォーマンスはこんな感じ。
次は同じ処理内容で、やり方を変えたコード。
// SOQLをマップに入れる Map<id,account> aMap = new Map<id,account>([Select Id,Name from Account limit 10000]); // マップからリストを取得 List accList = aMap.values(); // マップからIDリストを取得 Set accIds = aMap.keySet();
こちらのパフォーマンスはこんな感じ。
横幅のスケールが違うので若干分かりづらいですが、「APEX_CODE」ってところがCPU時間の対象となるところなので、マップベースクエリにした方がかなり処理時間が軽減されているのが分かります。
集計関数を使用する
SOQLで集計できるものは、集計関数を使用して集計しましょう。
まずは、集計関数を使わないでAPEXで集計した場合。
List<Account> lstacc=[Select Id, NumberOfEmployees from Account]; Integer num = 0; for(Account a:lstacc){ num = num + a.NumberOfEmployees; }
パフォーマンスはこう。
次は集計関数を使って同じようにカウント。
// 集計関数でカウント List<AggregateResult> lstar = [SELECT SUM(NumberOfEmployees) sumemp FROM Account]; Integer num = Integer.valueOf(lstar[0].get('sumemp'));
パフォーマンスはこう。
こちらもAPEC_CODEが軽減されていますね。
ガバナ制限に抵触しなければ、DBでやらせられるものはDBに任せるというスタンスでいいかと思います。
不要なループを減らす
これは当然ですね。
無駄なループが多くなると、その分ステップ数が増えるため、CPU時間が増えてしまいます。
まとめ
- Apexコード、ワークフローがCPU時間に考慮される
- CPU時間の削減のためには、マップベースクエリ、SOQLの集計関数などを使用する
コメント