Last active
August 29, 2015 14:06
-
-
Save minimum2scp/8e42b76b7a1cd2eb038c to your computer and use it in GitHub Desktop.
第8回 ITインフラ勉強会@福岡 (http://fukinfra.doorkeeper.jp/events/14234) でやったチューニングのメモ。「ISUCONで学ぶ Webアプリケーションのパフォーマンス向上のコツ 実践編 完全版」 http://blog.nomadscafe.jp/2014/08/isucon-web-apps-tune.html を実践してみた。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| やりたいこと | |
| * [x] etckeeper (/etc を git でバージョン管理) | |
| * [×] ~/isucon 以下を git でバージョン管理 | |
| * [x] ruby 2.1.2 のリビルド (libgmp つきで高速化) | |
| ## etckeeper | |
| sudo yum install --enablerepo=epel etckeeper | |
| sudo etckeeper init | |
| sudo etckeeper commit "Initial commit" | |
| 以降は、 | |
| * sudo etckeeper commit "commit message" | |
| * sudo etckeeper vcs status | |
| * sudo etckeeper vcs diff | |
| * sudo etckeeper vcs log | |
| とかできます | |
| ## ruby 2.1.2 のリビルド | |
| [isu-user@ip-10-126-167-190 ~]$ ldd ~/local/ruby-2.1.2/bin/ruby | |
| linux-vdso.so.1 => (0x00007fff6d9f6000) | |
| libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f322b542000) | |
| libdl.so.2 => /lib64/libdl.so.2 (0x00007f322b33d000) | |
| libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f322b107000) | |
| libm.so.6 => /lib64/libm.so.6 (0x00007f322ae09000) | |
| libc.so.6 => /lib64/libc.so.6 (0x00007f322aa63000) | |
| /lib64/ld-linux-x86-64.so.2 (0x00007f322bc53000) | |
| libfreebl3.so => /lib64/libfreebl3.so (0x00007f322a7e6000) | |
| なので、gmp-devel 入れてリビルドします | |
| (IntegerかFloatかが速くなるはず) | |
| sudo yum install gmp-devel | |
| mkdir ~/tmp | |
| cd ~/tmp | |
| curl -LO http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | |
| tar xf ruby-2.1.2.tar.gz | |
| cd ruby-2.1.2 | |
| ./configure --prefix=$HOME/local/ruby-2.1.2+gmp | |
| make -j4 | |
| make install | |
| ## 使用する ruby の切り替えと gem のインストール | |
| ~/.bashrc | |
| export PATH=/home/isu-user/local/ruby-2.1.2/bin:$PATH | |
| を | |
| export PATH=/home/isu-user/local/ruby-2.1.2+gmp/bin:$PATH | |
| に書き換え | |
| 変更を反映するため再ログイン後、 | |
| gem install -N bundler | |
| アプリにも反映するため、~/isucon/env.sh を書き換え | |
| commit c507039410992b5561dea1fcaef3b5209c3e9bf6 | |
| Author: Your Name <you@example.com> | |
| Date: Sat Sep 13 05:31:10 2014 +0000 | |
| env.sh: changed ruby interpreter (gmp enabled version) | |
| diff --git a/env.sh b/env.sh | |
| index 003bbd6..aa928fb 100755 | |
| --- a/env.sh | |
| +++ b/env.sh | |
| @@ -1,6 +1,7 @@ | |
| #!/bin/sh | |
| export PATH=/home/isu-user/local/perl-5.20/bin:$PATH | |
| -export PATH=/home/isu-user/local/ruby-2.1.2/bin:$PATH | |
| +#export PATH=/home/isu-user/local/ruby-2.1.2/bin:$PATH | |
| +export PATH=/home/isu-user/local/ruby-2.1.2+gmp/bin:$PATH | |
| export PATH=/home/isu-user/local/node-v0.10/bin:$PATH | |
| export PATH=/home/isu-user/local/python-3.4.1/bin:$PATH | |
| export PATH=/home/isu-user/local/go/bin:$PATH | |
| もともとの ruby を誤って使わないよう、固めて消しておく | |
| cd ~/local | |
| tar cfz ruby-2.1.2.tar.gz ruby-2.1.2 | |
| rm -rf ruby-2.1.2 | |
| アプリ再起動 | |
| sudo supervisorctl restart isucon_ruby | |
| # benchmark | |
| before 1619 | |
| after1 1530 (down) | |
| after2 1580 (down) | |
| # rollback ruby 2.1.2 | |
| スコアおちたので戻します | |
| cd ~/local | |
| tar xf ruby-2.1.2.tar.gz | |
| tar cfz ruby-2.1.2+gmp.tar.gz ruby-2.1.2+gmp | |
| rm -rf ruby-2.1.2+gmp | |
| ~/.bashrc と ~/isucon/env.sh も巻き戻し(内容略) | |
| プロセス(シェル、アプリ)再起動 | |
| # やること(やまだ) | |
| * [x] apache -> nginx | |
| * [x] rewrite | |
| * [x] unicorn プロセス数減らす | |
| * [x] TCP -> unix domain socket | |
| # やること(あづち) | |
| * [x] markdownのコマンド呼び出しをライブラリ化 | |
| * [×] mysqlのインデックス作成 | |
| # markdownのライブラリ化 | |
| Gemfileにgem 'rdiscount'を追加して bundle install --no-deployment | |
| app.rbでrequire 'rdiscount'を追加し、markdown生成するメソッドを以下に修正。 | |
| def gen_markdown(md) | |
| RDiscount.new(md).to_html | |
| end | |
| # benchmark | |
| before 1619 | |
| after 2086 (up) | |
| # apache -> nginx | |
| sudo yum install nginx | |
| (1.4.7 古い) | |
| sudo yum install http://nginx.org/packages/centos/6/x86_64/RPMS/nginx-1.6.1-1.el6.ngx.x86_64.rpm | |
| apache の設定をまねて、アプリに ReverseProxy できるようにする | |
| /etc/nginx/conf.d/default.conf を default.conf.disable にして無効化 | |
| /etc/nginx/conf.d/isucon.conf: | |
| server { | |
| listen 80; | |
| server_name localhost; | |
| root /home/isu-user/isucon/webapp/public; | |
| #index index.html index.htm; | |
| try_files try_files $uri/index.html $uri.html $uri @app; | |
| location @app { | |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| proxy_set_header Host $http_host; | |
| proxy_redirect off; | |
| proxy_pass http://localhost:5000; | |
| } | |
| error_page 500 502 503 504 /50x.html; | |
| location = /50x.html { | |
| root /usr/share/nginx/html; | |
| } | |
| } | |
| apache を停止して nginx を起動 | |
| sudo service httpd stop | |
| sudo service nginx start | |
| # benchmark | |
| score 2086 -> 2153 | |
| # MySQLのインデックス作成 | |
| init.shに↓追加 | |
| cat <<'EOF' | mysql -u isucon isucon | |
| ALTER TABLE memos ADD INDEX (is_private, created_at); | |
| EOF | |
| # benchmark | |
| score 2153.2 -> 2263.8 | |
| # rewrite | |
| try_files なのでもうできてる(はず) | |
| # nginx worker ふやす | |
| diff --git a/nginx/nginx.conf b/nginx/nginx.conf | |
| index e4bad8d..a3e54e0 100644 | |
| --- a/nginx/nginx.conf | |
| +++ b/nginx/nginx.conf | |
| @@ -1,6 +1,6 @@ | |
| user nginx; | |
| -worker_processes 1; | |
| +worker_processes 4; | |
| error_log /var/log/nginx/error.log warn; | |
| pid /var/run/nginx.pid; | |
| sudo service nginx reload | |
| # benchmark | |
| 2263.8 -> 2342 | |
| # unicorn プロセス数減らす | |
| diff --git a/webapp/ruby/unicorn_config.rb b/webapp/ruby/unicorn_config.rb | |
| index 9f6d110..628679e 100644 | |
| --- a/webapp/ruby/unicorn_config.rb | |
| +++ b/webapp/ruby/unicorn_config.rb | |
| @@ -1,2 +1,2 @@ | |
| -worker_processes 10 | |
| +worker_processes 4 | |
| preload_app true | |
| # benchmark | |
| 2342 -> 2242 | |
| SQL とかのチューニングが十分効くまでは、worker_processは戻しておく(4->10) | |
| 2242 -> 2200 | |
| (設定戻したのにスコアは戻らない…) | |
| # TCP -> unix domain socket | |
| nginx: | |
| diff --git a/nginx/conf.d/isucon.conf b/nginx/conf.d/isucon.conf | |
| index 95125ef..2e2cfa0 100644 | |
| --- a/nginx/conf.d/isucon.conf | |
| +++ b/nginx/conf.d/isucon.conf | |
| @@ -10,7 +10,8 @@ server { | |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| proxy_set_header Host $http_host; | |
| proxy_redirect off; | |
| - proxy_pass http://localhost:5000; | |
| + #proxy_pass http://localhost:5000; | |
| + proxy_pass http://unix:/tmp/unicorn.sock; | |
| } | |
| error_page 500 502 503 504 /50x.html; | |
| unicorn: | |
| diff --git a/webapp/ruby/unicorn_config.rb b/webapp/ruby/unicorn_config.rb | |
| index 9f6d110..559273a 100644 | |
| --- a/webapp/ruby/unicorn_config.rb | |
| +++ b/webapp/ruby/unicorn_config.rb | |
| @@ -1,2 +1,4 @@ | |
| worker_processes 10 | |
| preload_app true | |
| +listen "/tmp/unicorn.sock" | |
| + | |
| Procfile (foreman): | |
| diff --git a/webapp/ruby/Procfile b/webapp/ruby/Procfile | |
| index a17068e..32a2d12 100644 | |
| --- a/webapp/ruby/Procfile | |
| +++ b/webapp/ruby/Procfile | |
| @@ -1 +1,2 @@ | |
| -web: bundle exec unicorn -c unicorn_config.rb -p $PORT | |
| +#web: bundle exec unicorn -c unicorn_config.rb -p $PORT | |
| +web: bundle exec unicorn -c unicorn_config.rb | |
| # benchmark | |
| 2200 -> 2214 | |
| # newrelic | |
| Gemfile に 'newrelic_rpm' を追加、 | |
| newrelic.yml を追加、 | |
| config.ru に require 'newrelic_rpm' を追加 | |
| bundle してアプリ再起動 | |
| # benchmark | |
| 2143.2 | |
| # nrsysmond | |
| sudo yum install http://download.newrelic.com/pub/newrelic/el5/i386/newrelic-repo-5-3.noarch.rpm | |
| sudo yum install newrelic-sysmond | |
| sudo nrsysmond-config --set license_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxx | |
| sudo /etc/init.d/newrelic-sysmond start | |
| # titleカラムの追加 | |
| init.shを以下に修正。 | |
| cat <<'EOF' | mysql -u isucon isucon | |
| ALTER TABLE memos ADD INDEX (is_private, created_at); | |
| ALTER TABLE memos ADD COLUMN title text; | |
| UPDATE memos SET title = substring_index(content, "\n", 1); | |
| EOF | |
| app.rbのポスト処理時にtitleを生成。 | |
| index.erbのタイトル生成処理をtitleカラムを参照するよう変更。 | |
| # benchmark | |
| 2143.2 -> 2215.8 | |
| # memoのユーザ取得をクエリ発行じゃなくてmemosのJOINで一緒に行うよう修正 | |
| app.rbの/と/recent/:pageアクセス時のクエリを | |
| SELECT memos.*, users.username FROM memos INNER JOIN users ON (users.id = memos.user) WHERE is_private=0 ORDER BY memos.created_at DESC, memos.id DESC | |
| に変更し、memos.eachでusersテーブルへ発行してたクエリを削除 | |
| # benchmark | |
| 2215.8 -> 2479.7 | |
| # gen_markdown を memcached に | |
| 1回目 2390 くらい | |
| 2回目で FAIL した | |
| * MySQL は巻き戻されたけどキャッシュクリアしてなかった | |
| * そもそも同じメモを2回以上 GET されないと意味がない | |
| revert した | |
| # select * してる部分をカラム指定に変更 | |
| # benchmark | |
| 2479.7 -> 2522.3 | |
| # mysql my.cnf チューニング | |
| -innodb_buffer_pool_size = 2GB | |
| +innodb_buffer_pool_size = 8GB | |
| +innodb_flush_log_at_trx_commit = 2 | |
| mysql restart. | |
| # benchmark | |
| 2522 -> 2594.5 | |
| # SQL書き換え (get /recent/:page) | |
| diff --git a/webapp/ruby/app.rb b/webapp/ruby/app.rb | |
| index 15167d5..0191dd7 100644 | |
| --- a/webapp/ruby/app.rb | |
| +++ b/webapp/ruby/app.rb | |
| @@ -91,7 +91,11 @@ class Isucon3App < Sinatra::Base | |
| page = params["page"].to_i | |
| total = mysql.xquery('SELECT count(*) AS c FROM memos WHERE is_private=0').first["c"] | |
| - memos = mysql.xquery("SELECT memos.id, memos.title, memos.created_at, users.username FROM memos INNER JOIN users ON (users.id = me | |
| + memos = mysql.xquery( | |
| + "SELECT memos.id, memos.title, 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 OFFSET #{page * 100}) AS t " + | |
| + "WHERE t.id = memos.id AND users.id = memos.user" | |
| + ) | |
| if memos.count == 0 | |
| halt 404, "404 Not Found" | |
| end | |
| # benchmark | |
| 2594.5 -> 3301.2 | |
| # SQL書き換え (get /memo/:memo_id) | |
| diff --git a/webapp/ruby/app.rb b/webapp/ruby/app.rb | |
| index 0191dd7..6c62604 100644 | |
| --- a/webapp/ruby/app.rb | |
| +++ b/webapp/ruby/app.rb | |
| @@ -177,7 +177,7 @@ class Isucon3App < Sinatra::Base | |
| memos = [] | |
| older = nil | |
| newer = nil | |
| - results = mysql.xquery("SELECT * FROM memos WHERE user=? #{cond} ORDER BY created_at", memo["user"]) | |
| + results = mysql.xquery("SELECT id FROM memos WHERE user=? #{cond} ORDER BY created_at", memo["user"]) | |
| results.each do |m| | |
| memos.push(m) | |
| end | |
| # benchmark | |
| 3301 -> 3539 | |
| # unicorn プロセス数 10 -> 4 | |
| diff --git a/webapp/ruby/unicorn_config.rb b/webapp/ruby/unicorn_config.rb | |
| index 559273a..a8abd72 100644 | |
| --- a/webapp/ruby/unicorn_config.rb | |
| +++ b/webapp/ruby/unicorn_config.rb | |
| @@ -1,4 +1,4 @@ | |
| -worker_processes 10 | |
| +worker_processes 4 | |
| preload_app true | |
| listen "/tmp/unicorn.sock" | |
| # benchmark | |
| 3539 -> 3603 | |
| # SQL tuning (GET /) | |
| diff --git a/webapp/ruby/app.rb b/webapp/ruby/app.rb | |
| index 6c62604..e0df60d 100644 | |
| --- a/webapp/ruby/app.rb | |
| +++ b/webapp/ruby/app.rb | |
| @@ -76,8 +76,12 @@ class Isucon3App < Sinatra::Base | |
| user = get_user | |
| total = mysql.query("SELECT count(*) AS c FROM memos WHERE is_private=0").first["c"] | |
| - memos = mysql.query(" SELECT memos.id, memos.title, memos.created_at, users.username FROM memos INNER JOIN users ON (users.id = m | |
| - erb :index, :layout => :base, :locals => { | |
| + memos = mysql.xquery( | |
| + "SELECT memos.id, memos.title, 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" | |
| + ) | |
| + erb :index, :layout => :base, :locals => { | |
| :memos => memos, | |
| :page => 0, | |
| :total => total, | |
| # benchmark | |
| 3603 -> 3570.2 | |
| GET / のチューニングはrevert. | |
| # インデックス追加 | |
| ALTER TABLE memos ADD INDEX (is_private, created_at), | |
| ADD INDEX mypage(user, created_at), | |
| ADD INDEX memo_private(user,is_private,created_at); | |
| # benchmark | |
| 3570.2 -> 4303.3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment