概要
高アクセス負荷への対策を真剣にやることになったときのために、これまでの経験 + 参考サイトの情報を列挙。
いずれ、どういったときにどれやるのが有効か?とか用語の掘り下げを詳細にまとめたい。
アクセスが集中するタイミング
- PUSH通知による誘致
- メディア放送による誘致
- 報酬がよいイベント期間(特にランキングイベントだと終了日時間近)
- 超短期間の無料ガチャ開催
Webサーバの負荷対策
- アクセス数自体をへらす(不変なデータはクライアント側でキャッシュしてもらう)
- スケールアウトさせる(=台数増やす)
- 実現:LB(ELBなど),DNSラウンドロビン利用して負荷分散した上で、必要なときだけスケールアウトするオートスケーリング機能を組み込む
- 注意すべき機能
- セッション: 毎回クライアントが異なるサーバーにアクセスすることになるので、セッション維持を工夫する必要あり(memcachedやRDBへ保存)
- クライアントからの画像アップロード: 各Webサーバーでなく、各Webサーバが参照する共通のストレージ(S3とか)へ
- ログ出力: オートスケーリング利用時は、各Webサーバのログが失われるため、Fluentdなどを利用して集約
- CDNを利用する
- 実現: CloudFrontなどの利用
DBサーバの負荷対策
DBアクセスを実際に行うアプリケーションサーバー側での努力
- DBアクセス回数を減らせるようにする(「DBサーバ群全体の通信回数・同時接続数・処理回数」を低減)
- 更新されないテーブルに対する(マスターデータなど)検索クエリの結果(頻度が高いもの)は、アプリケーションサーバ側でキャッシュする
- 処理に必要なレコード群はなるべく一括で取得する
- 関係するテーブル間レコードを取得する際はN+1問題を発生させないようにする
DBサーバー側での努力
- 同一テーブルを複数DBにまたがせる(「1DBサーバあたりの通信回数・同時接続数・処理回数」を低減、水平分割: 1DBサーバあたりのレコード数低減による検索の高速化、そもそも1DBあたりのレコード件数上限があるので、想定外のレコード数増加によるパンク防止の効果もある)
- 垂直分割: 同じテーブルに含まれるカラムを、更新頻度が高いもの・低いもので分割して複数DBにまたがせる
- 水平分割(シャーディング): 同じテーブルに含まれるレコードを、IDの範囲などで分割して複数DBにまたがせる
- こちらは、分割基準となるカラム以外での検索をするとなると、全DBへの検索が必要になってしまうことに注意(どのシャードに所属してるかわからないため。分割基準となるカラムとその分割アルゴリズムはアプリケーションサーバー側で把握している)
- 経験したスマホゲーだと、ユーザーデータ系テーブルで「user_idの値をシャード数で割った余り」で所属シャードを決めていた。これにより、同一ユーザーのユーザーデータ系のテーブルはすべて同じシャードに所属していたので、関連レコードの検索が容易にできていた印象。
- インデックスを張る(各テーブルにおける検索の高速化)
- 書き込み用の1台のDB(マスタ)と、読み込み用の複数DB(リードレプリカ)の構成にする(書き込み読み込みに関して「1DBサーバあたりの通信回数・同時接続数・処理回数」を低減)
- DB自体のキャッシュ機能を使う
運用段階での継続的な改善
- スロークエリの分析・改善など
参考