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

6192f8305c77cb9caa979b14fae75d24?s=47 akihito
September 08, 2018
8.3k

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

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

6192f8305c77cb9caa979b14fae75d24?s=128

akihito

September 08, 2018
Tweet

Transcript

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

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

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

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

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

  6. アジェンダ • ゲームの仕組み • ゲーム制作チームとプロジェクト • 高負荷に至った原因と対策対応 • リリース •

    マルチプレイ • アプリに仕込まれていた機能 • 想定外のアクセス上昇
  7. ゲームの仕組み

  8. 開発運営チーム • プロデューサー • ディレクター • プロジェクトマネージャー • レベルデザイナー •

    デザイナー • イラストレーター • クライアントアプリエンジニア • サーバエンジニア • インフラエンジニア • テスター • プロモーション • カスタマーサポート :
  9. ゲームの開発とは…. コスト周りの話などを少々

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

  11. ゲームシステムの構成要素 • スマホアプリ(iOS/Android) • アプリ内で保存管理するデータ • マスターデータ (キャラクター/クエスト/武器/装備) • 素材データ

    (画像/音声) • プレイヤー情報 (レベル/進行状況/入手キャラクター) • API (https) • リアルタイム通信 (websocket/photon)
  12. Webapp Server Realtime Server MasterData Image Sound

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

  14. 負荷の原因と対策

  15. 事例1 リリース時

  16. リリースまで 多くのチェックポイントを通過しリリースとなります • αテスト • βテスト • 予測流入数の算出 • 負荷試験

    • 障害試験 (Failover試験) • QA (インゲーム/アウトゲーム/課金試験) • 運用演習 (リリース/データ更新) : :
  17. リリース時サーバ構成 ・アプリケーションサーバ x N台 ・ DB Server Master x 1

    ・ Cache Server(Redis) x 1 ・管理用サーバ(ログサーバ兼用)
  18. 当初の予測流入数 • 他ゲームタイトルの実績 • βテストのKPI(継続率/ゲームサイクル) • 広告による流入見込み

  19. いよいよリリース

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

  21. 予測流入数を越えてきたので DB Server • DBのインスタンスタイプを変更しスケールアップ • Master x 1 →

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

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

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

  25. ログ集約の流れ WebApp Server Admin Server Log Server Redshi ft S

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

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

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

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

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

  32. 事例2 マルチプレイ

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

  34. None
  35. Webapp Server Realtime Server Room A Room B RoomAの状況を同期 Room

    A Room A Room B Room A Open or Close ? クライアントを介して RoomAの状況を伝える
  36. リリース数日経過… <fujiwara> 昨日のピークでredisだいぶヤバい <fujiwara__> どんなクエリが飛んでるんだろうなあ… <akihito> 昨日のピーク時、山というかビルみたいになっている

  37. None
  38. <acidlemon> keysが時間くってるんじゃん! <acidlemon> keysはredis殺すから本番じゃつかっちゃだめだよ!

  39. None
  40. Redisでkyesを使うと死ぬ

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

  42. Webapp Server Realtime Server Room A Room B Room A

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

  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
  45. 投入

  46. 対応:第2手 ・Itemが増加傾向なのでkeysもいずれ限界を迎える ↓ ・ZADD + ZRAGEBYSCORE ・SCOREにはepoch timeを利用して時間範囲検索

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

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

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

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

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

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

  54. 予想アクセス数 • 既存ユーザ数 • 新規ユーザ数 • 復帰ユーザ数 • イベントスケジュールの確認 •

    広告スケジュールの確認 • 短時間あたりの最大アクセス数を見積もる
  55. 負荷試験 • 短時間あたりの最大アクセス数を元に秒間を見積もる • 新規ユーザ/既存ユーザの比率をシュミレート • ユーザのプレイサイクルを再現したスクリプト(シナリオ)を準備 • 本番同等の環境を準備しスクリプトを実行する •

    サーバの状況をモニタリングし経過観察
  56. インフラ • インスタンスタイプ(アプリケーションサーバ/DB) • DBとRedisの最大接続数 • ネットワーク転送量 • Photonの最大同時接続数の確認 •

    マイクロサービスへの影響 • ELBの暖機
  57. 障害対応手順確認 エスカレーション DB キャッシュサーバ :

  58. 準備完了

  59. 施策実施1週間前 APIへのアクセスパターンが変わっている?! ↓ • 何故か重めのAPIが頻繁に呼ばれている • 基本的にはアプリ立ち上げ時に1度だけ呼ばれるはずだが

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

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

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

    できることは少なかった…
  63. 結果

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

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

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

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

  69. 完全に想定外の事態

  70. 原因はガチャでした

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

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

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

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

  75. ログから対応を判断 show processlist のログから以下を実施 • Closing tables/Opening tablesがstatusに頻出 • キャッシュヒットミスが起きている可能性

    • MySQLのtable_open_cache値を上げる • System Lockがstatusに頻出 • アイテムをカウントしているクエリをSlave参照に
  76. アイテムをカウントしているクエリ? SELECT COUNT(*) FROM user_item where type = 1; •

    施策前はレアアイテムだったので手持ちは数個ほどであった • ガチャ施策後一気に行数が増えて1000個を超えるようになった • 結果多くの行を読むクエリが誕生してしまった ↓ • 一部サーバ側でのカウントを停止し、クライアントアプリ側でのカウントを正とする
  77. slow log • min_examined_row_limit=1000を設定していた • レコード数増加によりslow log大量に出力 • 結果Opening Tables/System

    Lockが発生している可能性 ↓ min_examined_row_limit=3000に変更しログ出力を抑制
  78. トランザクションログ innodb_flush_log_at_trx_commit=2 一時しのぎではあるが…diskへの同期回数を1sync/sec

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

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

  81. 修正方針 • DB上でのアイテムの持ち方が良くない • アイテムN個 = Nレコード ↓ 要件的にN個を1レコードにまとめても問題ない あらゆる所に影響するので大規模改修が必要

  82. アイテム B アイテム A アイテム B アイテム B アイテム B アイテム A x 2 アイテム B

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

    アプリケーションのコードを新方式に頑張ってなおす
  84. 結果

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

    射幸心怖い
  86. まとめ • ソーシャルゲームでは常に想定外の自体が起こりうる • 予測流入数も所詮予測にしか過ぎない • 障害を防ぐことは不可能 • 想定障害のケースは準備しておく •

    想定外の事態にも対応できる余裕ある運用体制 • 過度に神経質にならない
  87. 是非フィードバックの方、よろしくおねがいします ご清聴ありがとうございました