概要
プログラムを書いていてものすごく処理が重くなったりすることがある。
この辺りを意識すると改善するかもしれないことをまとめる。
今後もいい感じのものがあれば追加していきたい。
Tips
並列実行を採用する
Javaはスレッド処理ができるので並列処理ができるか検討してみる。
EX.
final int threadSize = 3;
final ForkJoinPool pool = new ForkJoinPool(threadSize);
final List<String> stringList = pool
.submit(() -> List.of(1, 2).parallelStream().map(Objects::toString))
.get().collect(
Collectors.toUnmodifiableList());
このような形でsubmit内に並列処理をしたい処理を書くと並列で実行してくれた結果を取得できる。
並列実行数はForkJoinPoolの引数で指定するが、実行環境のCPU・スレッド数と同等になるように設定する。(スレッド数より多くの値を設定するとパフォーマンスが落ちるので注意)
トランザクションの設定箇所に気をつける
DB接続を行う際、参照系の処理に対してTransactionalを記載していると、
その度にトランザクションを張りにいくため、繰り返し処理になるとコストがかかる。
@Transactional(readonly = true)
public int getCount() {...}
Transactionalは参照系では基本不要なので使用しないようにする
DBの同時接続は重いので並列処理から外す
DB接続の並列接続は逆にコストがかかって遅延の原因になるため、直列実行した方が早くなる場合がある。
EX.
selectX(); // DB接続
selectY(); // DB接続
execApi(); // API実行
上記のような繰り返し処理を並列実行する際は、DB接続は直列にしてAPI実行だけ並列など試してみると改善する可能性がある。
DBによるがoracleなどはmaxPoolSizeなど並列処理が許容できる設定か確認しておくと良い。(重い処理であればpoolに余力があっても並列処理をしない)
https://docs.oracle.com/cd/E16338_01/win.112/b66456/featConnecting.htm
並列実行処理内での更なるループ処理は行わない
並列実行内でループ処理を行うと、該当ループ処理がスレッドを占有し続けるためループ処理は並列実行内では実装しない。
EX.
final int threadSize = 3;
final ForkJoinPool pool = new ForkJoinPool(threadSize);
final List<String> stringList = pool
.submit(() -> List.of(1, 2).parallelStream()
.map(i ->
// stream処理の中で更にstream処理をする
List.of(1 + i, 2 + i).stream().map(Objects::toString).findFirst().get()))
.get().collect(
Collectors.toUnmodifiableList());
データ参照時の取得キーが同じであれば結合して取得する
複数回別テーブルにアクセスする場合、対象が少なければ
処理時間は誤差だが1万件など対象になってくると0.1秒でも1000秒の違いが出てくる。
なるべくテーブルJOINして一回のDB接続でデータは取れるようにしておく。