Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ソーシャルゲームが高負荷に陥っているとき、何が起こっているのか

akihito
September 08, 2018
12k

 ソーシャルゲームが高負荷に陥っているとき、何が起こっているのか

builderscon 2018 の 9/7 セッションのスライドになります

akihito

September 08, 2018
Tweet

Transcript

  1. ソーシャルゲームが高負荷
    に見舞われる原因と対策
    builderscon 2018
    Takeda Akihito

    View Slide

  2. 自己紹介
    竹田 昭仁
    @takihito
    github.com/takihito

    View Slide

  3. 「つくる人を増やす」「面白く働く」

    View Slide

  4. 自己紹介
    面白法人カヤック
    技術部
    ゲーム事業部 (2013~

    View Slide

  5. 話すこと
    • スマホアプリでゲームが動く仕組み
    • 開発と運営チームの体制について
    • リリースから現在までに起きた問題と対策対応

    View Slide

  6. アジェンダ
    • ゲームの仕組み
    • ゲーム制作チームとプロジェクト
    • 高負荷に至った原因と対策対応
    • リリース
    • マルチプレイ
    • アプリに仕込まれていた機能
    • 想定外のアクセス上昇

    View Slide

  7. ゲームの仕組み

    View Slide

  8. 開発運営チーム
    • プロデューサー
    • ディレクター
    • プロジェクトマネージャー
    • レベルデザイナー
    • デザイナー
    • イラストレーター
    • クライアントアプリエンジニア
    • サーバエンジニア
    • インフラエンジニア
    • テスター
    • プロモーション
    • カスタマーサポート

    View Slide

  9. ゲームの開発とは….
    コスト周りの話などを少々

    View Slide

  10. 開発コスト
    ・開発期間は2〜3年以上
    ・その間にかかるコストは投資
    ・リリースして投資を回収できるようになる

    初期開発費の回収を達成 = リクープ

    View Slide

  11. ゲームシステムの構成要素
    • スマホアプリ(iOS/Android)
    • アプリ内で保存管理するデータ
    • マスターデータ (キャラクター/クエスト/武器/装備)
    • 素材データ (画像/音声)
    • プレイヤー情報 (レベル/進行状況/入手キャラクター)
    • API (https)
    • リアルタイム通信 (websocket/photon)

    View Slide

  12. Webapp
    Server
    Realtime
    Server
    MasterData
    Image
    Sound

    View Slide

  13. ゲーム特有のサーバ事情
    • 予測と結果
    • 構成要素と複雑さ
    • クライアントアプリ
    • 施策とアクセス状況の関係
    数年に渡り運用したタイトルに起きた事例を元に話します

    View Slide

  14. 負荷の原因と対策

    View Slide

  15. 事例1
    リリース時

    View Slide

  16. リリースまで
    多くのチェックポイントを通過しリリースとなります
    • αテスト
    • βテスト
    • 予測流入数の算出
    • 負荷試験
    • 障害試験 (Failover試験)
    • QA (インゲーム/アウトゲーム/課金試験)
    • 運用演習 (リリース/データ更新)


    View Slide

  17. リリース時サーバ構成
    ・アプリケーションサーバ x N台
    ・ DB Server Master x 1
    ・ Cache Server(Redis) x 1
    ・管理用サーバ(ログサーバ兼用)

    View Slide

  18. 当初の予測流入数
    • 他ゲームタイトルの実績
    • βテストのKPI(継続率/ゲームサイクル)
    • 広告による流入見込み

    View Slide

  19. いよいよリリース

    View Slide

  20. 実際の流入数
    予測流入数

    View Slide

  21. 予測流入数を越えてきたので
    DB Server
    • DBのインスタンスタイプを変更しスケールアップ
    • Master x 1 → Master x 1 / Slave x 2
    • DB SlaveはHAProxyにより分散
    WebApp Server
    • 既にMaster x Slaves構成を考慮し開発をしていた
    • 事前にピーク時最大アクセス数での負荷試験も終えていた
    • インスタンスを順次追加(当時はオートスケールではなかった)

    View Slide

  22. 想定内!!

    View Slide

  23. めでたしめでたし…とはなりませんでした
    <「Redshiftの容量が増加しています」

    View Slide

  24. 盲点…
    ココ→管理用サーバ(ログサーバ兼用)

    View Slide

  25. ログ集約の流れ
    WebApp
    Server
    Admin
    Server
    Log Server
    Redshi
    ft
    S
    3
    KPI batch
    集計遅延
    容量増加
    ログ投稿遅延

    View Slide

  26. 対策:管理サーバ(ログサーバ)
    • 過負荷によりログの投稿遅延が発生
    • インスタンスタイプを変更しスケールアップ
    • td-agentプロセスを用途別に分離(アクセスログ/行動ログ)

    View Slide

  27. 対応:ログ容量
    • Redshiftに行動ログがたまり続ける
    • 不要なログを削除
    • ループしていたログ出力クエリをまとめてポスト

    View Slide

  28. 対応:データの圧縮
    ・既存のテーブルカラムは未圧縮で定義されていた
    ・ANALYZE COMPRESSIONで既存テーブルを解析
    ・解析に従って圧縮定義(delta/lzo)したテーブルに移行

    View Slide

  29. 対応:集計遅延
    • テーブル設計ミス
    • 1カラムに大きめのデータを投入していた
    • データ圧縮したことで集計時間が改善

    View Slide

  30. View Slide

  31. 学び
    ログ集約はサービスに即座に影響が出るとこでは無いが…
    • リリース直後はサービス対応に時間がとられ後手に回る
    • 気軽にスケールアウトやスケールアップしにくい
    • 全体最適化の視点が欠けていた

    View Slide

  32. 事例2
    マルチプレイ

    View Slide

  33. マルチプレイ
    • リアルタイムサーバを介して通信
    • 複数ユーザがゲーム状況をリアルタイムで共有
    • 一人のユーザの操作が他ユーザにも伝わり同期される

    View Slide

  34. View Slide

  35. Webapp
    Server
    Realtime
    Server
    Room
    A
    Room
    B
    RoomAの状況を同期
    Room
    A
    Room
    A
    Room
    B
    Room A
    Open or Close ?
    クライアントを介して
    RoomAの状況を伝える

    View Slide

  36. リリース数日経過…

    昨日のピークでredisだいぶヤバい

    どんなクエリが飛んでるんだろうなあ…

    昨日のピーク時、山というかビルみたいになっている

    View Slide

  37. View Slide


  38. keysが時間くってるんじゃん!

    keysはredis殺すから本番じゃつかっちゃだめだよ!

    View Slide

  39. View Slide

  40. Redisでkyesを使うと死ぬ

    View Slide

  41. # 公開中のRoom一覧返す
    my @room_list = $self->redis->keys($self->publish_room . '*');
    シングルプロセスであるRedisでkyesを実行するヤバさ

    View Slide

  42. Webapp
    Server
    Realtime
    Server
    Room
    A
    Room
    B
    Room
    A
    Room
    A
    Room
    B
    KeyRoomA: open,
    members, ..
    KeyRoomB: close,
    members, ..
    keys
    keys
    keys

    View Slide

  43. 対応:第1手
    ・keysをサーバアプリケーションから剥がす
    ・常駐プロセスを立てkeysの定期実行結果をSetに保存
    ・サーバアプリケーションからはSetを参照

    View Slide

  44. Webapp
    Server
    Realtime
    Server
    Room
    A
    Room
    B
    Room
    A
    Room
    A
    Room
    B
    srandmemb
    er
    srandmemb
    er
    srandmemb
    er
    keys
    worker
    sad
    d

    View Slide

  45. 投入

    View Slide

  46. 対応:第2手
    ・Itemが増加傾向なのでkeysもいずれ限界を迎える

    ・ZADD + ZRAGEBYSCORE
    ・SCOREにはepoch timeを利用して時間範囲検索

    View Slide

  47. Webapp
    Server
    Realtime
    Server
    Room
    A
    Room
    B
    Room
    A
    Room
    A
    Room
    B
    srandmember/za
    dd
    srandmember/zad
    d
    srandmember/zadd
    zremrangebyscor
    e
    worker
    sad
    d

    View Slide

  48. 事前に露見しなかった?
    • βテストでは全くマルチプレイが使われていなかった
    • 負荷試験シナリオもマルチプレイの優先度を下げていた
    • レビュー検証不足
    • とにかく忙しかった
    βテストの結果を経てマルチプレイ報酬を手厚くしユーザ動線が変化
    マルチプレイが相当使われていたのは想定外

    View Slide

  49. 学び
    チーム内外のメンバーに助けられた
    • リリース直後ということで他でも火を吹いていた
    • 実際に目と手が足りていなかったので大変助かった
    • スピードとチームワークが結構楽しい

    View Slide

  50. (fujiwara)
    リアルISUCONはあれを見つけてから1時間で実装する必要がある…

    View Slide

  51. 事例3
    クライントに仕込まれていた機能

    View Slide

  52. 経緯
    運営も順調なので大規模な流入施策を打つことが決定
    施策実施前にサーバ側の検証と対策を行う

    View Slide

  53. 設計見直し
    アクセスログからalpを使って現況を分析
    新規ユーザの場合に既存ユーザ専用処理をスキップ

    View Slide

  54. 予想アクセス数
    • 既存ユーザ数
    • 新規ユーザ数
    • 復帰ユーザ数
    • イベントスケジュールの確認
    • 広告スケジュールの確認
    • 短時間あたりの最大アクセス数を見積もる

    View Slide

  55. 負荷試験
    • 短時間あたりの最大アクセス数を元に秒間を見積もる
    • 新規ユーザ/既存ユーザの比率をシュミレート
    • ユーザのプレイサイクルを再現したスクリプト(シナリオ)を準備
    • 本番同等の環境を準備しスクリプトを実行する
    • サーバの状況をモニタリングし経過観察

    View Slide

  56. インフラ
    • インスタンスタイプ(アプリケーションサーバ/DB)
    • DBとRedisの最大接続数
    • ネットワーク転送量
    • Photonの最大同時接続数の確認
    • マイクロサービスへの影響
    • ELBの暖機

    View Slide

  57. 障害対応手順確認
    エスカレーション
    DB
    キャッシュサーバ
    :

    View Slide

  58. 準備完了

    View Slide

  59. 施策実施1週間前
    APIへのアクセスパターンが変わっている?!

    • 何故か重めのAPIが頻繁に呼ばれている
    • 基本的にはアプリ立ち上げ時に1度だけ呼ばれるはずだが

    View Slide

  60. 重めのAPIとは
    ユーザの全情報を取ってくるAPI = UserInfo API

    View Slide

  61. うっかりクライアントに実装
    • ユーザデータは差分取得しアプリ内でmergeが基本
    • しかしユーザ全情報取得が手っ取り早い

    View Slide

  62. サーバ側で対策できるか
    アプリをバージョンアップする時間は無い
    • APIを研ぎ澄ます
    • Slave参照
    • キャッシュ
    • 心の準備
    できることは少なかった…

    View Slide

  63. 結果

    View Slide

  64. 学び
    サーバ側だけでの対応には限界がある
    • 今回は大事に至らなかった
    • アプリ側との認識不一致
    • API存在意図の伝承

    View Slide

  65. 事例4
    想定外のアクセス上昇

    View Slide

  66. 某日某時間
    過負荷によりオートスケールが発動
    アプリケーションサーバが次々と追加

    View Slide

  67. View Slide

  68. そして、ついに…
    DBが詰まりAPIが502を返し始める
    緊急メンテナンス突入

    View Slide

  69. 完全に想定外の事態

    View Slide

  70. 原因はガチャでした

    View Slide

  71. ガチャ引かれすぎ問題
    イベント概要
    • ユーザは手持ちポイント持て余していた
    • ポイントで引けるガチャを投入
    • ガチャでレア合成アイテムと限定キャラクターが入手
    プレイサイクルが施策後全く変わってしまった
    チームメンバーもガチャを引きまくってた

    View Slide

  72. 自分も引きまくってた

    View Slide

  73. 対応方針
    • 普段はそれほど過剰に叩かれるAPIでない
    • 取りえず効果がありそうな事は全部やってみる
    • なにが決定打であったか確証取れない

    View Slide

  74. 問題はDB
    • MySQL
    • Slow log
    • Masterでshow processlistを毎分発行済み

    View Slide

  75. ログから対応を判断
    show processlist のログから以下を実施
    • Closing tables/Opening tablesがstatusに頻出
    • キャッシュヒットミスが起きている可能性
    • MySQLのtable_open_cache値を上げる
    • System Lockがstatusに頻出
    • アイテムをカウントしているクエリをSlave参照に

    View Slide

  76. アイテムをカウントしているクエリ?
    SELECT COUNT(*) FROM user_item where type = 1;
    • 施策前はレアアイテムだったので手持ちは数個ほどであった
    • ガチャ施策後一気に行数が増えて1000個を超えるようになった
    • 結果多くの行を読むクエリが誕生してしまった

    • 一部サーバ側でのカウントを停止し、クライアントアプリ側でのカウントを正とする

    View Slide

  77. slow log
    • min_examined_row_limit=1000を設定していた
    • レコード数増加によりslow log大量に出力
    • 結果Opening Tables/System Lockが発生している可能性

    min_examined_row_limit=3000に変更しログ出力を抑制

    View Slide

  78. トランザクションログ
    innodb_flush_log_at_trx_commit=2
    一時しのぎではあるが…diskへの同期回数を1sync/sec

    View Slide

  79. メンテ終了サービス再開
    • 通常のイベント施策であればピークは1〜2時間
    • 今回はガチャが数時間以上に渡って引かれている
    • さらに障害を聞きつけてアクセス上昇
    • みなさま、学校やお仕事はどうしてるのでしょうか

    View Slide

  80. 対策
    時間稼ぎができたので、本格的な対策を行う

    View Slide

  81. 修正方針
    • DB上でのアイテムの持ち方が良くない
    • アイテムN個 = Nレコード

    要件的にN個を1レコードにまとめても問題ない
    あらゆる所に影響するので大規模改修が必要

    View Slide

  82. アイテム B
    アイテム A
    アイテム B
    アイテム B
    アイテム B
    アイテム A x 2
    アイテム B x 3
    旧方式:旧テーブル 新方式:新テーブル

    View Slide

  83. 修正作戦
    • 特定バージョン以降でアイテムの管理方法を新方式に変更
    • メンテナンス中に全ユーザのアイテムをバッチで変換し新テーブルに移動
    • 本番と同じDBを手配し変換検証を行う
    • 変換に失敗した場合に備えてクラスタを1セット用意しておく
    • アプリケーションのコードを新方式に頑張ってなおす

    View Slide

  84. 結果

    View Slide

  85. 学び
    ガチャは偉大な発明
    • 計測大切(show processlist /slowlog)
    • 設計変更 >チューニング
    • 射幸心怖い

    View Slide

  86. まとめ
    • ソーシャルゲームでは常に想定外の自体が起こりうる
    • 予測流入数も所詮予測にしか過ぎない
    • 障害を防ぐことは不可能
    • 想定障害のケースは準備しておく
    • 想定外の事態にも対応できる余裕ある運用体制
    • 過度に神経質にならない

    View Slide

  87. 是非フィードバックの方、よろしくおねがいします
    ご清聴ありがとうございました

    View Slide