Slide 1

Slide 1 text

@PHPerKaigi 2026(2026/03/22) プログラミングをするパンダ(@Panda_Program) 大規模ECサイトの あるバッチのパフォーマンスを改善するために 僕たちのチームがしてきたこと 1

Slide 2

Slide 2 text

2 © 2012-2025 BASE, Inc. 自己紹介 ● 所属: BASE株式会社(プロダクトリード) ○ フロントエンドもバックエンドもPjMもやります ○ 2025年は管理画面デザインリニューアルPJのチームリードをしてました ○ 直近はカート領域を担当してます ● 激推しツール: Heptabase ● 激推し漫画: さむわんへるつ、カグラバチ ● 活動 ○ 2025年の執筆 ■ 技術書典18でチーム開発の書籍を自費出版(全85P)→ ■ 2026年は執筆抑えてますが、またどんどんやりたい プログラミングをするパンダ(X: @Panda_Program)

Slide 3

Slide 3 text

@PHPerKaigi 2026(2026/03/22) プログラミングをするパンダ(@Panda_Program) 大規模ECサイトの あるバッチのパフォーマンスを改善するために 僕たちのチームがしてきたこと 3

Slide 4

Slide 4 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 44 Q. 5万件の商品が23:59からセール開始の予約をされたら、 実際に全ての商品のセールが開始されるまで、 どれくらいの時間がかると思いますか?

Slide 5

Slide 5 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 55 Q. 5万件の商品が23:59からセール開始の予約をされたら、 実際に全ての商品のセールが開始されるまで、 どれくらいの時間がかると思いますか? A. 約5分かかっていました つまり、1万件のセール開始に1分かかる計算です (実測値: 2025/11/09 23:59 開始, 最⻑308s)

Slide 6

Slide 6 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 66 課題: セール予約の処理は8年前に作られた → 8年前のショップ数は40万。2025/11 時点では250万ショップを突破 → セール予約が集中した時に、開始‧終了がタイムリーに実施できない

Slide 7

Slide 7 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 77 このまま何もしないと、2026/01/01にインシデントが再度発⽣してしまう

Slide 8

Slide 8 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 88 1 2 3 現状把握・準備・設計 改善サイクルを回す そして、新年を迎える 4 まとめ

Slide 9

Slide 9 text

1. 現状把握・準備・設計

Slide 10

Slide 10 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 10 10 ポストモーテム共有会(2024/8/7) 2025年の2回のインシデントは、「セール予約」で セール開始が⼤幅に遅延したことによるものだった

Slide 11

Slide 11 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 11 11 11⽉にブラックフライデー、12⽉中旬にコードフリーズ、 翌1⽉1⽇には正⽉のセールがある それまでに改善を⼊れたい 000 0000 0000

Slide 12

Slide 12 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 12 12 アクセル+セールで accsale @panda @meihei 業務委託 Aさん セール開始を早くする「PJ-アクセル」 10⽉中旬から対応開始

Slide 13

Slide 13 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 13 13 Looker セール予約対象の商品数を可視化 Slack に毎⽇通知して⽇々の変化をチェックする 20000 70000 300 80 30 60

Slide 14

Slide 14 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 14 14 New Relic セールバッチのダッシュボードを作成 バッチの実⾏時間やCPU, メモリの使⽤率などを可視化

Slide 15

Slide 15 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 15 15 Meteofall ⾃動で⼤量のショップを作成し、商品を登録し、セール予約をする Playwright 製ツール

Slide 16

Slide 16 text

© 2012-2026 BASE, Inc. 1. 現状把握・準備・設計 16 16 理想と現実のはざまで揺れる意思決定 アプリケーションコードを書き換えるだけの⽅針を採⽤ ちなみにここで発表するのはレガシー側のコード (モジュラーモノリス側ではない)

Slide 17

Slide 17 text

2. 改善サイクルを回す

Slide 18

Slide 18 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 18 18 SELECT の N+1 ループを削減する 改善 No.1

Slide 19

Slide 19 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 19 19 SELECT の N+1 ループを削減する 改善 No.1

Slide 20

Slide 20 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 20 20 SELECT の N+1 ループを削減する 改善 No.1 事実: for ループの中で SELECT が N+1 になっている 仮説: SELECT の N+1 を解消すると早くなるのでは?

Slide 21

Slide 21 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 21 21 SELECT の N+1 ループを削減する 改善 No.1 dev 環境で計測したものの、 効果なし → 不採⽤ PJ初⽇に推測で「早くなるんじゃね?」 で試したが、だめ やはり「推測するな、計測せよ」 (”Don’t tune for speed until you’ve measured” by Rob Pike) https://www.cs.unc.edu/~stotts/COMP590-059-f24/robsrules.html

Slide 22

Slide 22 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 22 22 パフォーマンス改善のサイクルを回す

Slide 23

Slide 23 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 23 23 「接続」—パフォーマンスチューニングの最後の一手 〜点と点を結ぶ、その一瞬のために〜(武田 憲太郎) https://fortee.jp/phperkaigi-2026/proposal/a16d700c-23b2-4345-956c-eb7869daa814

Slide 24

Slide 24 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 24 24 AWS SNS への Publish を Bulk 化する 改善 No.2 計測: New Relic の APM の Transactions でバッチの実⾏時間をチェック → 「Custom/EventNotifier::notifyItemEdited」 何これ?

Slide 25

Slide 25 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 25 25 AWS SNS への Publish を Bulk 化する 改善 No.2 事実: 商品価格の変更を CloudSearch に通知するため、 O(NM)でSNSへpublish している 仮説: ネットワークのレイテンシが遅い原因の⼀つでは?

Slide 26

Slide 26 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 26 26 AWS SNS への Publish を Bulk 化する 改善 No.2 実装: 1セールグループにつき、SNS Publishを1回に削減

Slide 27

Slide 27 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 27 27 AWS SNS への Publish を Bulk 化する 改善 No.2 検証: 5,000件, 1プロセスで 4m33s → 2m58s | 34.8% の速度改善! (Dev環境。 50ショップ×100商品)

Slide 28

Slide 28 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 28 28 次!

Slide 29

Slide 29 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 29 29 価格変更ログへの Insert を Bulk 化する 改善 No.3 計測: 「Custom/Model::save」 何これ?

Slide 30

Slide 30 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 30 30 価格変更ログへの Insert を Bulk 化する 改善 No.3 事実: 商品価格を変更するとき、CakePHPの Model の afterSave で 価格変動ログに履歴を Insert している 仮説: Itemテーブル以外のN+1を抑制すれば早くなるのでは?

Slide 31

Slide 31 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 31 31 価格変更ログへの Insert を Bulk 化する 改善 No.3 実装: 専⽤メソッドで価格変更の N+1 を回避 + Loop 外で⼀括挿⼊

Slide 32

Slide 32 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 32 32 価格変更ログへの Insert を Bulk 化する 改善 No.3 検証: 5,000件, 1プロセス で 2m58s → 2m30s | 15.7% の速度改善! (Dev環境。 50ショップ×100商品)

Slide 33

Slide 33 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 33 33 Prodの結果: 1万件/分 → 1.6万件/分にスピードアップ (約10.8万件 → 6m44s で完了)

Slide 34

Slide 34 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 34 34 次!次!

Slide 35

Slide 35 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 35 35 更新する商品数をプロセスごとに均等にする 改善 No.4 ● 事実 ○ セール開始バッチは、2つのバッチサーバー で20並列で実⾏している ○ 合計40のプロセスにそれぞれ割り当てられる 商品数にはバラつきがある ● 仮説 ○ プロセスごとに商品数を均等にすれば、処理 時間も差がつかないのでは?

Slide 36

Slide 36 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 36 36 更新する商品数をプロセスごとに均等にする 改善 No.4 仕様変更: セール予約は5分前に締め切る(PdMに提案してOKを貰った) ロジック: グループを商品数が多い順ソート→合計商品数が最⼩のプロセスに割当 実装 : 新規テーブルを作成し、セール開始の3分前に各セールグループに 「どのプロセスが処理するか」というアサイン番号を振る

Slide 37

Slide 37 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 37 37 「パフォーマンスを改善するには仕様変更が 1番はやい」(やまもとひろや) https://speakerdeck.com/yamamotohiroya/pahuomansuwogai-shan-surunihashi-yang-bian-geng-ga1fan-hayai

Slide 38

Slide 38 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 38 38 更新する商品数をプロセスごとに均等にする 改善 No.4

Slide 39

Slide 39 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 39 39 更新する商品数をプロセスごとに均等にする 改善 No.4

Slide 40

Slide 40 text

© 2012-2026 BASE, Inc. 2. 改善サイクルを回す 40 40 結果: 1.6万件/分 → 2.3万件/分にスピードアップ! (約4万件 → 1m44s で完了) 1万件/分 → 2.3万件/分だと130%(!)の改善 PJ-アクセル号

Slide 41

Slide 41 text

© 2012-2026 BASE, Inc. バッチのパフォーマンス改善 41 41 PJ本格始動からコードフリーズまで 1ヶ⽉半しかない中でやれることはやった。 あとは年末年始を迎えるだけ...

Slide 42

Slide 42 text

3. そして、新年を迎える

Slide 43

Slide 43 text

© 2012-2026 BASE, Inc. 3. そして、新年を迎える 43 43 セール開始予約 2025/12/31 23:59:00 - 約7万件 2026/01/01 00:00:00 - 約5万件

Slide 44

Slide 44 text

© 2012-2026 BASE, Inc. 3. そして、新年を迎える 44 44

Slide 45

Slide 45 text

© 2012-2026 BASE, Inc. 3. そして、新年を迎える 45 45 結果: 顧客からの問い合わせなし、インシデントなし! 2025/12/31 23:59:00 - 約70,000件 → 176s 2026/01/01 00:00:00 - 約50,000件 → 160s

Slide 46

Slide 46 text

4. まとめ

Slide 47

Slide 47 text

© 2012-2026 BASE, Inc. 4. まとめ 47 47 検討したけど⾒送った案 ● バッチサーバーの増設やインスタンスサイズ変更 ● リアーキテクチャ済みの環境に載せ替える ● 商品テーブルから価格カラムを切り出す ● 時間が来たら価格が切り替わるようにする etc. 時間的制約から根本解決まではできず... (7万件で3分かかるので「イイハナシダナー」では終われない )

Slide 48

Slide 48 text

© 2012-2026 BASE, Inc. 4. まとめ 48 48 コードフリーズ中に調べていたこと DBのメトリクス⾊々 ● redo_log_flush / CommitLatency ● DiskQueueDepth急増 / Read/Write IOPS → Item->save()(= commit )のたびに、redo_log の disk への書き込み(flush)が発⽣していた → 年明けに対策をリリースしたら、逆に遅くなったのでリ バート ● Transaction分離レベル / Next Key Lock ● autocommit, innodb_flush_log_at_trx_commit=1 → Claude Code で根本対応を図る(進⾏中) Special Thanks

Slide 49

Slide 49 text

© 2012-2026 BASE, Inc. 4. まとめ 49 49 ● パフォーマンス改善は、計測‧仮説‧実装‧検証の サイクルを回す ● 「戦略の失敗は戦術で補うことはできない」(クラウゼビッツ) ○ 設計と実装も同じ。根本対応が必要になる時がいつか来る ○ ただ、機能作成当時の判断は尊重すべし ● ソフトウェアエンジニアは、ユーザーのペインを技術で解決 して、多くの⼈に⻑く使われるプロダクトに作り替え続け、 ビジネス成⻑に貢献する職種である