Webアプリケーションの パフォーマンス向上のコツ 実践編
-
Upload
masahiro-nagano -
Category
Internet
-
view
23.033 -
download
0
description
Transcript of Webアプリケーションの パフォーマンス向上のコツ 実践編
Webアプリケーションのパフォーマンス向上のコツ
実践編ISUCON夏期講習
2014/8/20Masahiro Nagano
午前3時ぐらいまで挑戦してみました
最終スコア
9246
やってみたことを紹介します
初期スコア
1664
(1) 環境整備
静的コンテンツをReverse Proxy で配信
Reverse Proxy: クライアントからの接続を受け、Applicationサーバに処理を中継する。画像,js,css などの静的コンテンツを返す役割もある
Application Server: ユーザからのリクエストを受けて適切なページを構築・レスポンスを行う
$ 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
スコア
1664 => 1719
Nginx 化
• オープンソースのWebサーバ。高速に動作し、メモリ使用量がすくないなどの特徴があります
Apache vs. Nginx
worker worker worker
worker worker worker
worker worker worker
リクエスト
コンテキストスイッチが大量発生
リクエスト
worker
1個のプロセスで効率よく通信を処理
$ sudo yum install nginx$ sudo service httpd stop
[program:nginx]directory=/command=/usr/sbin/nginx -c /home/isu-user/isucon/nginx.confautostart = true
command
run.ini
nginx.confはのちほど公開します
スコア
1719 => 1764
(2) Perl にしますワタシハパールチョットデキル
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.iniTCPではなくUNIX domain
socketを使う
プロセスを長生きさせる
プロセスはあげすぎない
TCPの接続は高コスト
ReverseProxy
AppServer
リクエスト毎にthree way handshake
スコア
1764 => 1891
(3) アプリをみよう
“/” “/recent/xxx”
“/memo/xxxx” “/mypage”
“/” “/recent/xxx”
“/memo/xxxx” “/mypage”
DBへの問い合わせが重い
markdown の変換にプロセス起動
DBへの問い合わせが若干重い
(4) 外部プロセス起動
+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”
スコア
1891 => 2233
(5) N+1 クエリ
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回ルーーーープ
“/”
use the join, luke
id user_id id name
memosテーブル usersテーブル
id user_id name
memos JOIN users ON memos.user_id = user.id
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”
スコア
2233 => 2398
(6) インデックス
SELECT * FROM memos WHERE is_private=0 ORDER BY created_at DESC LIMIT 100
id is_private
...
0
0
1
0
1
memosテーブル
id is_private
...
0
0
0
SORT
webapp/perl/lib/Isucon3/Web.pm
indexがないと
indexをつくる
cat <<'EOF' | mysql -u isucon isuconALTER TABLE memos ADD INDEX (is_private,created_at);EOF
init.sh
B-Tree
0 1is_private
created_at
older newer older newer
B-Tree
0 1is_private
created_at
older newer older newer
B-Tree
0 1is_private
created_at
older newer older newer
B-Tree
0 1is_private
created_at
older newer older newer
スコア
2398 => 2668
(7) タイトル生成
これ
mysql> show create table memos\G*************************** 1. row *************************** Table: memosCreate 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=utf81 row in set (0.00 sec)
mysql
titleカラムが存在しない!
<: $memo.content.split('\r?\n').first() :>webapp/perl/views/index.tx
splitでCPU使用contentの転送で通信
cat <<'EOF' | mysql -u isucon isuconALTER TABLE memos ADD COLUMN title text;UPDATE memos SET title = substring_index(content,"\n",1);EOF
init.sh
titleカラムの追加
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
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を取ってしまう
スコア
2668 => 3060
そして戦いは続く
Next Conan's HINT
“/mypage” のインデックス
以上。