テレビ通販、ECのAPIで短時間大量リクエスト・リアルタイム集計が必要だった際に
パフォーマンスを上げるため実装面で行った内容など
概要
- 単純な在庫数などを持っておらず、
ある納期までの販売において販売可能な数量(やセット品≒材料の概念)など
多少複雑な条件で都度INSERT、都度集計するやや複雑な仕組みの引当 - 基本的には量を捌くため単発の速度を上げる、1リクエストを高速に捌けるよう行った内容
(結果としては更新系で0.05秒未満) - 処理の各所にタイマーを仕掛けてボトルネックを調査、処理を調整していきました
- 環境:Webサーバーx2、DB1
- ネックになるのはDB
- Webサーバーも対策する
DB関連
同時実行数の制限
DBの高負荷対策。
参照系でも無制限にDBに同時処理させると速度低下を起こすため
同時に処理できる最大数を設ける
(速度があげるというより下がらないよう高負荷状態を回避しつつ処理速度が高い状態を保つ)
処理開始時にロックを取り、
ロックを取れなかった場合は0.5秒待機ののちリトライ(上限あり)
(どの程度まで同時処理できるかは負荷テストで要確認。
8コアでも8件、16コアでも9件程度を超えると遅かったため7件までで制限)
インデックスの再考
INSERT速度の低下も一応注意
Covering Index の検討
結果にあたる情報までインデックスに組み込み、SQLがインデックスで完結できるようにする
かなり早い
実装の再考
-
SQLの発行数そのものを抑える(オーバーヘッドが大きい)
-
一時テーブルの活用はありだが、
あまり綺麗ではないものの必要なデータをSQLで組み立ててしまい、
文字列の形でSQL内に直接突っ込むなどしてしまったほうがさらに高速
多少多くなっても全てをパラメータとして渡してしまうのが
SQL発行数もDB側の演算も抑えられて結果かなり速い
0.1秒が大きい差になる場面ではかなり違う。
-
非同期での処理の分離を検討する
ログ出力など直接必要ない&リアルタイムである必要もない、
最悪落ちても良いような処理などは投げっぱなしで結果も待たない、など。 -
遅い構文を避ける
- DBを使わずPHP側で行える処理はPHP側で行う
-
ORDERの代わりに自己結合などに置き換える
-
その他普通に計測と修正を繰り返す
Webサーバー関連
Apacheプロセス数の監視
処理が追いつかなかった場合を想定した、Webサーバーの高負荷対策。
Apacheのプロセスが貯まりすぎると固まり、
PHPの処理自体にも到達しなくなりNGレスポンスすら返せなくなる。
これを回避するため処理開始時点のプロセス数を確認し
閾値を超えている場合は処理することなく即レスポンスを返す
(閾値も負荷をかけて待機状態でも問題なく返せている件数を基に
瞬間的な大量リクエストの懸念があるため、かなり余裕を見て設定)
その他
データ量について
集計対象の件数増加はそれなりに速度に影響する(じわじわ遅くなる)
データ総数自体は1億レコード程度あってもほぼ影響はなかった
スペックアップについて
どちらかというと高負荷になったとき、どれくらい粘れるかはそこそこ変わります
(リクエストが減るまで堪え切れれば復帰できる可能性が高まる)
ただ先のとおりそもそも高負荷を回避する実装をしているため、速度面ではほぼ影響なし。
(明らかにリソースが足りてないなどでなければ、スペックアップしたところで
元々遅い処理がやたらと速くなるなどということはないと思われる。試した範囲では)
プロセス数や同時実行数の制限を上げられはするけど
大幅に増えるということもなく、積んだところであまり意味はなかった
さいごに
単発で早くても負荷テストで長時間走らせてみないと気づけないところもあるので
計測して試行錯誤ですね…