105
ISUCONで学ぶ Webアプリケーションの パフォーマンス向上のコツ 実践編 完全版 ISUCON夏期講習 2014/8/20 Masahiro Nagano

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

Embed Size (px)

DESCRIPTION

 

Citation preview

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

ISUCONで学ぶ Webアプリケーションのパフォーマンス向上のコツ

実践編 完全版ISUCON夏期講習

2014/8/20Masahiro Nagano

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

この資料を読む前に以下の記事をお読みください

http://blog.nomadscafe.jp/2014/08/isucon-2014-ami.html

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

チューニングにあたり@acidlemon さんのblog記事を参考にしています

「ざっくりと #isucon 2013年予選問題の解き方教えます」

http://isucon.net/archives/32976287.html

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

挑戦してみました

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

最終スコア

9079

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

やってみたことを紹介します

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

初期スコア

1664ruby実装にて

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

(1) 環境整備

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

静的コンテンツをReverse Proxy で配信

Reverse Proxy: クライアントからの接続を受け、Applicationサーバに処理を中継する。画像,js,css などの静的コンテンツを返す役割もある

Application Server: ユーザからのリクエストを受けて適切なページを構築・レスポンスを行う

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

<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>

/etc/httpd/conf.d/isucon.conf

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

スコア

1664 => 1719

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

Nginx 化

• オープンソースのWebサーバ。高速に動作し、メモリ使用量がすくないなどの特徴があります

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

Apache vs. Nginx

worker worker worker

worker worker worker

worker worker worker

リクエスト

コンテキストスイッチが大量発生

リクエスト

worker

1個のプロセスで効率よく通信を処理

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

$ 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: https://gist.github.com/kazeburo/7b0385cce1b0a4565581

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

スコア

1719 => 1764

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

(2) Perl にしますワタシハパールチョットデキル

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

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を使う

プロセスを長生きさせる

プロセスはあげすぎない

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

TCPの接続は高コスト

ReverseProxy

AppServer

リクエスト毎にthree way handshake

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

スコア

1764 => 1891

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

(3) アプリをみよう

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

“/” “/recent/xxx”

“/memo/xxxx” “/mypage”

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

“/” “/recent/xxx”

“/memo/xxxx” “/mypage”

DBへの問い合わせが重い

markdown の変換にプロセス起動

DBへの問い合わせが若干重い

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

(4) 外部プロセス起動

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

+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”

XS(C)で高速にmarkdownを処理するモジュール

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

スコア

1891 => 2233

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

(5) N+1 クエリ

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

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回ルーーーープ

“/”

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

use the join, luke

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

id user_id id name

memosテーブル usersテーブル

id user_id name

memos JOIN users ON memos.user_id = user.id

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

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”

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

スコア

2233 => 2398

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

(6) インデックス

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

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

ソート

webapp/perl/lib/Isucon3/Web.pm

indexがないと

抽出

CPU負荷高い

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

indexをつくる

cat <<'EOF' | mysql -u isucon isuconALTER TABLE memos ADD INDEX (is_private,created_at);EOF

init.sh

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

B-Tree

0 1is_private

created_at

older newer older newer

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

B-Tree

0 1is_private

created_at

older newer older newer

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

B-Tree

0 1is_private

created_at

older newer older newer

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

B-Tree

0 1is_private

created_at

older newer older newer

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

B-Tree

0 1is_private

created_at

older newer older newer

順に取得するだけ

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

スコア

2398 => 2668

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

(7) タイトル事前生成

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

これ

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

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カラムが存在しない!

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

<: $memo.content.split('\r?\n').first() :>webapp/perl/views/index.tx

splitでCPU使用contentの転送で通信

タイトルは本文から都度生成

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

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カラムの追加し、事前生成

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

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

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

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を取ってしまう

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

スコア

2668 => 3060

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

(8) OFFSET = 破棄

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

SELECT * FROM memos ORDER BY created_at LIMIT 100

OFFSET 10000とても大きなOFFSET

”/recent/100”100ページ目

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

MySQLのOFFSET処理のイメージ

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

1 2 3 4

5 6 7 8

9 10 11 12

13

10000

10001 10002 10003 10004

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

MySQLのOFFSET処理のイメージ

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

1 2 3 4

5 6 7 8

9 10 11 12

13

10000

10001 10002 10003 10004

頑張ってソート

必要な個数まで到達

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

MySQLのOFFSET処理のイメージ

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

id title user ... . id title user ... . id title user ... . id title user ... .

id title user ... .

1 2 3 4

5 6 7 8

9 10 11 12

13

10000

10001 10002 10003 10004

頑張ってソート

必要な個数まで到達

廃棄

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

MOTTAINAI

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

捨てるデータを減らす

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

SELECT id FROM memos ORDER BY created_at LIMIT 100

OFFSET 10000

取得するデータを制限

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

・・・・・

MySQLのOFFSET処理のイメージ

1 2 3 4 5 6 7 8 9 10 11 12 13

9999

id id id id id id id id id id id id id ・・・・・

id id

10000

id id id id

10001 10002 10003 10004

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

・・・・・

MySQLのOFFSET処理のイメージ

1 2 3 4 5 6 7 8 9 10 11 12 13

9999

id id id id id id id id id id id id id ・・・・・

id id

10000

id id id id

10001 10002 10003 10004

廃棄読むデータも、捨てるデータも少ない

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

“id” だけにすると高速になるもう一つの理由

“Covering Index”

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

MySQLのインデックスとデータの持ち方

titleuser

....

titleuser

...

titleuser

...

titleuser

...

titleuser

...

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEYCLUSTERED INDEX

リーフノードにデータを含む

small largeid id id id id id id id

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

MySQLのインデックスとデータの持ち方

SECONDARY KEYprimary keyじゃないkey

リーフノードにPRIMARY KEYが含まれ、データはCLUSTERED INDEX

から取得

id id id id id id id id

is_private

created_atolder newer older newer

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT * の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

何度も繰り返す

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

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だけで探索が終わる

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

SELECT id の場合

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

titleuser

....

PRIMARY KEY

id id id id id id id id

SECONDARY KEY

id id id id id id id id

is_private

created_at

= “Covering Index”indexだけで探索が終わる

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

Covering Indexで高速に絞り込んだidの

titleなど、他のデータを取得する方法

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

SELECT id FROM memos WHERE is_private = 0 ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 100000

クエリ1

(1) IN 句

SELECT * FROM memos WHERE id IN (10000,10001,10002,1003,....) ORDER BY created_at DESC, id DESC

クエリ2 ID羅列

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

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

クエリ

(2) SELF JOIN

サブクエリーを使用し派生テーブル”t”を作成派生テーブル”t”と

元のテーブルをJOIN

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

スコア

3060 => 4234よりクエリの少ないSELF JOINを使いました

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

(9) その他インデックス

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

cat <<'EOF' | mysql -u isucon isuconALTER TABLE memos ADD INDEX (is_private,created_at), ADD INDEX mypage(user,created_at), ADD INDEX memo_private(user,is_private,created_at)EOF

init.sh

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

my $memos = $self->dbh->select_all( "SELECT id FROM memos WHERE user=? $cond ORDER BY created_at", $memo->{user},);

webapp/perl/lib/Isucon3/Web.pm

“/memo/xxx”

元は”*”だが、Covering Indexを狙って”id”に変更

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

スコア

4234 => 5309

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

(10) OFFSET殲滅データ構造を変更

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

予めソート済みのmemoのリストがあり、BETWEEN句で

アクセスができればOFFSETで破棄される

データはいなくなり、エコ

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

id memo1 42 63 8... ...... ...

10525 2127410526 2127710527 21280

... ...10626 2147710627 21480

... ...20627 41345

public_memosテーブル

OFFSET 10000の代わりにBETWEEN 10001 AND 10100

is_private=0 のmemoのidリストolder

newer

memoの個数にもなる!

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

B-TreeでイメージPRIMARY KEY

older newerid id id id id id id id

memo

memo

memo

memo

memo

memo

memo

memo

BETWEEN 10001 AND 10100

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

cat <<'EOF' | mysql -u isucon isuconDROP 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

init.sh

* innodb_autoinc_lock_mode の影響でInnoDBではauto increment が連続した値にならない可能性がある

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

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);

webapp/perl/lib/Isucon3/Web.pm

“/” or “/recent/xxx”

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

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);}

webapp/perl/lib/Isucon3/Web.pm

post “/memo”

is_private = 0 ならpublic_memosにもinsert

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

スコア

5309 => 8720

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

あと、セッション周りのクエリを減らしたりすると

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

スコア

8720 => 9079

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

Cache がなくても SQL やインデックスのチューニングでここまで変わる、この問題は面白いなぁと思いました。

出題の@fujiwaraさん、@acidlemonさんをはじめKAYACの皆様にあらためて感謝