「Mobageを支える技術」読了
Mobageを支える技術 ~ソーシャルゲームの舞台裏~ (WEB+DB PRESS plus)
- 作者: DeNA
- 出版社/メーカー: 技術評論社
- 発売日: 2012/06/13
- メディア: 単行本(ソフトカバー)
- 購入: 31人 クリック: 737回
- この商品を含むブログを見る
- わくわくしながら読める一冊ですね
Part1
- style float left/right の動作
- 左配置/右配置の指定
- clear:both で回り込みの解除
use XXX qw(:ALL)
モジュールの全関数をインポート- XSS
- POSTの内容にJavascriptを埋め込んで、ターゲットサイトへのCookieを自分のサイトに転送して盗む
- c.f. クロスサイト・スクリプティング(XSS)の具体例 | Webセキュリティの小部屋
- CSRF
- 自分のサイトからターゲットサイトへのリンクを貼り、正規のユーザがログインした状態のままクリックすることで正規のCookieを伴った命令を実行させ、内容を改竄する
- c.f. クロスサイト・リクエストフォージェリ(CSRF)と対策 | Webセキュリティの小部屋
- iOSのGPUアクセラレーション
- Apache mod_expires
- ファイルタイプごとにキャッシュ有効期間を指定
- HTTP Cache-Control
- no-cache : キャッシュが有効であるかどうかをサーバに確認した上で再利用しなければいけない、というフラグ
- no-store : キャッシュしてはいけない、というフラグ
- データのバイナリシリアライズ形式
- ProtocolBuffer
- MessagePack <-
- c.f. Protocol Buffersは遅い - Blog by Sadayuki Furuhashi
- Load Balancer DSR方式
- 応答パケットは直接返すので、DNAT方式に比べてLoad Balancerの負荷が低い
- ストリーミングサーバの場合などはその差が顕著
- Part3にて後述
Part2
- スレーブへの参照分散は、スレーブサーバのホスト名をDNSラウンドロビン
- DNSはMyDNSで構成変更にも柔軟に対応可能
- 非同期処理としてのジョブキュー
- FastCGI
- マルチプロセスのfork処理軽くない
- ipvsadmコマンド
- LVSの管理インターフェース
- httpdとソケットでFastCGIを複数接続
- Server::Starter Hot Deploy用モジュール
- サーバーの再起動中もリクエストの受信を継続する
- プロセスforkにより、ソケットを更新前と更新後のプログラムで共有し、更新後のプログラムの起動が完了した時点で更新前のプログラムをシャットダウンする
- c.f. Server::Starterから学ぶhot deployの仕組み - $shibayu36->blog;
- mysqldのswapだけは避けたい
- メモリの空き部分をファイルキャッシュが占めていた
- クリーンなキャッシュであれば、Swapが起きる前に解放してくれればいいものを、Linuxではそうはなっていない
- ファイルキャッシュの優先度を落とす、つまり早めに解放されるように指定するには、posix_fadvice()を使う
- ファイルディスクリプタに対してPOSIX_FADV_DONTNEEDを使うことで、近いうちに再度アクセスはしないことをアドバイスできる(メモリに載せておく必要性が十分低いことをLinux Kernelに伝えることができる)
- posix_fadvice()はfd単位なので、全体を対象としたいのであれば/proc/sys/vm/drop_caches(1〜3を指定)
- クリーンなページを解放
- NYTProf
- Perlプロファイラ
innodb_flush_method
- 指定無し
- data : open(),write+fsync
- log : open(),write+fsync
- O_DSYNC
- data : open(),write+fdatasync
- log : open(O_SYNC),write(+fsync) (内部的にはwriteしたときに同時にfsyncされるのと同じ)
- O_DIRECT
- data : open(O_DIRECT),write(ファイルシステムの機能を使わない書き込み)
- log : open(),write+fdatasync
- 指定無し
スレーブの再起動直後はバッファプールが空のため、徐々に参照頻度を上げていくような運用にしたい
- ストアドプロシージャなどでDBへのネットワークアクセスを一回でも少なくする
- マスタの更新は並列だが、レプリケーションのSQLスレッド(スレーブ上でリレーログを適用するスレッド)が単一スレッドのためレプリケーション遅延が発生する
SHOW SLAVE STATUS Seconds_Behind_Master
は秒単位(繰り上げ)- slave net timeout
- マスタの時刻をテーブルに登録することで、マスタの時刻をレプリケーションによってスレーブに通知することができ、その値とスレーブの時刻との差分を確認することで、時刻差を認識できる
- スロークエリログは実行中は出力されない、実行完了後に出力される
- 実行中のスロークエリは
SHOW FULL PROCESSLIST
で確認できる - Perconaのmk-killでKillだけじゃなくその出力が可能
- mysqladmin extended-status
- vmstatコマンド
- biがRead(ブロックデバイスから受け取ったブロック)で、boがWrite(ブロックデバイスに送ったブロック)
- iostatコマンドというのもある
- mutexが起こった時点のスタックトレースを取って、MySQLのソースコードに踏み込んで調べる
- LOAD DATAやALTER TABLEは処理が重いため、一時的にバイナリログを無効にし、マスター・スレーブを一つずつ実行していく
- バイナリログのRow formatは容量大きいが、SQLを実行しないので構文解析や実行計画が省ける
- PCIeのSSDは高価で高速
- マスタの更新処理は並列なのでHDDで大丈夫
- サービスでは使わない低スペックなスレーブを立てておいて、そのスレーブで早めに遅延を検知して、問題が大きくなる前に対処する
- 5.6からはデータベース毎にSQLスレッドが立てられる
- プリフェッチの効果
- SQLスレッドがリレーログを読む際のディスクのランダムリードを回避するために、SQLスレッドがまだ実行していないリレーログの更新・削除クエリをSELECTに変換して実行する
- それによりSQLスレッドが実行するより前にバッファキャッシュ上に準備しておくことができる
- c.f. Yoshinori Matsunobu's blog: Making slave pre-fetching work better with SSD
- レプリケーション遅延が許容できないときは、参照もマスタへ向けてみる
- DeNAの開発環境ではレプリケーションにスリープ処理をあえて入れて、運用環境に近づけた状態で開発を行っている
- スレーブの三層構造では、二層目がクラッシュしたときに、CHANGE MASTER TO でマスタのバイナリログのどの位置かを指定できない
- 安いSSDはプチフリーズに注意
- pdflush
- カーネルスレッド
- write()はメモリ上のページにダーティな印をつける
- pdflushが定期的(非同期)に、ダーティページをブロックデバイスに同期する
/proc/sys/vm/dirty_writeback_centisecs
が、pdflushの同期間隔を10ms単位で指定- c.f. Linux I/O のお話 write 編 - naoyaのはてなダイアリー
- MySQLのバイナリログとリレーログはフラッシュしない
- sync_binlog=1であればコミット毎にバイナリログがフラッシュ(fdatasync)されるが負荷が高い
- sync_binlog=0はフラッシュのタイミングをOSにまかせる
- xfs
- B+Tree構造の情報管理による検索高速化
- エクステントを単位としたブロック管理による、シーケンシャルな読み込み・書き込み性能の向上
- 遅延アロケーションによる、write()の高速化・フラグメンテーションの回避
- mmap
- ファイルをメモリにマッピングするもの
- UNIXのOSが行なっているページングやスワッピングを自プロセスで行なうようなもの
- c.f. コンピューター:C言語講座:mmapについて
- メモリに乗り切っているときはCPUバウンド
- MySQLの書き込みスケールアウトは、アプリケーションの中でデータベースを分けておく
- ジョインはシャーディングキーが同じ(つまり保存先のデータベースインスタンスが同じ)レコード同士であればこの場合でも可能
- 剰余方式のシャーディングであっても、冪乗でノードを増やしていけば既存シャードは不変のままスケールアウトが可能
- コンシステントハッシュ方式がマッピングの変更が少なく有利
- シャード追加時のマッピングの変更の即時性はマッピングテーブル方式が有利
- MySQL Cluster, MongoDB, Membase は分散型データベースの自動シャーディング
- ただし、リシャード(マッピングの変更によるデータのノード間移行)がなくなるわけではないので影響の検証は必要
- MHA for MySQLによる高速マスターフェイルオーバー
- ダウンタイム1秒前後!
- 一回のリクエストで複数のマスターへ更新することが多い
- 一部のマスターがフェイルオーバーしていて、かつロールバックするようなことになれば不整合となる
- サービス面から考えれば、不整合が発生してもユーザー側に有利になる順序でアプリケーショントランザクションを組んでおく必要がある
- 名前解決のキャッシュによりTTLの期限が切れるまで、IPが変更になるまでのタイムラグを回避するために、新旧のマスタのIPをDNSラウンドロビンとして登録
- 切り替えるまでは新マスタにユーザーを作らない?ユーザを削除する?(よくわからなかった...)
- max_connectionsを1にする
SET GLOBAL read_only=1
は実行中のセッションの完了を待つ
- マスタ障害時の自動フェイルオーバー
- もっとも進んだスレーブのリレーログを全てに反映する
- マスターと同期されていない場合はロストの可能性あり
- リレーログがトランザクションの途中で終わっていることがある
- その場合はコミット or ロールバックしてやる必要がある(全てのスレーブで実行位置をそろえる)
- マスターが死んだことを正確に検出しなければいけない
- もし自動フェイルオーバーしたあとでマスターが生きていたとなると、スプリットブレインとなり事態は深刻になる
- MHAではデフォルト3秒定期で接続確認、3回以上接続不能でフェイルオーバー実行
- 複数のネットワーク経由で確認した結果を採用するべき
- フェイルオーバーしたらマスターの電源を別の方法(ipmitoolなど)で落とす
Part3
- Admintool catalyst
- 故障でいうと、HDDが一番多い
- アプリケーションサーバの監視はポートのチェックだけでなく、アプリケーションの機能が動いているかまでチェックする
- 可用性を高めるためにヘルスチェックのみNGにできる機能をつけた
- ロードバランサの設定変更なしでサービスアウトが可能
- DSR構成は、ロードバランサの仮想IPをサーバのループバックインタフェースにも設定し、ロードバランサは宛先MACアドレスでサーバを割り振る
- サーバがLinuxの場合は仮想IP(ループバックインタフェース)に対するARPリクエストには応えない設定が必要
- c.f. 負荷分散装置:DSR(Direct Server Return)方式 - ぢろーらものおもちゃ箱:引っ越し後
- スパイクに対する閾値は、時間帯によりオフセットを変える必要がある
- 統計解析的なアプローチが必要
- データベースを分けて分散することを垂直分割とよぶ
- JOINに制限がかかることになる
- 水平分割はレンジやハッシュで分けることで、これがシャーディング
- mock化, fixture, MySQL::Sandbox...
- SIGNAL/RESIGNAL, ストアドプロシージャ
- トリガやイベントスケジューラから呼び出せる
SET GLOBAL event_scheduler = ON CREATE EVENT XXX ON SCHEDULE EVERY @interval HOUR STARTS @time DO BEGIN ... END
- イベントスケジューラの注意点
- エラーハンドリングができない
- バイナリログに書かれる
- SLAVESIDE_DISABLED
- memcached udf
- OAuth
- Facebook Graph API Explorer
- オーバーロードPOST
- TheSchwarts
- Perlのジョブキューライブラリ、キューの処理結果をクライアントに返すことはない
- Q4M
- MySQLのストレージエンジン
- INSERTはキューへのPUSHであり、queue_wait()しているワーカーがいればPOPされる
- queue_wait() -> オーナーモードへ、キューが空ならデフォルト60秒ブロックして待つ、返る値は指定したテーブルのインデックス値
- SELECT -> 先頭POP (ここで削除はしない)
- queue_end() -> 先頭削除
- オーナーモードでのINSERTは可能?
- Pararell::Preforkモジュール
- Perlのeval-$@はtry-catch構文です
- キューの処理に失敗した場合、その処理は異常終了となるだけ
- 少し遅延させたあとリトライするといったことができない
- そこでexponential back off
- 遅延時間を冪乗で増やしていくからexponential
- Perlの%hash+ +{...}は、波括弧でくくった部分がハッシュのリファレンスであることを明示するためのものです
scalar
は引数を強制的にスカラコンテキストで解釈してスカラ値を返す(配列に対して使うとlengthと同じ意味)ものです- Prefork workflow(hook point) : before_fork -> start -> after_fork -> on_child_reap
- Memcached
- Slab allocator
- 「Slab Allocatorの基本は確保したメモリをあらかじめ決められたクラスサイズに応じた固定長の固まりに分けて,フラグメンテーション問題を完全に克服するということです」
- c.f. 第2回 memcachedのメモリストレージを理解する:memcachedを知り尽くす|gihyo.jp … 技術評論社
- データの更新がある場合もlocalのexpiresを1秒など極端に短くしておくことでmasterを更新するだけですべてのサーバに1秒後には反映される?(よくわからなかった...)
- DNSパケットはUDP
- UserAgentモジュールのかわりにFurl
- DNSルックアップに任意の関数使える
- リスト11.13のコード
- DNSエントリーキャッシュの効率的活用
- ENGINE=MEMORY
- テーブルスキーマがなく、KVSのような使い方ができる
- クエリを集約して減らす
- 例えば友達リスト100件を20件づつ、アプリテーブルによるフィルタリングにかけていくと、結果として20より少なくなり、例えば10件になる
- ここでこの10件に対してクエリを実行するよりも、次のループに持ち越して集約した上でクエリを発行した方が良い
- 例えばインデックスが5でカウントが10の場合、10は[5,15]に収まるので、ループを続ける
- こうやってフィルタリングした結果を次のフィルタリング処理に渡すことで、発行するクエリ回数を減らすことができる
- このデザインはGroupedRangeを使って直感的に実装できる
- GroupedRangeの結果(つまりnextの結果)は、配列リファレンスがrange数分になるかundefになる
- undefで終了となる
return;
はundefを返す
- DBが別のため、アプリ経由で各DBにクエリ発行(ジョインが使えないため)
- ただし、結局フィルタリングするものだし、何万件もあるデータをアプリのメモリに載せたくない
- fetch_arrayrefを使えばやってくれる
- memcached get_multi
- $filtered_ids = []; 配列リファレンスはスカラです
- バグ早期発見のためのアプリケーション監視
- GrowthForecast
- この時点だとFluentdは初期
- Push型のリソース監視が望まれている、とのこと
- mackerelですね
- Data::Dumperの変数に任意の値を指定することで表示方法をカスタマイズできる
- ログは1出力を1行にすること
- Komainu
- 監視フレームワーク
- ShipIt
- テスト実行からタグ切りなど
- Archer
- perlのデプロイモジュール
- rubyのCapistrano