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

  • View
    23.034

  • Download
    0

  • Category

    Internet

Preview:

DESCRIPTION

 

Citation preview

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

そして戦いは続く

“/mypage” のインデックス

以上。

Recommended