SaaS型ECプラットフォームにおける高負荷対策株式会社インターファクトリー CTO水野 謙
View Slide
自己紹介 略歴 2006年 日本IBM 東京基礎研究所 入社 マルチコア環境におけるJavaの高速化 設計書(Word, Excel)の品質検証ツールの作成、適用 2013年 インターファクトリー 入社 2016年 CTO就任 興味 パフォーマンスチューニング
今日話すこと 下記のような特徴をもつサービスでの高負荷時の課題と解決策EC:瞬間的な高負荷がある(セール、SNS拡散)共用環境:同一サーバーで複数の店舗が稼働拡張性:店舗により負荷傾向が異なるJava, Tomcat, Linux, PostgreSQL
先に結論 各サーバーの、各リソースについて、店舗ごとの閾値を設ける 閾値を超えたときに、より「マシな」エラーの出し方をする
サービスの特徴:拡張性と最新性の両立ASP ebisumart パッケージ拡張性 △ 〇・標準のJavaクラスを拡張してカスタマイズを記述・API経由でカスタマイズ〇最新性 〇 〇カスタマイズしていても標準機能は自動でアップデート×
共用環境最大の課題:他店舗影響 ある店舗でアクセスが集中したことにより他の店舗も遅くなってしまう ある店舗のカスタマイズロジックのパフォーマンスバグにより、他の店舗も遅くなってしまうパフォーマンスバグがあるのは悪いことそれにより他店舗影響が出るのは最悪なこと
アプリケーションサーバーのリソース制御
解決策1:スレッド数制限 ある店舗のリクエストを処理しているスレッド数が閾値を超えたら、それ以上は受け付けない処理が滞留したら確実に制御が発動するSLA(秒間PV)未満でも発動する可能性がある
解決策2:秒間PV制限 ある店舗にSLA以上のアクセスが発生したらそれ以上は受け付けない店舗管理者への説明がしやすいSLA(秒間PV)未満でもサーバーが高負荷になることはある⇒「最悪なこと」を防げない
ここまでの対策で十分?以下のようなサービスならたぶん十分⚫ SNS⚫ Web検索以下のようなサービスでは不十分⚫ EC⚫ 東京オリンピックのボランティア申し込み
ここまでの対策で十分?以下のようなサービスならたぶん十分⚫ SNS⚫ Web検索以下のようなサービスでは不十分⚫ EC⚫ 東京オリンピックのボランティア申し込みリクエスト単位でほぼ目的達成複数のリクエストを経て目的達成
ECシステムの難しさ セッションレスなプロトコルユーザーの離脱を正確に判定できないSLAは秒間PVで定義 セッションフルなアプリケーション複数の通信を経て目的達成完了直前のエラーはユーザー体験として最悪
ユーザー体験として「マシな」アクセス制御 新規ユーザーは混雑時に一定の確率でエラー 回遊中ユーザーはエラーにはなりにくい 「回遊中ユーザー」の定義は? 一回でもアクセスしたら カートに商品を投入したら 注文情報入力画面を表示したら 離脱の判定方法は? 一定期間アクセスがなかったら ブラウザのタブを閉じたら
具体的な制御の方法 新規ユーザーだった場合 回遊中ユーザー数が閾値以上ならエラー そうでなければ、回遊中ユーザー一覧として登録 回遊中ユーザーだった場合 最終アクセス日時から一定時間以上たっていたら新規ユーザーとして処理 そうでなければ、最終アクセス日時を更新制限発動時のユーザー体験が比較的良いPVやサーバー負荷が低くても制限が発動する場合がある適切な閾値の設定が難しい
代替案:二段階の秒間PV制限(未採用) 1秒間に一定のリクエスト数までは無条件に受け付ける それを超えた場合、回遊中ユーザーからのアクセスのみ受け付ける閾値を決めやすい 高負荷時も一定の新規ユーザーを受け付ける⇒回遊中ユーザーにエラーを表示する可能性が増える
まとめ:アプリケーションサーバーのリソース制御 スレッド数制限⇒サーバーを守るため 秒間PV制限⇒SLAに関する説明のしやすさのため 回遊ユーザー数制限⇒ユーザー体験をよくするため
DBサーバーのリソース制御
解決策1:接続数制限 店舗ごとにコネクションプールを用意し、接続数上限を設定技術的に簡単 負荷が低くても制限が発動する
ケースA:他店舗の負荷状況の違い 1店舗のみの負荷の場合店舗A: 1/10コネクション利用店舗B:10/10コネクション利用 複数店舗同時の負荷の場合店舗A:10/10コネクション利用店舗B:10/10コネクション利用低負荷なのに制限発動高負荷で制限発動
解決策2:二段階の接続数制限 一定数までは無条件に確保 それを超えた場合、全店舗の接続数合計が閾値以下ならコネクション追加 プール上限に達したら拒絶 参考:店舗共通のプールにしない理由 他店舗影響防止 DBサーバーのメモリ使用量削減
ケースB:コネクションを保持しても使っていない クエリを投げた後、アプリサーバーで処理商品一覧画面:商品ごとの金額計算購入処理:トランザクション保持中に多数の処理 単にコネクションを解放していないフレームワークにてメソッド末尾で解放複数処理で単一コネクションを使いまわしこまめに開放すると、今度はコネクション取得のオーバーヘッドがかかる
解決策3:アクティブ接続数制御 「クエリを投げて結果待ち」の状態の接続数をカウント 一定数を超えたらそれ以上クエリを投げない他のクエリが終わるまで待つ待っている数が処理が多ければエラー
ここまでの対策で十分?例:受注処理1. 入力値のチェック2. 決済連携3. 在庫更新&受注登録4. メール送信
ここまでの対策で十分?⇒障害を防ぐための制限で障害発生例:受注処理1. 入力値のチェック2. 決済連携3. 在庫更新&受注登録4. メール送信制限発動でエラー決済連携済みなので手動リカバリーが必要
解決策4:リクエスト開始時のチェック 接続数・アクティブ接続数を、リクエスト処理開始時にもチェック その時点で閾値を超えていたら、処理を開始せずにエラー画面を表示
まとめ:DBサーバーのリソース制御 2段階の接続数制限⇒複数店舗間の負荷制御 アクティブ接続数制限⇒「実際にDB負荷となる接続」の数の制御 リクエスト開始時のチェック⇒致命的なタイミングでのエラーを防ぐ
今日話したこと 下記のような特徴をもつサービスでの高負荷時の課題と解決策EC:瞬間的な高負荷がある(セール、SNS拡散)共用環境:同一サーバーで複数の店舗が稼働拡張性:店舗により負荷傾向が異なるJava, Tomcat, Linux, PostgreSQL
結論 各サーバーの、各リソースについて、店舗ごとの閾値を設ける アプリケーションサーバー、 DBサーバー PV数、スレッド数、ユーザー数、etc. 閾値を超えたときに、より「マシな」エラーの出し方をする 回遊中ユーザーは優先 エラーはなるべく処理の最初に