Slide 1

Slide 1 text

バッチ処理が終わらない!? -処理時間を90%削減した話- TechBrew in 東京 〜バッチ処理 最適化の取り組み〜

Slide 2

Slide 2 text

⽬次 1. ⾃⼰紹介/弊社紹介 2. 前提 3. 性能改善事例の共有 4. まとめ

Slide 3

Slide 3 text

⾃⼰紹介 猪熊 朔也 ( いのくま さくや ) / @sinocloudon - 株式会社 Red Frasco - インフラエンジニア u経歴 - ⾦融系 SIer, リクルート(SUUMO), ⾦融系スタートアップ, 現職 uその他コメント - うどんが好きです - ラーメン⼆郎が好きです - うどん脳 をプロフィールアイコンにすることが多いです 3

Slide 4

Slide 4 text

株式会社Red Frasco • https://www.red-frasco.com/ • 不動産業界に特化したプロダクト開発・集客⽀援を実施 4

Slide 5

Slide 5 text

5 前提

Slide 6

Slide 6 text

不動産会社向けの業務⽀援システムにおける事例です 6 不動産会社 担当者 ⼊⼒ 変換 連携 業務⽀援 システム 他システム 物件データ 画像データ ⼿⼊⼒ 連携先システム ⾃動連携 システムA システムB システムC システムD ⾃動連携 ⾃動連携 ⾃動連携 ⾃動連携

Slide 7

Slide 7 text

システム構成 • Java で開発しており、Web アプリとバッチが存在します • バッチの構成は以下のとおりです • アプリケーション︓Spring Boot, Spring Batch • AWSサービス︓EventBridge, Step Function, AWS Batch(※) 7 Amazon EventBridge AWS Step Functions workflow AWS Batch AWS Batch AWS Batch ※ジョブの内容によって、ECS や Lambda も使⽤しています

Slide 8

Slide 8 text

バッチ処理概要 8 物件データ 抽出 • 登録した物件データを抽出し、ポータルサイトにデータ連携 • 物件画像は公開⽤のS3にコピーする • ポータルサイトの仕様に合わせてデータを変換する • 作成したCSVファイルをZIP化して送信する • 不動産の店舗の数だけバッチを動かす(店舗単位でバッチジョブを並列実⾏) S3に画像 コピー Aurora データ変換 ファイル 送信 内部 S3 公開⽤ S3 Get Put

Slide 9

Slide 9 text

9 性能改善事例の共有

Slide 10

Slide 10 text

どんな性能課題が発⽣したのか 10 物件データ 抽出 • 1店舗分の物件データ5,800件の処理におよそ60分かかった • 将来的には100店舗以上のデータを捌くことを想定しているため、1店舗あ たり60分はかなり厳しい… • 物件掲載に時間がかかるシステムは当然ビジネス的にもマズい… S3に画像 コピー Aurora データ変換 ファイル 送信 内部 S3 公開⽤ S3 Get Put 1分未満 1分未満 37分 22分

Slide 11

Slide 11 text

S3に画像をコピーする処理とデータ変換がやたら遅い 11 物件データ 抽出 S3に画像 コピー Aurora データ変換 ファイル 送信 内部 S3 公開⽤ S3 Get Put 1分未満 1分未満 約37分 約22分 課題1 課題2 約60分

Slide 12

Slide 12 text

12 課題1︓S3に画像をコピーする処理

Slide 13

Slide 13 text

処理の内訳を分析してみる • S3に画像コピー処理全体︓約37分 • 建物画像︓3分52秒 / 4,135枚 • 1枚あたりの処理時間︓56.1ミリ秒/枚 • 部屋画像︓32分28秒 / 38,690枚 • 1枚あたりの処理時間︓50.3ミリ秒/枚 • 周辺環境画像︓355ミリ秒 / 6枚 • 1枚あたりの処理時間︓59.1ミリ秒/枚 13 • 枚数にかかわらず処理性能が⼀定 • S3画像コピー処理を並列化(シングルスレッド→マルチスレッド)してみた

Slide 14

Slide 14 text

Java の Parallel Stream を使⽤する • 多重度の設定(並列数20に設定) • ForkJoinPool customThreadPool = new ForkJoinPool(copyImageParallelism); • 並列実⾏ • customThreadPool.submit(() -> buildingObjectPathList.parallelStream().forEach(objectPath -> buildingObjectPathMap.put(objectPath, copyS3ToOpen(objectPath)))).get(); 14

Slide 15

Slide 15 text

チューニング結果 • 37分が6分に改善した • S3を使った処理は並列化が効く • 並列化しすぎると、S3のレートリミットに引っかかった • 画像処理 10 並列 × 10 バッチ︓OK • 画像処理 20 並列 × 20 バッチ︓OK • 画像処理 40 並列 × 20 バッチ︓NG 15

Slide 16

Slide 16 text

16 課題2︓データ変換

Slide 17

Slide 17 text

処理の内訳を分析してみる その1 • データ変換処理の概要 • YAML形式の変換定義に従ってデータ変換を⾏う。 • 例︓何もしない、固定値に変換、メソッド呼び出しなど • 変換処理にメソッドを呼び出す場合は、Javaのリフレクション機能を 使った汎⽤的な実装をしていた。 • リフレクションってあんまり早くないと⾔われるのでこれが原因︖ 17

Slide 18

Slide 18 text

処理の内訳を分析してみる その2 • データ変換をさらに深掘り • 変換⽅法ごとに処理時間の平均をとった結果 • CALL_METHOD(リフレクションを使った処理) • 0.87 ミリ秒/項⽬ • CONST(定数設定) • 0.55 ミリ秒/項⽬ • INPUT(⼊⼒値設定) • 0.76 ミリ秒/項⽬ • MAPPING(マッピング) • 0.77 ミリ秒/項⽬ • NONE(何もしない) • 0.27 ミリ秒/項⽬ 18

Slide 19

Slide 19 text

処理の内訳を分析してみる その3 • データ変換をさらに深掘り • 処理時間の⼤きい変換項⽬を抽出した結果(TOP3のみ抜粋) • aboutPrice • CALL_METHOD 6.32 ミリ秒/項⽬ • interiorContents • CALL_METHOD 4.61 ミリ秒/項⽬ • roomSituationCode • CALL_METHOD 3.05 ミリ秒/項⽬ 19 • リフレクションを使っており、メソッドの引数が多い項⽬ほど処理時間が⼤きい • リフレクションがボトルネックになっていると判断し、実装を変更

Slide 20

Slide 20 text

リフレクションによる汎⽤的な処理をやめる • リフレクションを必要最低限に絞り込む • Before︓定義ファイルを1⾏読み込むたびに、リフレクションで変換処 理に必要なクラスを⽣成 • After︓定義ファイルを読み込んだ際に変換処理に必要なクラスを⼀括 ⽣成(不要なリフレクションの繰り返しをやめた) 20

Slide 21

Slide 21 text

検証したところ、どうやら効果がありそうだ 21 → 4.86秒 → 0.02秒

Slide 22

Slide 22 text

チューニング結果 • 22分が10秒に改善した • バッチ処理のような繰り返しが多い局⾯では、リフレクション のオーバーヘッドは無視できない 22

Slide 23

Slide 23 text

23 まとめ

Slide 24

Slide 24 text

おさらい︓改善前 24 物件データ 抽出 S3に画像 コピー Aurora データ変換 ファイル 送信 内部 S3 公開⽤ S3 Get Put 1分未満 1分未満 約37分 約22分 課題1 課題2 約60分

Slide 25

Slide 25 text

おさらい︓改善後 25 物件データ 抽出 S3に画像 コピー Aurora データ変換 ファイル 送信 内部 S3 公開⽤ S3 Get Put 1分未満 1分未満 約6分 約10秒 課題1 課題2 約6~7分

Slide 26

Slide 26 text

本⽇のまとめ • (当たり前ですが)処理時間の内訳を分析して、ボトルネックを 特定した上で性能改善を実施 • S3へのアクセスはとにかく並列化することで⾼速化可能 • ただし、やりすぎるとレート制限にひっかかるので注意 • 他に良い⽅法あるよ︕って⼈いたらぜひ情報交換してみたい • リクレクションを使うとオーバーヘッドがかかるので、使⽤す る際はパフォーマンスに注意 26

Slide 27

Slide 27 text

Thanks for listening!