SlideShare a Scribd company logo
1 of 105
Download to read offline
ISUCONで学ぶ 
Webアプリケーションの 
パフォーマンス向上のコツ 
実践編 完全版 
ISUCON夏期講習 
2014/8/20 
Masahiro Nagano
この資料を読む前に 
以下の記事をお読みください 
http://blog.nomadscafe.jp/2014/08/isucon-2014-ami.html
チューニングにあたり@acidlemon さんの 
blog記事を参考にしています 
「ざっくりと #isucon 2013年予選問題の 
解き方教えます」 
http://isucon.net/archives/32976287.html
挑戦してみました
最終スコア 
9079
やってみたことを 
紹介します
初期スコア 
1664 
ruby実装にて
(1) 環境整備
静的コンテンツを 
Reverse Proxy で配信 
Reverse Proxy: クライアントからの接続を 
受け、Applicationサーバに処理を中継す 
る。画像,js,css などの静的コンテンツを返す 
役割もある 
Application Server: ユーザからのリクエス 
トを受けて適切なページを構築・レスポン 
スを行う
/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>
スコア 
1664 => 1719
Nginx 化 
• オープンソースのWebサーバ。高速に動 
作し、メモリ使用量がすくないなどの 
特徴があります
Apache vs. Nginx 
リクエスト 
worker worker worker 
worker worker worker 
worker worker worker 
コンテキストスイッチが 
大量発生 
リクエスト 
worker 
1個のプロセスで 
効率よく通信を処理
command 
$ sudo yum install nginx 
$ sudo service httpd stop 
run.ini 
[program:nginx] 
directory=/ 
command=/usr/sbin/nginx -c /home/isu-user/isucon/ 
nginx.conf 
autostart = true 
nginx.conf: https://gist.github.com/kazeburo/7b0385cce1b0a4565581
スコア 
1719 => 1764
(2) Perl にします 
ワタシハパールチョットデキル
Perl の起動方法 
run.ini 
TCPではなくUNIX domain 
socketを使う 
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 
プロセスを長生きさせる
TCPの接続は高コスト 
Reverse 
Proxy 
リクエスト毎に 
three way handshake 
App 
Server
スコア 
1764 => 1891
(3) アプリをみよう
“/” “/recent/xxx” 
“/memo/xxxx” “/mypage”
“/” “/recent/xxx” 
DBへの問い合わせが重い 
“/memo/xxxx” “/mypage” 
markdown の変換に 
プロセス起動 
DBへの問い合わせが 
若干重い
(4) 外部プロセス起動
webapp/perl/lib/Isucon3/Web.pm 
“/memo/xxxx” 
+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) 
} 
ここがmarkdownコマンドを 
起動している 
XS(C)で高速にmarkdownを 
処理するモジュール
スコア 
1891 => 2233
(5) N+1 クエリ
webapp/perl/lib/Isucon3/Web.pm 
“/” 
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}, 
); 
} 
100回ルーーーープ
use the join, luke
memosテーブルusersテーブル 
id user_id id name 
memos JOIN users ON memos.user_id = user.id 
id user_id name
webapp/perl/lib/Isucon3/Web.pm 
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' 
); 
“/”, “/recent”
スコア 
2233 => 2398
(6) インデックス
indexがないと 
SELECT * FROM memos WHERE is_private=0 ORDER BY 
created_at DESC LIMIT 100 
memosテーブル 
id is_priv 
ate 
... 
0 
0 
1 
0 
1 
id is_priv 
ate 
... 
0 
0 
0 
ソート 
webapp/perl/lib/Isucon3/Web.pm 
抽出 
CPU負荷高い
indexをつくる 
init.sh 
cat <<'EOF' | mysql -u isucon isucon 
ALTER TABLE memos ADD INDEX (is_private,created_at); 
EOF
B-Tree 
is_private 0 1 
created_at 
older newer older newer
B-Tree 
is_private 0 1 
created_at 
older newer older newer
B-Tree 
is_private 0 1 
created_at 
older newer older newer
B-Tree 
is_private 0 1 
created_at 
older newer older newer
B-Tree 
is_private 0 1 
created_at 
older newer older newer 
順に取得するだけ
スコア 
2398 => 2668
(7) タイトル事前生成
これ
mysql 
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) 
titleカラムが存在しない!
タイトルは本文から 
都度生成 
webapp/perl/views/index.tx 
<: $memo.content.split('r?n').first() :> 
contentの転送で通信splitでCPU使用
titleカラムの追加し、 
init.sh 
事前生成 
cat <<'EOF' | mysql -u isucon isucon 
ALTER TABLE memos ADD COLUMN title text; 
UPDATE memos SET 
title = substring_index(content,"n",1); 
EOF
POST時にも生成 
webapp/perl/lib/Isucon3/Web.pm 
$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' 
); 
“/”, “/recent” 
memos.* だと contentを 
取ってしまう
スコア 
2668 => 3060
(8) OFFSET = 破棄
”/recent/100” 
100ページ目 
SELECT * FROM memos ORDER 
BY created_at LIMIT 100 
OFFSET 10000 
とても大きなOFFSET
MySQLのOFFSET処理のイメージ 
1 2 3 4 
id title user ... . id title user ... . id title user ... . id title user ... . 
5 6 7 8 
id title user ... . id title user ... . id title user ... . id title user ... . 
9 10 11 12 
id title user ... . id title user ... . id title user ... . id title user ... . 
13 
id title user ... . 
10000 
id title user ... . 
10001 10002 10003 10004 
id title user ... . id title user ... . id title user ... . id title user ... .
MySQLのOFFSET処理のイメージ 
頑張ってソート 
1 2 3 4 
id title user ... . id title user ... . id title user ... . id title user ... . 
5 6 7 8 
id title user ... . id title user ... . id title user ... . id title user ... . 
9 10 11 12 
id title user ... . id title user ... . id title user ... . id title user ... . 
13 
id title user ... . 
10000 
id title user ... . 
10001 10002 10003 10004 
id title user ... . id title user ... . id title user ... . id title user ... . 
必要な個数まで到達
MySQLのOFFSET処理のイメージ 
頑張ってソート 
1 2 3 4 
id title user ... . id title user ... . id title user ... . id title user ... . 
5 6 7 8 
id title user ... . id title user ... . id title user ... . id title user ... . 
9 10 11 12 
id title user ... . id title user ... . id title user ... . id title user ... . 
13 
id title user ... . 
10000 
id title user ... . 
10001 10002 10003 10004 
id title user ... . id title user ... . id title user ... . id title user ... . 
必要な個数まで到達 
廃棄
MOTTAINAI
捨てるデータを減らす
取得するデータを制限 
SELECT id FROM memos 
ORDER BY created_at LIMIT 100 
OFFSET 10000
MySQLのOFFSET処理のイメージ 
1 2 3 4 5 6 7 8 9 10 11 12 13 
id id id id id id id id id id id id id ・・・・・ 
・・・・・ 
9999 
10000 
id id 
10001 10002 10003 10004 
id id id id
MySQLのOFFSET処理のイメージ 
1 2 3 4 5 6 7 8 9 10 11 12 13 
id id id id id id id id id id id id id ・・・・・ 
10001 10002 10003 10004 廃棄 
・・・・・ 
9999 
10000 
id id 
id id id id 
読むデータも、捨て 
るデータも少ない
“id” だけにすると 
高速になるもう一つの理由 
“Covering Index”
MySQLのインデックスと 
データの持ち方 
title user ... title user ... 
user ... PRIMARY title user ... 
title user ... 
title user ... 
title user ... title user ... title KEY 
CLUSTERED INDEX 
リーフノードに 
データを含む 
small large 
id 
id 
id 
id 
id 
id 
id 
id
MySQLのインデックスと 
データの持ち方 
SECONDARY KEY 
primary keyじゃないkey 
リーフノードに 
PRIMARY KEYが含まれ、 
データはCLUSTERED INDEX 
から取得 
is_private 
id id id id id id id id 
created_at 
older newer older newer
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT * の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at 
何度も繰り返す
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at 
indexだけで 
探索が終わる
SELECT id の場合 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
title user ... . 
PRIMARY KEY 
id 
id 
id 
id 
id 
id 
id 
id 
SECONDARY KEY 
id id id id id id id id 
is_private 
created_at 
indexだけで= “Covering Index” 
探索が終わる
Covering Indexで高速に 
絞り込んだidの 
titleなど、他のデータを 
取得する方法
(1) IN 句 
クエリ1 
SELECT id FROM memos 
WHERE is_private = 0 
ORDER BY created_at DESC, id DESC 
LIMIT 100 OFFSET 100000 
クエリ2 ID羅列 
SELECT * FROM memos 
WHERE id IN (10000,10001,10002,1003,....) 
ORDER BY created_at DESC, id DESC
(2) SELF JOIN 
クエリ 
SELECT memos.id, memos.title, memos.is_private, 
memos.created_at, users.username 
FROM memos, users, 
(SELECT id FROM memos WHERE is_private = 0 ORDER 
BY created_at DESC, id DESC LIMIT 100) AS t 
WHERE t.id = memos.id 
AND users.id = memos.user 
サブクエリーを使用し 
派生テーブル”t”と派生テーブル”t”を作成 
元のテーブルをJOIN
スコア 
3060 => 4234 
よりクエリの少ないSELF JOINを使いました
(9) その他インデックス
init.sh 
cat <<'EOF' | mysql -u isucon isucon 
ALTER TABLE memos ADD INDEX (is_private,created_at), 
ADD INDEX mypage(user,created_at), 
ADD INDEX memo_private(user,is_private,created_at) 
EOF
webapp/perl/lib/Isucon3/Web.pm 
my $memos = $self->dbh->select_all( 
"SELECT id FROM memos WHERE user=? $cond ORDER 
BY created_at", 
$memo->{user}, 
); 
“/memo/xxx” 
元は”*”だが、Covering Indexを 
狙って”id”に変更
スコア 
4234 => 5309
(10) OFFSET殲滅 
データ構造を変更
予めソート済みのmemoの 
リストがあり、BETWEEN句で 
アクセスができれば 
OFFSETで破棄される 
データはいなくなり、エコ
public_memos 
テーブル 
id memo 
1 4 
2 6 
3 8 
... ... 
... ... 
10525 21274 
10526 21277 
10527 21280 
... ... 
10626 21477 
10627 21480 
... ... 
20627 41345 
is_private=0 のmemoの 
idリストolder 
OFFSET 10000の代わりに 
BETWEEN 10001 AND 10100 
memoの 
個数にもなる! 
newer
B-Treeでイメージ 
PRIMARY KEY 
older newer 
id 
id 
id 
id 
id 
id 
id 
id 
memo 
memo 
memo 
memo 
memo 
memo 
memo 
memo 
BETWEEN 10001 AND 10100
init.sh 
cat <<'EOF' | mysql -u isucon isucon 
DROP TABLE IF EXISTS public_memos; 
CREATE TABLE public_memos ( 
id INT NOT NULL AUTO_INCREMENT, 
memo int DEFAULT NULL, 
PRIMARY KEY (id) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 
INSERT INTO public_memos (memo) SELECT id FROM memos 
WHERE is_private=0 ORDER BY created_at ASC, id ASC; 
EOF 
* innodb_autoinc_lock_mode の影響で 
InnoDBではauto increment が連続した値にならない可能性がある
webapp/perl/lib/Isucon3/Web.pm 
my $total = $self->dbh->select_one( 
'SELECT MAX(id) FROM public_memos' 
); 
my $memos = $self->dbh->select_all( 
'SELECT memos.id, memos.title, memos.is_private, 
memos.created_at, users.username 
FROM memos,users, 
(SELECT memo FROM public_memos WHERE id 
BETWEEN ? AND ? ORDER BY id DESC) AS t 
WHERE t.memo = memos.id 
AND users.id=memos.user', 
$total-99, $total 
); 
“/” or “/recent/xxx”
post “/memo” 
webapp/perl/lib/Isucon3/Web.pm 
my $memo_id = $self->dbh->last_insert_id; 
if ( ! scalar($c->req->param('is_private')) ) { 
$self->dbh->query('INSERT INTO public_memos 
(memo) VALUES (?)',$memo_id); 
} 
is_private = 0 なら 
public_memosにもinsert
スコア 
5309 => 8720
あと、セッション周りの 
クエリを減らしたりすると
スコア 
8720 => 9079
Cache がなくても SQL や 
インデックスのチューニングで 
ここまで変わる、この問題は 
面白いなぁと思いました。 
出題の@fujiwaraさん、@acidlemonさん 
をはじめKAYACの皆様にあらためて感謝

More Related Content

What's hot

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021Hiroshi Tokumaru
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターンSoudai Sone
 
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)NTT DATA Technology & Innovation
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装infinite_loop
 
MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話Yoshinori Matsunobu
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門泰 増田
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話Koichiro Matsuoka
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるpospome
 
開発速度が速い #とは(LayerX社内資料)
開発速度が速い #とは(LayerX社内資料)開発速度が速い #とは(LayerX社内資料)
開発速度が速い #とは(LayerX社内資料)mosa siru
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門増田 亨
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
ドメイン駆動で開発する ラフスケッチから実装まで
ドメイン駆動で開発する ラフスケッチから実装までドメイン駆動で開発する ラフスケッチから実装まで
ドメイン駆動で開発する ラフスケッチから実装まで増田 亨
 
マイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチマイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチ増田 亨
 
分散システムについて語らせてくれ
分散システムについて語らせてくれ分散システムについて語らせてくれ
分散システムについて語らせてくれKumazaki Hiroki
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
分散システムの限界について知ろう
分散システムの限界について知ろう分散システムの限界について知ろう
分散システムの限界について知ろうShingo Omura
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 

What's hot (20)

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話MHA for MySQLとDeNAのオープンソースの話
MHA for MySQLとDeNAのオープンソースの話
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
 
開発速度が速い #とは(LayerX社内資料)
開発速度が速い #とは(LayerX社内資料)開発速度が速い #とは(LayerX社内資料)
開発速度が速い #とは(LayerX社内資料)
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
ドメイン駆動で開発する ラフスケッチから実装まで
ドメイン駆動で開発する ラフスケッチから実装までドメイン駆動で開発する ラフスケッチから実装まで
ドメイン駆動で開発する ラフスケッチから実装まで
 
マイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチマイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチ
 
分散システムについて語らせてくれ
分散システムについて語らせてくれ分散システムについて語らせてくれ
分散システムについて語らせてくれ
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
分散システムの限界について知ろう
分散システムの限界について知ろう分散システムの限界について知ろう
分散システムの限界について知ろう
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 

Viewers also liked

ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術
ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術
ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術Masahiro Nagano
 
ISUCONの話(夏期講習2014)
ISUCONの話(夏期講習2014)ISUCONの話(夏期講習2014)
ISUCONの話(夏期講習2014)SATOSHI TAGOMORI
 
DDDをScrumで廻す あるいは ScrumをDDDで廻す
DDDをScrumで廻す あるいは ScrumをDDDで廻す DDDをScrumで廻す あるいは ScrumをDDDで廻す
DDDをScrumで廻す あるいは ScrumをDDDで廻す Kiro Harada
 
Beautiful Development ブレイクスルー体験記
Beautiful Development ブレイクスルー体験記Beautiful Development ブレイクスルー体験記
Beautiful Development ブレイクスルー体験記kentaro watanabe
 
Webアプリケーションの パフォーマンス向上のコツ 概要編
 Webアプリケーションの パフォーマンス向上のコツ 概要編 Webアプリケーションの パフォーマンス向上のコツ 概要編
Webアプリケーションの パフォーマンス向上のコツ 概要編Masahiro Nagano
 
Webアプリケーションの パフォーマンス向上のコツ 実践編
 Webアプリケーションの パフォーマンス向上のコツ 実践編 Webアプリケーションの パフォーマンス向上のコツ 実践編
Webアプリケーションの パフォーマンス向上のコツ 実践編Masahiro Nagano
 

Viewers also liked (6)

ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術
ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術
ISUCON4 予選問題で(中略)、”my.cnf”に1行だけ足して予選通過ラインを突破するの術
 
ISUCONの話(夏期講習2014)
ISUCONの話(夏期講習2014)ISUCONの話(夏期講習2014)
ISUCONの話(夏期講習2014)
 
DDDをScrumで廻す あるいは ScrumをDDDで廻す
DDDをScrumで廻す あるいは ScrumをDDDで廻す DDDをScrumで廻す あるいは ScrumをDDDで廻す
DDDをScrumで廻す あるいは ScrumをDDDで廻す
 
Beautiful Development ブレイクスルー体験記
Beautiful Development ブレイクスルー体験記Beautiful Development ブレイクスルー体験記
Beautiful Development ブレイクスルー体験記
 
Webアプリケーションの パフォーマンス向上のコツ 概要編
 Webアプリケーションの パフォーマンス向上のコツ 概要編 Webアプリケーションの パフォーマンス向上のコツ 概要編
Webアプリケーションの パフォーマンス向上のコツ 概要編
 
Webアプリケーションの パフォーマンス向上のコツ 実践編
 Webアプリケーションの パフォーマンス向上のコツ 実践編 Webアプリケーションの パフォーマンス向上のコツ 実践編
Webアプリケーションの パフォーマンス向上のコツ 実践編
 

Similar to ISUCONで学ぶ Webアプリケーションのパフォーマンス向上のコツ 実践編 完全版

ISUCONの勝ち方 YAPC::Asia Tokyo 2015
ISUCONの勝ち方 YAPC::Asia Tokyo 2015ISUCONの勝ち方 YAPC::Asia Tokyo 2015
ISUCONの勝ち方 YAPC::Asia Tokyo 2015Masahiro Nagano
 
スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門Hisashi HATAKEYAMA
 
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"Kentaro Yoshida
 
Reladomo in Scala #scala_ks
Reladomo in Scala #scala_ks Reladomo in Scala #scala_ks
Reladomo in Scala #scala_ks Hiroshi Ito
 
pi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピングpi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピングkunihikokaneko1
 
リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)Mariko Goda
 
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したいAwsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい聡 大久保
 
My sqlで遭遇したトランザクションとロックのお話
My sqlで遭遇したトランザクションとロックのお話My sqlで遭遇したトランザクションとロックのお話
My sqlで遭遇したトランザクションとロックのお話拓 小林
 
Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Daiyu Hatakeyama
 
シラサギハンズオン 東京
シラサギハンズオン 東京シラサギハンズオン 東京
シラサギハンズオン 東京Yu Ito
 
activerecord-oracle_enhanced-adapterのご紹介
activerecord-oracle_enhanced-adapterのご紹介activerecord-oracle_enhanced-adapterのご紹介
activerecord-oracle_enhanced-adapterのご紹介Kevin Toyoda
 
Handlersocket 20110517
Handlersocket 20110517Handlersocket 20110517
Handlersocket 20110517akirahiguchi
 
jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -Hayato Mizuno
 
Java ee6 with scala
Java ee6 with scalaJava ee6 with scala
Java ee6 with scalaSatoshi Kubo
 
More Better Nested Set
More Better Nested SetMore Better Nested Set
More Better Nested Setxibbar
 
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性JPCERT Coordination Center
 

Similar to ISUCONで学ぶ Webアプリケーションのパフォーマンス向上のコツ 実践編 完全版 (20)

ISUCONの勝ち方 YAPC::Asia Tokyo 2015
ISUCONの勝ち方 YAPC::Asia Tokyo 2015ISUCONの勝ち方 YAPC::Asia Tokyo 2015
ISUCONの勝ち方 YAPC::Asia Tokyo 2015
 
スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門スマートフォン向けサービスにおけるサーバサイド設計入門
スマートフォン向けサービスにおけるサーバサイド設計入門
 
OpenStack Grizzly構築手順書
OpenStack Grizzly構築手順書OpenStack Grizzly構築手順書
OpenStack Grizzly構築手順書
 
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
 
Reladomo in Scala #scala_ks
Reladomo in Scala #scala_ks Reladomo in Scala #scala_ks
Reladomo in Scala #scala_ks
 
pi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピングpi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピング
 
リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)
 
Ext.direct
Ext.directExt.direct
Ext.direct
 
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したいAwsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい
Awsで構築したのだよ 05 プロセス監視、メモリ使用率、ディスク使用率をCloudWatchのカスタムメトリクスに追加したい
 
My sqlで遭遇したトランザクションとロックのお話
My sqlで遭遇したトランザクションとロックのお話My sqlで遭遇したトランザクションとロックのお話
My sqlで遭遇したトランザクションとロックのお話
 
Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版
 
シラサギハンズオン 東京
シラサギハンズオン 東京シラサギハンズオン 東京
シラサギハンズオン 東京
 
activerecord-oracle_enhanced-adapterのご紹介
activerecord-oracle_enhanced-adapterのご紹介activerecord-oracle_enhanced-adapterのご紹介
activerecord-oracle_enhanced-adapterのご紹介
 
Handlersocket 20110517
Handlersocket 20110517Handlersocket 20110517
Handlersocket 20110517
 
jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -
 
Java ee6 with scala
Java ee6 with scalaJava ee6 with scala
Java ee6 with scala
 
Inside Movable Type
Inside Movable TypeInside Movable Type
Inside Movable Type
 
Django boodoo
Django boodooDjango boodoo
Django boodoo
 
More Better Nested Set
More Better Nested SetMore Better Nested Set
More Better Nested Set
 
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性
Apache Sling におけるサービス運用妨害(無限ループ)の脆弱性
 

More from Masahiro Nagano

Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/Min
Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/MinAdvanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/Min
Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/MinMasahiro Nagano
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Masahiro Nagano
 
Big Master Data PHP BLT #1
Big Master Data PHP BLT #1Big Master Data PHP BLT #1
Big Master Data PHP BLT #1Masahiro Nagano
 
Stream processing in Mercari - Devsumi 2015 autumn LT
Stream processing in Mercari - Devsumi 2015 autumn LTStream processing in Mercari - Devsumi 2015 autumn LT
Stream processing in Mercari - Devsumi 2015 autumn LTMasahiro Nagano
 
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月Masahiro Nagano
 
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LTNorikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LTMasahiro Nagano
 
メルカリでのNorikraの活用、 Mackerelを添えて
メルカリでのNorikraの活用、 Mackerelを添えてメルカリでのNorikraの活用、 Mackerelを添えて
メルカリでのNorikraの活用、 Mackerelを添えてMasahiro Nagano
 
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LT
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LTGazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LT
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LTMasahiro Nagano
 
Mackerel & Norikra mackerel meetup #4 LT
Mackerel & Norikra mackerel meetup #4 LTMackerel & Norikra mackerel meetup #4 LT
Mackerel & Norikra mackerel meetup #4 LTMasahiro Nagano
 
Isucon makers casual talks
Isucon makers casual talksIsucon makers casual talks
Isucon makers casual talksMasahiro Nagano
 
blogサービスの全文検索の話 - #groonga を囲む夕べ
blogサービスの全文検索の話 - #groonga を囲む夕べblogサービスの全文検索の話 - #groonga を囲む夕べ
blogサービスの全文検索の話 - #groonga を囲む夕べMasahiro Nagano
 
Gazelle - Plack Handler for performance freaks #yokohamapm
Gazelle - Plack Handler for performance freaks #yokohamapmGazelle - Plack Handler for performance freaks #yokohamapm
Gazelle - Plack Handler for performance freaks #yokohamapmMasahiro Nagano
 
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Masahiro Nagano
 
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LT
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LTWeb Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LT
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LTMasahiro Nagano
 
Webアプリケーションとメモリ
WebアプリケーションとメモリWebアプリケーションとメモリ
WebアプリケーションとメモリMasahiro Nagano
 
最近作ったN個のCPANモジュール Yokohama.pm #10
最近作ったN個のCPANモジュール Yokohama.pm #10最近作ったN個のCPANモジュール Yokohama.pm #10
最近作ったN個のCPANモジュール Yokohama.pm #10Masahiro Nagano
 
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題Masahiro Nagano
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server Masahiro Nagano
 
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-Thon
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-ThonApache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-Thon
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-ThonMasahiro Nagano
 

More from Masahiro Nagano (20)

Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/Min
Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/MinAdvanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/Min
Advanced nginx in mercari - How to handle over 1,200,000 HTTPS Reqs/Min
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015
 
Big Master Data PHP BLT #1
Big Master Data PHP BLT #1Big Master Data PHP BLT #1
Big Master Data PHP BLT #1
 
Stream processing in Mercari - Devsumi 2015 autumn LT
Stream processing in Mercari - Devsumi 2015 autumn LTStream processing in Mercari - Devsumi 2015 autumn LT
Stream processing in Mercari - Devsumi 2015 autumn LT
 
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月
メルカリのデータベース戦略 / PHPとMySQLの怖い話 MyNA会2015年8月
 
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LTNorikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
 
メルカリでのNorikraの活用、 Mackerelを添えて
メルカリでのNorikraの活用、 Mackerelを添えてメルカリでのNorikraの活用、 Mackerelを添えて
メルカリでのNorikraの活用、 Mackerelを添えて
 
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LT
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LTGazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LT
Gazelle & CPAN modules for performance. Shibuya.pm Tech Talk #17 LT
 
Mackerel & Norikra mackerel meetup #4 LT
Mackerel & Norikra mackerel meetup #4 LTMackerel & Norikra mackerel meetup #4 LT
Mackerel & Norikra mackerel meetup #4 LT
 
Isucon makers casual talks
Isucon makers casual talksIsucon makers casual talks
Isucon makers casual talks
 
blogサービスの全文検索の話 - #groonga を囲む夕べ
blogサービスの全文検索の話 - #groonga を囲む夕べblogサービスの全文検索の話 - #groonga を囲む夕べ
blogサービスの全文検索の話 - #groonga を囲む夕べ
 
Gazelle - Plack Handler for performance freaks #yokohamapm
Gazelle - Plack Handler for performance freaks #yokohamapmGazelle - Plack Handler for performance freaks #yokohamapm
Gazelle - Plack Handler for performance freaks #yokohamapm
 
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
 
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LT
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LTWeb Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LT
Web Framework Benchmarksと Perl の現状報告会 YAPC::Asia Tokyo 2014 LT
 
Webアプリケーションとメモリ
WebアプリケーションとメモリWebアプリケーションとメモリ
Webアプリケーションとメモリ
 
最近作ったN個のCPANモジュール Yokohama.pm #10
最近作ったN個のCPANモジュール Yokohama.pm #10最近作ったN個のCPANモジュール Yokohama.pm #10
最近作ったN個のCPANモジュール Yokohama.pm #10
 
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server
 
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-Thon
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-ThonApache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-Thon
Apache::LogFormat::Compiler YAPC::Asia 2013 Tokyo LT-Thon
 
MHA for MySQL の話
MHA for MySQL の話MHA for MySQL の話
MHA for MySQL の話
 

ISUCONで学ぶ Webアプリケーションのパフォーマンス向上のコツ 実践編 完全版

  • 1. ISUCONで学ぶ Webアプリケーションの パフォーマンス向上のコツ 実践編 完全版 ISUCON夏期講習 2014/8/20 Masahiro Nagano
  • 3. チューニングにあたり@acidlemon さんの blog記事を参考にしています 「ざっくりと #isucon 2013年予選問題の 解き方教えます」 http://isucon.net/archives/32976287.html
  • 9. 静的コンテンツを Reverse Proxy で配信 Reverse Proxy: クライアントからの接続を 受け、Applicationサーバに処理を中継す る。画像,js,css などの静的コンテンツを返す 役割もある Application Server: ユーザからのリクエス トを受けて適切なページを構築・レスポン スを行う
  • 10. /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>
  • 12. Nginx 化 • オープンソースのWebサーバ。高速に動 作し、メモリ使用量がすくないなどの 特徴があります
  • 13. Apache vs. Nginx リクエスト worker worker worker worker worker worker worker worker worker コンテキストスイッチが 大量発生 リクエスト worker 1個のプロセスで 効率よく通信を処理
  • 14. command $ sudo yum install nginx $ sudo service httpd stop run.ini [program:nginx] directory=/ command=/usr/sbin/nginx -c /home/isu-user/isucon/ nginx.conf autostart = true nginx.conf: https://gist.github.com/kazeburo/7b0385cce1b0a4565581
  • 16. (2) Perl にします ワタシハパールチョットデキル
  • 17. Perl の起動方法 run.ini TCPではなくUNIX domain socketを使う 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 プロセスを長生きさせる
  • 18. TCPの接続は高コスト Reverse Proxy リクエスト毎に three way handshake App Server
  • 22. “/” “/recent/xxx” DBへの問い合わせが重い “/memo/xxxx” “/mypage” markdown の変換に プロセス起動 DBへの問い合わせが 若干重い
  • 24. webapp/perl/lib/Isucon3/Web.pm “/memo/xxxx” +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) } ここがmarkdownコマンドを 起動している XS(C)で高速にmarkdownを 処理するモジュール
  • 27. webapp/perl/lib/Isucon3/Web.pm “/” 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}, ); } 100回ルーーーープ
  • 29. memosテーブルusersテーブル id user_id id name memos JOIN users ON memos.user_id = user.id id user_id name
  • 30. webapp/perl/lib/Isucon3/Web.pm 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' ); “/”, “/recent”
  • 33. indexがないと SELECT * FROM memos WHERE is_private=0 ORDER BY created_at DESC LIMIT 100 memosテーブル id is_priv ate ... 0 0 1 0 1 id is_priv ate ... 0 0 0 ソート webapp/perl/lib/Isucon3/Web.pm 抽出 CPU負荷高い
  • 34. indexをつくる init.sh cat <<'EOF' | mysql -u isucon isucon ALTER TABLE memos ADD INDEX (is_private,created_at); EOF
  • 35. B-Tree is_private 0 1 created_at older newer older newer
  • 36. B-Tree is_private 0 1 created_at older newer older newer
  • 37. B-Tree is_private 0 1 created_at older newer older newer
  • 38. B-Tree is_private 0 1 created_at older newer older newer
  • 39. B-Tree is_private 0 1 created_at older newer older newer 順に取得するだけ
  • 43. mysql 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) titleカラムが存在しない!
  • 44. タイトルは本文から 都度生成 webapp/perl/views/index.tx <: $memo.content.split('r?n').first() :> contentの転送で通信splitでCPU使用
  • 45. titleカラムの追加し、 init.sh 事前生成 cat <<'EOF' | mysql -u isucon isucon ALTER TABLE memos ADD COLUMN title text; UPDATE memos SET title = substring_index(content,"n",1); EOF
  • 46. POST時にも生成 webapp/perl/lib/Isucon3/Web.pm $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, );
  • 47. 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' ); “/”, “/recent” memos.* だと contentを 取ってしまう
  • 49. (8) OFFSET = 破棄
  • 50. ”/recent/100” 100ページ目 SELECT * FROM memos ORDER BY created_at LIMIT 100 OFFSET 10000 とても大きなOFFSET
  • 51. MySQLのOFFSET処理のイメージ 1 2 3 4 id title user ... . id title user ... . id title user ... . id title user ... . 5 6 7 8 id title user ... . id title user ... . id title user ... . id title user ... . 9 10 11 12 id title user ... . id title user ... . id title user ... . id title user ... . 13 id title user ... . 10000 id title user ... . 10001 10002 10003 10004 id title user ... . id title user ... . id title user ... . id title user ... .
  • 52. MySQLのOFFSET処理のイメージ 頑張ってソート 1 2 3 4 id title user ... . id title user ... . id title user ... . id title user ... . 5 6 7 8 id title user ... . id title user ... . id title user ... . id title user ... . 9 10 11 12 id title user ... . id title user ... . id title user ... . id title user ... . 13 id title user ... . 10000 id title user ... . 10001 10002 10003 10004 id title user ... . id title user ... . id title user ... . id title user ... . 必要な個数まで到達
  • 53. MySQLのOFFSET処理のイメージ 頑張ってソート 1 2 3 4 id title user ... . id title user ... . id title user ... . id title user ... . 5 6 7 8 id title user ... . id title user ... . id title user ... . id title user ... . 9 10 11 12 id title user ... . id title user ... . id title user ... . id title user ... . 13 id title user ... . 10000 id title user ... . 10001 10002 10003 10004 id title user ... . id title user ... . id title user ... . id title user ... . 必要な個数まで到達 廃棄
  • 56. 取得するデータを制限 SELECT id FROM memos ORDER BY created_at LIMIT 100 OFFSET 10000
  • 57. MySQLのOFFSET処理のイメージ 1 2 3 4 5 6 7 8 9 10 11 12 13 id id id id id id id id id id id id id ・・・・・ ・・・・・ 9999 10000 id id 10001 10002 10003 10004 id id id id
  • 58. MySQLのOFFSET処理のイメージ 1 2 3 4 5 6 7 8 9 10 11 12 13 id id id id id id id id id id id id id ・・・・・ 10001 10002 10003 10004 廃棄 ・・・・・ 9999 10000 id id id id id id 読むデータも、捨て るデータも少ない
  • 60. MySQLのインデックスと データの持ち方 title user ... title user ... user ... PRIMARY title user ... title user ... title user ... title user ... title user ... title KEY CLUSTERED INDEX リーフノードに データを含む small large id id id id id id id id
  • 61. MySQLのインデックスと データの持ち方 SECONDARY KEY primary keyじゃないkey リーフノードに PRIMARY KEYが含まれ、 データはCLUSTERED INDEX から取得 is_private id id id id id id id id created_at older newer older newer
  • 62. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 63. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 64. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 65. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 66. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 67. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 68. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 69. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 70. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 71. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 72. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 73. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 74. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 75. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 76. SELECT * の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at 何度も繰り返す
  • 77. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 78. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 79. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 80. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 81. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 82. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 83. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 84. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at
  • 85. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at indexだけで 探索が終わる
  • 86. SELECT id の場合 title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . title user ... . PRIMARY KEY id id id id id id id id SECONDARY KEY id id id id id id id id is_private created_at indexだけで= “Covering Index” 探索が終わる
  • 87. Covering Indexで高速に 絞り込んだidの titleなど、他のデータを 取得する方法
  • 88. (1) IN 句 クエリ1 SELECT id FROM memos WHERE is_private = 0 ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 100000 クエリ2 ID羅列 SELECT * FROM memos WHERE id IN (10000,10001,10002,1003,....) ORDER BY created_at DESC, id DESC
  • 89. (2) SELF JOIN クエリ SELECT memos.id, memos.title, memos.is_private, memos.created_at, users.username FROM memos, users, (SELECT id FROM memos WHERE is_private = 0 ORDER BY created_at DESC, id DESC LIMIT 100) AS t WHERE t.id = memos.id AND users.id = memos.user サブクエリーを使用し 派生テーブル”t”と派生テーブル”t”を作成 元のテーブルをJOIN
  • 90. スコア 3060 => 4234 よりクエリの少ないSELF JOINを使いました
  • 92. init.sh cat <<'EOF' | mysql -u isucon isucon ALTER TABLE memos ADD INDEX (is_private,created_at), ADD INDEX mypage(user,created_at), ADD INDEX memo_private(user,is_private,created_at) EOF
  • 93. webapp/perl/lib/Isucon3/Web.pm my $memos = $self->dbh->select_all( "SELECT id FROM memos WHERE user=? $cond ORDER BY created_at", $memo->{user}, ); “/memo/xxx” 元は”*”だが、Covering Indexを 狙って”id”に変更
  • 96. 予めソート済みのmemoの リストがあり、BETWEEN句で アクセスができれば OFFSETで破棄される データはいなくなり、エコ
  • 97. public_memos テーブル id memo 1 4 2 6 3 8 ... ... ... ... 10525 21274 10526 21277 10527 21280 ... ... 10626 21477 10627 21480 ... ... 20627 41345 is_private=0 のmemoの idリストolder OFFSET 10000の代わりに BETWEEN 10001 AND 10100 memoの 個数にもなる! newer
  • 98. B-Treeでイメージ PRIMARY KEY older newer id id id id id id id id memo memo memo memo memo memo memo memo BETWEEN 10001 AND 10100
  • 99. init.sh cat <<'EOF' | mysql -u isucon isucon DROP TABLE IF EXISTS public_memos; CREATE TABLE public_memos ( id INT NOT NULL AUTO_INCREMENT, memo int DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO public_memos (memo) SELECT id FROM memos WHERE is_private=0 ORDER BY created_at ASC, id ASC; EOF * innodb_autoinc_lock_mode の影響で InnoDBではauto increment が連続した値にならない可能性がある
  • 100. webapp/perl/lib/Isucon3/Web.pm my $total = $self->dbh->select_one( 'SELECT MAX(id) FROM public_memos' ); my $memos = $self->dbh->select_all( 'SELECT memos.id, memos.title, memos.is_private, memos.created_at, users.username FROM memos,users, (SELECT memo FROM public_memos WHERE id BETWEEN ? AND ? ORDER BY id DESC) AS t WHERE t.memo = memos.id AND users.id=memos.user', $total-99, $total ); “/” or “/recent/xxx”
  • 101. post “/memo” webapp/perl/lib/Isucon3/Web.pm my $memo_id = $self->dbh->last_insert_id; if ( ! scalar($c->req->param('is_private')) ) { $self->dbh->query('INSERT INTO public_memos (memo) VALUES (?)',$memo_id); } is_private = 0 なら public_memosにもinsert
  • 105. Cache がなくても SQL や インデックスのチューニングで ここまで変わる、この問題は 面白いなぁと思いました。 出題の@fujiwaraさん、@acidlemonさん をはじめKAYACの皆様にあらためて感謝