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

大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック

 大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック

OSC 2011 Hokkaidoの発表で使用したスライド資料です。
弊社が「ブラウザ三国志」や「英雄クエスト」といったゲームを、PHP+MySQLで構築してきた上で、身につけたノウハウや、注意すべき箇所、指針などをまとめた資料となっています。

Infiniteloop

July 12, 2023
Tweet

More Decks by Infiniteloop

Other Decks in Programming

Transcript

  1. 会社紹介 株式会社インフィニットループ 所在地: 札幌市中央区 設立: 2007年6月 人員: 27名(契約・アルバイト含む) 業務内容:ソーシャルゲーム開発 •

    もともと数人規模のよくあるウェブアプリケーション開発会社だったが、ブ ラウザゲーム開発をきっかけに、メイン事業を「ゲーム開発」「高負荷サ ーバサイド開発」にシフト • 絶賛人材募集中!! このスライドを見て興味を持たれた方は是非声を掛けてください
  2. 開発実績 [ [ [ [ブラウザ ブラウザ ブラウザ ブラウザ三国志 三国志 三国志

    三国志] ] ] ] 運営(株)AQインタラクティブ 開発ONE-UP(株) プログラム開発を(株)インフィニットループが担当 2009年7月正式サービス開始 公式のほか、mixi、Yahoo!モバゲー、ハンゲーム、 ニコニコアプリなど多くのプラットフォーム・チャネリングに対応、 海外にも展開中 [ [ [ [英雄 英雄 英雄 英雄クエスト クエスト クエスト クエスト] ] ] ] 運営・開発ONE-UP(株) サーバ側プログラム開発を(株)インフィニットループが担当 2010年10月正式サービス開始 Yahoo!モバゲーにて運用中
  3. 本日の内容 1. ソーシャルゲーム案件の特徴 2. 基本設計とポリシー 3. サーバとアプリの構成 4. なぜPHP+MySQLなのか 5.

    DBの多重化について 6. アプリ実装のポイント 7. DBのインデックスについて 8. DBのロックについて 9. ロック処理のコツ 10. KVSの利用
  4. ソーシャルゲーム案件の特徴 ・ 基本動作は、通常のウェブアプリケーション案件と同じ → HTMLとCSSで作られた画面の遷移 → FlashやJavaScriptによる処理 → DBへの登録・修正・削除 → 培った業務スキルは、そのまま流用出来る ・ 通常案件との違い → アクセス数や負荷が桁違いに大きいため、負荷対策が必要

    → 多ユーザの同時操作に耐えるため、ロック等はしっかりと → 開発規模が大きく、スピードが求められる   ・ 個人戦での限界、チーム開発は必須   ・ 開発だけではなくマネジメント的な要素も求められる   ・ 仕様書ありきの仕事ではない、コミュニケーションスキルが必要   ・ リリースしてからが本当の戦い    (感覚的にはリリース時点で、全体の60%くらい)
  5. 基本設計とポリシー(1) 破綻をきたさない設計が重要  【よくある失敗パターン】   1. とりあえず仕様を満たすように開発を進める   2. テストもパスしてオープンした   3. 利用者が増え、負荷が大きくなる   4.

    サーバ台数を増やすなどして対処するが、設計上の問題からボトルネック     発生し、ある一定数以上の接続数はどうしても捌くことができない   5. 慢性的に重い状態となり破綻 ※テストの段階で、本番と同等の負荷をかけるのはかなり難しい ※ゲームという性質上、利用人数の増加が読めない → サーバ台数を増やすことで、    処理能力を上げられる設計が重要
  6. 基本設計とポリシー(2) 負荷対策を考慮した設計が重要 ・ 負荷を「さばく」よりも、負荷を「かわす」方法を考える ・ データはとにかく溜めないこと  → データには保存期限を設ける  → DELETE文は遅いので、バッチ削除とかは無理が出てきやすい  → データを追加するときに、最も古いデータを消すなどの工夫が必要 ・

    厳密に扱う必要がないところは、どんどんルーズに  → 重要度が低く、変更が頻繁なデータは、DBに保存しない  → 例えばメールの新着チェックは、毎回行わずにn回に1回とする ・ ゲーム企画チームとのすり合わせがすごく大事  → 負荷との戦いは、ゲーム仕様を決めるところから始まっている  → 仕様書が来てから仕事開始ではない
  7. 基本設計とポリシー(3) 仕様はなるべく簡単に  → 面白さに影響が出ないのであれば、仕様は簡単な方が良い  → 仕様を限定しないと、のちのちサーバ負荷に限界が出てくる ※ ※ ※ ※バグ バグ バグ

    バグを を を を容認 容認 容認 容認しているわけではないです しているわけではないです しているわけではないです しているわけではないです 難しい仕様の例 簡単な仕様の例 全プレイヤーが1つのワールドでプレイ (分割は不可) ワールドで分けられる (ワールド単位でのサーバ分割が可能) 他者との関わりが多い (連携プレイが主のため、ユーザ単位での分割が難しい) 他者との関わりが少ない (ユーザID単位での分割が可能、排他ロックも不要) 二窓でのプレイを許可する (同時操作系のバグが起こりやすい) 二窓でのプレイができない/許可しない (同時操作系のバグが起こりにくい ※) アイテムに個数制限がある 例:武器Aは世界に10個まで (厳密なロック処理が必要) 個数制限はない (ロック処理は比較的ルーズでも大丈夫 ※) 処理結果が公開され、多くの人の目に触れる (処理に間違いが許されない) 処理結果が1度しか表示されず、1人にしか見えない (処理に多少の間違いがあっても気づかれない ※)
  8. サーバとアプリの構成(1) 使用している主なOSとソフトウェア ・ Linux ・ Apache ・ PHP ・ MySQL

    ・ memcached ・ (+ MongoDB) この構成でほとんどの ゲームに対応可能 ロードバランサ (Varnish) Webサーバ (Apache) Webサーバ (Apache) Webサーバ (Apache) DBマスター (MySQL) DBスレイブ (MySQL) DBスレイブ (MySQL) DBスレイブ (MySQL) DBアクセス レプリケーション HTTP振り分け HTTPアクセス バッチ処理サーバ キャッシュサーバ (memcached) ログサーバ (syslogd) ・ ・ ・ ・ ・ ・ 負荷 負荷 負荷 負荷に に に に応 応 応 応じて じて じて じて 台数 台数 台数 台数を を を を調整 調整 調整 調整
  9. サーバとアプリの構成(2) サーバ環境にはクラウドサービスを利用している。 ヒットするかどうかで、利用者数の変化の激しいゲーム案件は、 クラウドサービスとの相性が良い。 (可能ならDBマスターなど、要所にリアルサーバを併用できるとなお良い) ・ Amazon EC2  → 他と比べると安価な料金

     → コントロールパネルやコマンドから全て操作可  → オートスケーリングが利用可能  → 2011年春には、東京リージョンができ、通信レスポンスも早くなった ・ 国内クラウドサービス  → 料金ちょっと高め  → 日本語による手厚いサービス  → 業者によっては、インスタンスを足すのに、    営業に連絡して数日かかるなど、スピード感に欠ける
  10. なぜPHP+MySQLなのか ・ 高負荷での運用に、数多くの実績がある鉄板構成 ・ ネット上の情報も多く、実務に反映しやすい ・ チームマネジメント上もメリットが多い  → 人材確保が容易、教育も容易  → 開発スピードの向上に繋がる ・

    PHPのダメなところはコーディング規約でカバー ・ APCは必ず導入  → 試した範囲では、他の高速化エンジンよりもかなり早い ・ MySQLはなるべく新しいバージョンを
  11. DBの多重化について ・ MySQL標準のレプリケーション機能を使っての、 マスター/スレイブ構成が基本 ・ スレイブは数を増やせば良い、マスターの負荷軽減が課題となる ・ マスター分割は、ゲーム上の仕様も絡んできて、難易度も高い ・ レプリケーション遅延を防ぐ工夫をする

    → サイズがメモリに収まる範囲なら、スレイブはtmpfs上に展開 → 参照率をアプリ側で制御できるようにする ・ マスター/スレイブの使い分けには主に2つのアプローチがある → 最初マスターしか見ないように作って、    安全な部分から徐々にスレイブやKVSを見るように切り替えていく → 普段はスレイブ、トランザクション開始時にマスターに接続を切り替える ・ 「MySQL Cluster」、「XAトランザクション」ってどうなんだろう
  12. アプリ実装のポイント ・ フレームワークは使わないもしくは自作 → 既存フレームワークを使っても結局改造するはめに → 機能よりも速度重視 → Flashなどのクライアントアプリとの連携部は作り込んだ方がよい ・ O/Rマッパーは使用していない → 管理できないSQLは発行されないようにする → 自作のクエリビルダー的なものを作って利用している

    ・ アプリを書くときは処理スピードをそれほど意識しなくても大丈夫 → がんばって書いても、それほど負荷は下がらない → それよりもDBの負荷を下げるほうが効果的 → ただし共通処理(毎アクセス処理される箇所)は、気合を入れる → 大量の配列処理は遅いので注意 → Apacheログから重いページランキングを生成し、上から順に潰す
  13. DBのインデックスについて(1) ここが負荷対策の最大のキモ!! ・ インデックスとは → 本でいう索引のようなもの → 適切に利用すると、何十倍・何百倍も速度が変わることも ・ むやみに張ればいいというものではない → 索引を増やすと本が厚くなる(サイズが大きくなる) → INSERT,

    UPDATE, DELETE時にインデックスを    作り直すので、速度が低下する → インデックスサイズがメモリに収まるサイズを超えると、    その瞬間から大幅に速度が低下する ・ インデックスを適切に使うよう、SQLをチューニングしていく → インデックスを張っても、SQLの書き方によっては使われない → EXPLAINで確認しながら、チューニングしていく
  14. DBのインデックスについて(3) インデックスを使う上での主な注意点 ・ 「!=」、「<>」はインデックスが使用できない ・ LIKE検索時、前方一致以外では、インデックスは使用できない ・ ORや範囲検索(不等号)はインデックスが使用できないことがある ・ ORDER

    BYは、インデックスを使用できないパターンが多数ある ・ とにかくEXPLAINが大事、MySQLのスロークエリログも参考になる → プロジェクト内の全てのプログラマに周知徹底を! 参考: インフィニットループ技術ブログ ソーシャルゲーム開発者なら知っておきたい MySQL INDEX + EXPLAIN入門      http://www.infiniteloop.co.jp/blog/2011/03/mysql-index-explain/
  15. DBのロックについて(1) これをしっかりやらないと、リリース後バグだらけに!! ・ ロックはとても大事 → ロックをしっかりかけていないと、連打や同時操作でバグが起こる → テスト環境では、再現しないことも多いのでやっかい ・ 行ロックを使う、テーブルロックは使っていない ・ ストレージエンジンには「InnoDB」、

    分離レベルには、「REPEATABLE READ」を使っている ・ ロックの挙動は複雑で、しっかり理解することが重要 ・ 特に行ロック時に、インデックスが使われなかった場合は、 テーブルロックがかかってしまうため注意が必要
  16. 行ロックの挙動 例1: 存在するレコードを行ロックした場合 DBのロックについて(3) Gap(Infimum) 1 Gap 2 Gap 5 Gap(supremum)

    SELECT * FROM t WHERE c = 2 FOR UPDATE SELECT * FROM t WHERE c = 2 FOR UPDATE SELECT * FROM t WHERE c = 2 FOR UPDATE SELECT * FROM t WHERE c = 2 FOR UPDATE L DELETE FROM t WHERE c = 2 INSERT INTO t (c) VALUE(2) × × 参考: ・インフィニットループ社内勉強会資料 佐々木 亨基 MySQL InnoDB によるロック処理入門  ※近日公開予定      ・技術評論社  奥野幹也 著 エキスパートのためのMySQL運用+管理トラブルシューティングガイド ※ 例は全てMySQL5.1系で検証
  17. ギャップロックの挙動 例2: 存在しないレコードを行ロックした場合 DBのロックについて(4) Gap(Infimum) 1 Gap 2 Gap 5 Gap(supremum)

    SELECT * FROM t WHERE c = 4 FOR UPDATE SELECT * FROM t WHERE c = 4 FOR UPDATE SELECT * FROM t WHERE c = 4 FOR UPDATE SELECT * FROM t WHERE c = 4 FOR UPDATE L SELECT * FROM t WHERE c = 3 SELECT * FROM t WHERE c = 3 SELECT * FROM t WHERE c = 3 SELECT * FROM t WHERE c = 3 FOR UPDATE FOR UPDATE FOR UPDATE FOR UPDATE INSERT INTO t (c) VALUE(4) INSERT INTO t (c) VALUE(4) INSERT INTO t (c) VALUE(4) INSERT INTO t (c) VALUE(4) レコード レコード レコード レコードが が が が存在 存在 存在 存在しないため しないため しないため しないため、 、 、 、ギャップ ギャップ ギャップ ギャップが が が がロック ロック ロック ロックされる される される される INSERT INTO t (c) VALUE(3) INSERT INTO t (c) VALUE(3) INSERT INTO t (c) VALUE(3) INSERT INTO t (c) VALUE(3) × × ◦ ◦ ◦ ◦
  18. デッドロックの挙動 二つ以上のセッションが互いの処理待ちとなり、各セッションの処理が進まなくなった状態 例3: ロックして存在確認後、レコードがなければインサートする ※ t に c < 200 のデータしか無い場合

    DBのロックについて(5) A A A A B B B B SELECT * FROM t WHERE c = 200 FOR UPDATE SELECT * FROM t WHERE c = 200 FOR UPDATE SELECT * FROM t WHERE c = 200 FOR UPDATE SELECT * FROM t WHERE c = 200 FOR UPDATE SELECT * FROM t WHERE c = 300 FOR UPDATE SELECT * FROM t WHERE c = 300 FOR UPDATE SELECT * FROM t WHERE c = 300 FOR UPDATE SELECT * FROM t WHERE c = 300 FOR UPDATE INSERT INTO t (c) VALUE(200) INSERT INTO t (c) VALUE(200) INSERT INTO t (c) VALUE(200) INSERT INTO t (c) VALUE(200) INSERT INTO t (c) VALUE(300) INSERT INTO t (c) VALUE(300) INSERT INTO t (c) VALUE(300) INSERT INTO t (c) VALUE(300) これがデッドロックになる
  19. ロック処理のコツ ・ MySQLのロック処理は複雑で、新米プログラマを含めた全員が、 完全に理解するのは難しい(INSERTが絡むと特に複雑) ・ 基本指針を示し、難しい箇所はリードエンジニアがレビューで対応 ・ 基本は、「トランザクション開始後に、すぐ更新対象をロック」 ・ ロック時にインデックスが使用されていることをEXPLAINで確認

    ・ 値を加算/減算をするときは、なるべくSQLの中で行う → UPDATE example_tbl SET a = a + 1 WHERE foo = ‘bar’; ・ ロックで取ったもの以外、値が正しいことを信じない → スレイブ遅延やファントムリードに気をつける ・ デッドロックは、ロック範囲の見直しと、リトライで対処する ・ MySQLの気持ちになって考えるのが大事 とても書き切 れないので、 近日中に弊社 技術ブログで 別途資料を 公開予定
  20. KVS(キーバリューストア)の利用 ・ KVS(memcachedなど)は、一時期積極的に利用したが、 今は一部での利用に留めている ・ 開発やテストのコストがかかる → キャッシュのクリア漏れによるバグを生む → トランザクション中にKVSのデータを扱うのは難しい ・ システム全体のトータルコストで考える

    → KVSから値を取得し、それをアプリ側で結合するくらいなら、    MySQLのJOINの方が早い ・ 使うところにはもちろん使う → PHPのセッション管理 → 変更が入らないマスタテーブル類のキャッシュ → 読み込み回数が著しく多い箇所
  21. まとめ ・ ゲーム案件であっても、特に変わったことはしておらず、 基本が大事、基礎に忠実に淡々と ・ なるべく負荷をかけないゲーム仕様になるようすり合わせを ・ SQLチューニングとインデックスの見直しが 負荷対策の中核 ・

    ロックは大事、しっかりとした理解が必要 ・ キーバリューストアは、全体のコストを考えて 使いどころを検討して使う ・ MySQL(というかデータベース)は本当に難しい!! → オレたちは、このMySQL坂を登りはじめたばかり
  22. おまけ(2) アルバイト募集のお知らせ 株式会社インフィニットループでは、 アルバイトも募集しています。 ・ プログラマ見習い ・ テスター(プログラムのテスト、バグ検証) ・ その他雑務

    せっかくプログラムを書けるスキルを持っているのに、 コンビニ等でバイトするのは勿体ない!! 学生歓迎、ノートPC貸与 興味のある方は、直接声を掛けていただくか、 HPからお問い合わせください