Webアプリケーションの パフォーマンス向上のコツ 実践編

31,373 views

Published on

Published in: Internet

Webアプリケーションの パフォーマンス向上のコツ 実践編

  1. 1. Webアプリケーションの パフォーマンス向上のコツ 実践編 ISUCON夏期講習 2014/8/20 Masahiro Nagano
  2. 2. 午前3時ぐらいまで 挑戦してみました
  3. 3. 最終スコア 9246
  4. 4. やってみたことを 紹介します
  5. 5. 初期スコア 1664
  6. 6. (1) 環境整備
  7. 7. 静的コンテンツを Reverse Proxy で配信 Reverse Proxy: クライアントからの接続を 受け、Applicationサーバに処理を中継す る。画像,js,css などの静的コンテンツを返す 役割もある Application Server: ユーザからのリクエス トを受けて適切なページを構築・レスポン スを行う
  8. 8. $ cat /etc/httpd/conf.d/isucon.conf <VirtualHost *:80> DocumentRoot /home/isu-user/isucon/webapp/public RewriteEngine on RewriteCond REQUEST_URI !^/favicon.ico$ RewriteCond REQUEST_URI !^/(img|css|js)/ RewriteRule /(.*)$ http://localhost:5000/$1 [P] </VirtualHost> command
  9. 9. スコア 1664 => 1719
  10. 10. Nginx 化 • オープンソースのWebサーバ。高速に動 作し、メモリ使用量がすくないなどの 特徴があります
  11. 11. Apache vs. Nginx worker worker worker worker worker worker worker worker worker リクエスト コンテキストスイッチが 大量発生 リクエスト worker 1個のプロセスで 効率よく通信を処理
  12. 12. $ sudo yum install nginx $ sudo service httpd stop [program:nginx] directory=/ command=/usr/sbin/nginx -c /home/isu-user/isucon/ nginx.conf autostart = true command run.ini nginx.confはのちほど公開します
  13. 13. スコア 1719 => 1764
  14. 14. (2) Perl にします ワタシハパールチョットデキル
  15. 15. Perl の起動方法 command=/home/../isucon/env.sh carton exec -- start_server --path /tmp/app.sock -- plackup -s Starlet --max-workers 4 --max-reqs-per-child 50000 -E production -a app.psgi run.ini TCPではなくUNIXdomain socketを使う プロセスを長生きさせる プロセスはあげすぎない
  16. 16. TCPの接続は高コスト Reverse Proxy App Server リクエスト毎に threewayhandshake
  17. 17. スコア 1764 => 1891
  18. 18. (3) アプリをみよう
  19. 19. “/” “/recent/xxx” “/memo/xxxx” “/mypage”
  20. 20. “/” “/recent/xxx” “/memo/xxxx” “/mypage” DBへの問い合わせが重い markdownの変換に プロセス起動 DBへの問い合わせが 若干重い
  21. 21. (4) 外部プロセス起動
  22. 22. +use Text::Markdown::Hoedown qw//; sub markdown { my $content = shift; - my ($fh, $filename) = tempfile(); - $fh->print(encode_utf8($content)); - $fh->close; - my $html = qx{ ../bin/markdown $filename }; - unlink $filename; - return $html; + Text::Markdown::Hoedown::markdown($content) } webapp/perl/lib/Isucon3/Web.pm ここがmarkdownコマンドを 起動している “/memo/xxxx”
  23. 23. スコア 1891 => 2233
  24. 24. (5) N+1 クエリ
  25. 25. my $memos = $self->dbh->select_all( 'SELECT * FROM memos WHERE is_private=0 ORDER BY created_at DESC, id DESC LIMIT 100' ); for my $memo (@$memos) { $memo->{username} = $self->dbh->select_one( 'SELECT username FROM users WHERE id=?', $memo->{user}, ); } webapp/perl/lib/Isucon3/Web.pm 100回ルーーーープ “/”
  26. 26. use the join, luke
  27. 27. id user_id id name memosテーブル usersテーブル id user_id name memos JOIN users ON memos.user_id = user.id
  28. 28. my $memos = $self->dbh->select_all( 'SELECT memos.*,users.username FROM memos JOIN users ON memos.user = users.id WHERE memos.is_private=0 ORDER BY memos.created_at DESC, memos.id DESC LIMIT 100' ); webapp/perl/lib/Isucon3/Web.pm “/”,“/recent”
  29. 29. スコア 2233 => 2398
  30. 30. (6) インデックス
  31. 31. SELECT * FROM memos WHERE is_private=0 ORDER BY created_at DESC LIMIT 100 id is_priv ate ... 0 0 1 0 1 memosテーブル id is_priv ate ... 0 0 0 SORT webapp/perl/lib/Isucon3/Web.pm indexがないと
  32. 32. indexをつくる cat <<'EOF' | mysql -u isucon isucon ALTER TABLE memos ADD INDEX (is_private,created_at); EOF init.sh
  33. 33. B-Tree 0 1is_private created_at older newer older newer
  34. 34. B-Tree 0 1is_private created_at older newer older newer
  35. 35. B-Tree 0 1is_private created_at older newer older newer
  36. 36. B-Tree 0 1is_private created_at older newer older newer
  37. 37. スコア 2398 => 2668
  38. 38.  (7) タイトル生成
  39. 39. これ
  40. 40. mysql> show create table memosG *************************** 1. row *************************** Table: memos Create Table: CREATE TABLE `memos` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user` int(11) NOT NULL, `content` text, `is_private` tinyint(4) NOT NULL DEFAULT '0', `created_at` datetime NOT NULL, `updated_at` timestamp NOT NULL DEFAULT, PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=41311 DEFAULT CHARSET=utf8 1 row in set (0.00 sec) mysql titleカラムが存在しない!
  41. 41. <: $memo.content.split('r?n').first() :> webapp/perl/views/index.tx splitでCPU使用contentの転送で通信
  42. 42. cat <<'EOF' | mysql -u isucon isucon ALTER TABLE memos ADD COLUMN title text; UPDATE memos SET title = substring_index(content,"n",1); EOF init.sh titleカラムの追加
  43. 43. POST時に保存 $self->dbh->query(   'INSERT INTO memos (user, title, content, is_private, created_at) VALUES (?, ?, ?, ?, now()) ', $user_id, (split /r?n/, $content)[0], $content, $is_private, ); webapp/perl/lib/Isucon3/Web.pm
  44. 44. my $memos = $self->dbh->select_all( 'SELECT memos.id, memos.title, memos.is_private, memos.created_at, users.username FROM memos JOIN users ON memos.user = users.id WHERE memos.is_private=0 ORDER BY memos.created_at DESC, memos.id DESC LIMIT 100' ); webapp/perl/lib/Isucon3/Web.pm “/”,“/recent” memos.*だとcontentを 取ってしまう
  45. 45. スコア 2668 => 3060
  46. 46. そして戦いは続く
  47. 47. Next Conan's HINT
  48. 48. “/mypage”の インデックス
  49. 49. 以上。

×