isucon5 予選をphpで戦った話
TRANSCRIPT
ISUCON5 予選をPHPで戦った話
2015/10/28#phpstudy株式会社サイバーエージェント⽩白井 英
2
お前、誰よ• 株式会社 サイバーエージェント• SGE統括本部技術統括室 CTO• 白井 英• エンジニア• Twitter@goodoo• Bloghttp://ameblo.jp/goodoo• DQ10すぐちむ (FB392-435) プクリポ(旅芸人)
• ISUCONとは?• 準備• 何をしたか• 何が足らなかったか• まとめ
Agenda
ISUCONとは
・Webアプリケーションの 高速化コンテスト・1日かけてWebアプリケーションを チューニング・レギュレーション内であれば何でも可
・複数言語用意されていた(今回は、Ruby,Perl,Python,PHP)・ベンチマークツールのスコアで競う・1チーム3名以内(2名はいないとつらい・・・)
準備
前日までにしたこと・・
http://www.slideshare.net/kazeburo/isucon-yapcasia-tokyo-2015
スライドを熟読
チームで分担して準備する事を4つ決めた
1. コミュニケーションツール
2. レポジトリ
3. Wiki
4. GCPのプロジェクトの設定
そして当日・・
何をしたか
ルールの確認
予選通過予選は以下のルールで通過者が決定します。
1日目、2日目、それぞれで3000点に最も早く到達したチーム (ただし予選終了後の追試の対象には含まれます)それぞれの日で最後に提出した有効なスコアの上位4チーム上記10チームを除き、1日目と2日目を通した上位10チームまたこれとは別枠で、1日目と2日目を通した学生枠の上位5チーム
開始して最初の1時間
初期状態でベンチマークツールを走らせる・・・SCORE 100ぐらい(Ruby)
次にRuby -> PHPDBのバックアップ(dump)を実行
SCORE 800ぐらい
チーム内での一言「PHP優秀じゃん」
ちなみに後から考えてわかったことですが最初にDBのdumpをとったためDBのデータがキャッシュにのった関係でスコアが8倍のびました決してRuby->PHPが効いた訳ではありません
DBまわりのチューニングをはじめる
pt-query-digestをいれてもらう(そのあとNewRelicもいれてもらった)
my.cnfどこ問題
/etc/my.cnfを変更しても反映されない・・・とりあえずmysql> set global XXX=xxxで逃げるでもslow-queryのファイルが吐き出せない・・・
/etcの下をさがす・・
あった!/etc/mysql/mysql.conf.d/mysqld.cnf
・クエリーキャッシュをON・とりあえずメモリまわりチューニング - innodb_buffer_pool - xxxx_buffer_size ・php-fpmのプロセス数を1->10へ
あれ・・・
3,000スコア越えてる!
しかし・・・
10分差で3,000スコア1番乗りならず
もっと早くやっておけば・・
気を取り直して
pt-query-digestの結果を愚直に直す
・INDEXをはる -> スロークエリチェック のループ (カラム数が4つのテーブルに カラム数3つの複合インデックス read命みたいなものも作る・・)
結果、0.1sec以上のスロークエリーは0
遅いページは・・・・
/ HTTP/1.1" 200 17331 "-" "Isucon5q bench" 1.614
/TOPページがおもい!
ここまでで開始して
3時間くらい
プログラムを修正する決意!
PHPはSlimという
マイクロフレームワークでかかれていた
直した場所!
$stmt = db_execute('SELECT * FROM entries ORDER BY created_at DESC LIMIT 1000'); while ($entry = $stmt->fetch()) { if (!is_friend($entry['user_id'])) continue; list($title) = preg_split('/\n/', $entry['body']); $entry['title'] = $title; $entries_of_friends[] = $entry; if (sizeof($entries_of_friends) >= 10) break; }
この処理でis_friendをよんでますが(loop中に)中身は $user_id = $_SESSION['user_id']; $query = 'SELECT COUNT(1) AS cnt FROM relations WHERE (one = ? AND another = ?) OR (one = ? AND another = ?)'; $cnt = db_execute($query, array($user_id, $another_id, $another_id, $user_id))->fetch()['cnt']; return $cnt > 0 ? true : false;
です。
loop中にSQLをなげるのをやめた
もう1カ所loop中に
クエリーを投げている場所があったが直せず※問題の詳細が知りたい方はこちら(http://isucon.net/)
一旦今のベストスコアを作ってみる
※他のメンバーも諸々チューニングしてくれましたが自分のわかる範囲のみ書いてます
各種ログを止める・slowlog・NewRelic・xdebug
結果
何が足らなかったか
いろいろ足らないのは承知の上ですが
1つだけやっておけばよかったこと
こたえは最初にやったこと
DBのdump=(データの
キャッシュのせ)
正しくは次のパラメータinnodb_buffer_pool_dump_at_shutdown=ONinnodb_buffer_pool_load_at_startup=ON
バッファプールのダンプ、リストアを実施を行う設定
まとめ
オンライン予選 利用言語比率Ruby 43.2% 67組Python 15.5% 24組Golang 14.2% 22組Perl 14.2% 22組PHP 12.9% 20組Java 2.6% 4組Common Lisp 0.6% 1組
本選出場が決まった27チームRuby 37.0% 10組Perl 25.9% 7組Golang 22.2% 6組Python 11.1% 3組PHP 3.7% 1組未回答 7.4% 2組
頑張れPHP