├── 00.machine.md ├── 00.metrics.md ├── 01.aws.md ├── 01.ssh.md ├── 02.iptables_selinux.md ├── 02.kernel.md ├── 03.yum_repo.md ├── 04.tmux.md ├── 05.node.md ├── 05.python.md ├── 05.ruby.md ├── 05.ruby_metrics.md ├── 05.ruby_newrelic.md ├── 06.mysql_5.5.md ├── 06.mysql_5.6.md ├── 06.mysql_repl.md ├── 06.mysql_stat.md ├── 06.mysql_tmc.md ├── 06.mysql_tmpfs.md ├── 09.unicorn.md ├── 10.nginx.md ├── 11.varnish.md ├── 12.redis.md ├── 12.redis_resque.md ├── 12.redis_ruby.md ├── 13.daemontools.md ├── 14.golang.md ├── README.md └── isucon2 └── 01.setup.md /00.machine.md: -------------------------------------------------------------------------------- 1 | マシンのスペックを見るコマンド集。あとベンチとか。 2 | 3 | ## マシンスペックの把握 4 | 5 | ### OS 6 | 7 | ``` 8 | $ cat /etc/redhat-release 9 | $ uname -a 10 | ``` 11 | 12 | ### CPU詳細 13 | 14 | ``` 15 | $ cat /proc/cpuinfo 16 | ``` 17 | 18 | ### メモリ詳細 19 | 20 | ``` 21 | $ cat /proc/meminfo 22 | $ free # ぐらいでも可 23 | 24 | ``` 25 | 26 | ### 読み込みベンチ(シーケンシャル) 27 | 28 | ``` 29 | # hdparm -t /dev/sda 30 | /dev/sda: 31 | Timing buffered disk reads: 1246 MB in 3.00 seconds = 415.08 MB/sec 32 | 33 | ``` 34 | 35 | ### 書き込みベンチ 36 | 37 | ``` 38 | # rm -f /tmp/hdparm_write.tmp; sleep 3 ; time dd if=/dev/zero of=/tmp/hdparm_write.tmp ibs=1M obs=1M count=1024 39 | 1024+0 records in 40 | 1024+0 records out 41 | 1073741824 bytes (1.1 GB) copied, 0.870569 s, 1.2 GB/s 42 | real 0m0.908s 43 | user 0m0.174s 44 | sys 0m0.695s 45 | ``` 46 | 47 | ## ウェブアプリのベンチ 48 | 49 | ab でも ruby スクリプトでもいいけど、もっとトップページだけとか素早くベンチ取る方法用意しといたほうがいいかもしれない 50 | 51 | 52 | ### apache bench 53 | 54 | ただし、apache のインストールが必要(ついでについてくる) 55 | 56 | ``` 57 | $ ab -n 回数 -c 並列数 URL 58 | $ ab -n 回数 -c 並列数 -p POSTするデータのファイル名 -T "application/x-www-form-urlencoded" URL 59 | ``` 60 | あとは -k で keepalive 有効とか cf. http://www.atmarkit.co.jp/ait/articles/0206/29/news001_2.html 61 | 62 | ### スクリプト書く 63 | 64 | ToDo: ab だと URL 代えたりできないのでスクリプトを自前で用意する。 65 | 66 | isucon ではベンチアプリが提供されるから不要かな。 67 | -------------------------------------------------------------------------------- /00.metrics.md: -------------------------------------------------------------------------------- 1 | 統計情報をみるコマンド集。ずっと流しておくべし 2 | 3 | * iostat / iotop 4 | * vmstat 5 | * top 6 | * free 7 | * dstat 8 | 9 | # iostat -dkxt 1 10 | 11 | rkB/s, wkB/s がベンチとった値に近づいてしまっていないか(I/Oつまってないか) 12 | ``` 13 | $ iostat -dkxt 1 14 | 09/30/2013 02:47:10 AM 15 | Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util 16 | sda 0.22 7.99 0.02 5.37 0.71 53.57 20.13 0.00 0.38 0.11 0.06 17 | ``` 18 | 19 | # iotop 20 | 21 | ``` 22 | $ sudo iptop 23 | Total DISK READ : 0.00 B/s | Total DISK WRITE : 15.49 K/s 24 | Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 38.71 K/s 25 | TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 26 | 379 be/3 root 0.00 B/s 0.00 B/s 0.00 % 0.06 % [jbd2/vda1-8] 27 | 12056 be/4 mysql 0.00 B/s 0.00 B/s 0.00 % 0.03 % mysqld --basedir=/usr --datadir=/data/mysql 28 | ``` 29 | 30 | 31 | # vmstat 1 32 | 33 | i/oもメモリもcpuもみたい場合 34 | ``` 35 | $ vmstat 1 36 | procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- 37 | r b swpd free buff cache si so bi bo in cs us sy id wa st 38 | 2 0 0 13665620 299304 8190076 0 0 0 2 0 1 5 2 94 0 0 39 | ``` 40 | 41 | # top -c 42 | 43 | CPUが有効に使われているか。%wa がでかくないか(I/O待ちになってないか) 44 | メモリが空いているか、特にスワップが発生していないか # メモリについては free で見てもよい 45 | ``` 46 | $ top -c # 1 押す 47 | top - 02:49:30 up 19 days, 5:06, 1 user, load average: 0.19, 0.20, 0.24 48 | Tasks: 1008 total, 1 running, 908 sleeping, 0 stopped, 99 zombie 49 | Cpu0 : 0.3%us, 0.7%sy, 0.0%ni, 99.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st 50 | Cpu1 : 0.0%us, 0.3%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st 51 | Mem: 24566292k total, 10903152k used, 13663140k free, 299304k buffers 52 | Swap: 4192956k total, 0k used, 4192956k free, 8190068k cached 53 | 54 | ``` 55 | 56 | # free 57 | 58 | top でも見れるけど. スワップしてないか。メモリ空いてるか。下の例だと Swap - Used があるので Swap してる。 59 | 60 | ``` 61 | $ free 62 | total used free shared buffers cached 63 | Mem: 24566292 14090916 10475376 0 273764 5216856 64 | -/+ buffers/cache: 8600296 15965996 65 | Swap: 4192956 19224 4173732 66 | ``` 67 | 68 | スワップ開放 69 | ``` 70 | $ sudo /sbin/swapoff -a 71 | $ sudo /sbin/swapon -a 72 | ``` 73 | 74 | # dstat 75 | 76 | cf. http://d.hatena.ne.jp/hirose31/20120229/1330501968 77 | 78 | alias 定義 79 | 80 | ``` 81 | alias dstat-full='dstat -Tclmdrn' 82 | alias dstat-mem='dstat -Tclm' 83 | alias dstat-cpu='dstat -Tclr' 84 | alias dstat-net='dstat -Tclnd' 85 | alias dstat-disk='dstat -Tcldr' 86 | ``` 87 | ![dstat](http://cdn-ak.f.st-hatena.com/images/fotolife/h/hirose31/20120229/20120229164539.png) 88 | -------------------------------------------------------------------------------- /01.aws.md: -------------------------------------------------------------------------------- 1 | cf. https://console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1 2 | 3 | ## Instance たちあげ 4 | 5 | **アカウント作っておくこと** 6 | 7 | 基本的にはアカウント作って、そのまま EC2 に行って、Launch Instance からたちあげれば良い。 8 | 右上の Region が Tokyo になっているかだけ確認。 9 | AMI から作るときは左の AMIs から AMI イメージを検索してローンチ 10 | 11 | 鍵が作られるのでそれを使って、ec2-user で入る 12 | 13 | ``` 14 | ssh -v ec2-user@ec2-54-250-225-19.ap-northeast-1.compute.amazonaws.com -i ~/.ssh/microkey.pem 15 | ``` 16 | 17 | あとは、.ssh/authorized_keys にいつも自分が使っている鍵(公開鍵)を突っ込めばよいだろう 18 | 19 | ``` 20 | $ echo '鍵' >> .ssh/authorized_keys 21 | ``` 22 | 23 | 手元の .ssh/config に追加しておくと良さそう 24 | 25 | ``` 26 | Host isucon 27 | Username ec2-user 28 | Hostname ec2-54-250-225-19.ap-northeast-1.compute.amazonaws.com 29 | ``` 30 | 31 | ## AMI作成 32 | 33 | Instance 右クリック > Create Image (AMI) から AMI 作れる 34 | 35 | Permission を Public にするか、特定のユーザを指定して参照できるようにする 36 | 37 | 38 | -------------------------------------------------------------------------------- /01.ssh.md: -------------------------------------------------------------------------------- 1 | 流れ 2 | 3 | 1. isucon サーバの ~/.ssh/authorized_keys に公開鍵を追加し、手元の秘密鍵で isucon サーバに入れるようにする 4 | 2. ssh-agent を起動して、isucon サーバ上からも、手元の秘密鍵で github.com にアクセスできるようにする。 5 | 6 | ### 1. /etc/ssh/ssh_config の設定確認 7 | 8 | ``` 9 | RSAAuthentication yes 10 | PubkeyAuthentication yes 11 | AuthorizedKeysFile .ssh/authorized_keys 12 | ``` 13 | 14 | ```bash 15 | $ sudo /etc/init.d/sshd reload 16 | ``` 17 | 18 | ### 2. ssh-keygen (if you do not have a key) 19 | 20 | cf. http://www.turbolinux.com/support/document/knowledge/152.html 21 | 22 | (1) RSA キーを生成します。 23 | 24 | ```bash 25 | local$ ssh-keygen 26 | ``` 27 | 28 | 出力するファイル名、パスフレーズの入力を求められますが何も入力せずに全てEnteyキーを押下して下さい。 29 | 30 | (2) 公開キーをリモートホストにコピーします。 31 | 32 | ```bash 33 | local$ scp ~/.ssh/id_rsa.pub name@remort.host: 34 | ``` 35 | 36 | (3) リモートホストにログインし、authorized_keys ファイルを生成します。 37 | 38 | ```bash 39 | local$ ssh name@remort.host 40 | remort$ cd .ssh/ 41 | remort$ touch authorized_keys 42 | remort$ chmod 600 authorized_keys 43 | remort$ cat ~/id_rsa.pub >> authorized_keys 44 | remort$ rm ~/id_rsa.pub 45 | ``` 46 | 47 | (4) 設定完了です。動作を確認します。 48 | 49 | ### 3. ssh-agent を使ってリモートでも自分の秘密鍵で github にアクセスする 50 | 51 | 手元の環境で 52 | 53 | ``` 54 | $ ssh-agent 55 | $ ssh-add ~/.ssh/id_rsa 56 | ``` 57 | 58 | .ssh/config に `ForwardAgent yes` を足す 59 | 60 | ``` 61 | Host isucon 62 | User isucon 63 | Hostname xx.xx.xx.xx 64 | ForwardAgent yes 65 | ``` 66 | 67 | ssh isucon して、ssh git@github.com で自分の名前が出てくれれば成功 68 | -------------------------------------------------------------------------------- /02.iptables_selinux.md: -------------------------------------------------------------------------------- 1 | cf. http://d.hatena.ne.jp/uriyuri/20080704/1215164085 2 | 3 | ### iptables 4 | 5 | ```bash 6 | $ /etc/rc.d/init.d/iptables stop 7 | $ chkconfig iptables off 8 | $ chkconfig --list iptables 9 | iptables 0:off 1:off 2:off 3:off 4:off 5:off 6:off 10 | ``` 11 | 12 | ### SELinux 13 | 14 | 動作状態の確認 15 | 16 | ```bash 17 | $ getenforce 18 | Enforcing    SELinux機能は有効でアクセス制御も有効。 19 | permissive   SElinuxはwarningを出すが、アクセス制限は行われません 20 | disabled    SElinux機能・アクセス制御ともに無効 21 | ``` 22 | 23 | SELinuxを一時的に無効化 24 | 25 | ```bash 26 | $ setenforce 0 27 | ``` 28 | -------------------------------------------------------------------------------- /02.kernel.md: -------------------------------------------------------------------------------- 1 | ToDo: もっとたす 2 | 3 | ## network 4 | 5 | somaxcon を増やすのは必須. ポートレンジも増やしたい 6 | 7 | $ vi /etc/sysctl.conf 8 | 9 | ``` 10 | kernel.panic = 60 11 | net.ipv4.tcp_timestamps = 0 12 | # 今回は SYN Flood 来ないだろうしオフ cf. http://ja.wikipedia.org/wiki/SYN_cookies 13 | net.ipv4.tcp_syncookies = 0 14 | 15 | # http://d.hatena.ne.jp/ita-wasa/20090224/1235441693 16 | net.ipv4.ip_local_port_range = 10000 64000 17 | # システムが許容する TIME_WAIT 状態にあるソケットの最大数 18 | net.ipv4.tcp_max_tw_buckets = 2000000 19 | # reuse と recycle cf. http://www.speedguide.net/articles/linux-tweaking-121 20 | # reuse connections in TIME_WAIT state, useful for web servers. 21 | net.ipv4.tcp_tw_reuse = 1 22 | # TCP_TW_RECYCLE could cause some problems when using load balancers. 23 | # net.ipv4.tcp_tw_recycle=1 24 | net.ipv4.tcp_fin_timeout = 15 25 | 26 | # http://ora.geo.jp/?p=465 27 | # CPソケットが受け付けた接続要求を格納するキューの最大長 28 | # cf. http://dsas.blog.klab.org/archives/51977201.html 29 | net.core.somaxconn = 65535 30 | # カーネルがキューイング可能なパケットの最大個数 31 | net.core.netdev_max_backlog = 10000 32 | # ソケット当たりのSYNを受け付けてACKを受け取っていない状態のコネクションの保持可能数 33 | net.ipv4.tcp_max_syn_backlog = 4096 34 | ``` 35 | 36 | 反映 37 | 38 | $ /sbin/sysctl -p 39 | 40 | 41 | ## システム全体のスレッド数 42 | 43 | ユーザの最大プロセス数は ulimit で cf. http://d.hatena.ne.jp/Fiore/20080310/1205155154 44 | 45 | ``` 46 | # cat /proc/sys/kernel/threads-max 47 | 383441 # デフォルトで勝手に計算される. 大抵デフォルトでおk 48 | # vi /etc/sysctl.conf 49 | kernel.threads-max = 383441 50 | ``` 51 | 52 | ## limits 53 | 54 | 1. ユーザが使えるプロセス数 cf. http://d.hatena.ne.jp/Fiore/20080310/1205155154 55 | 1. 特定ユーザが開けるファイルディスクリプタ数 cf. http://d.hatena.ne.jp/akishin999/20130213/1360711554 56 | 57 | ※起動スクリプトの先頭に書かないといけなかった希ガス => そうですね cf. http://yumewaza.yumemi.co.jp/2010/07/limitsconf.html 58 | 59 | ``` 60 | # ulimit -u # プロセス数 61 | 1024 62 | # ulimit -n # ファイルディスクリプタ数 63 | 1024 64 | # vi /etc/security/limits.conf # => ダメ 65 | * soft noproc 15000 66 | * hard noproc 15000 67 | * soft nofile 65536 68 | * hard nofile 65536 69 | # ulimit -u 15000 #=> 起動スクリプトの先頭に書く 70 | # ulimit -n 65536 71 | ``` 72 | 73 | 82 | -------------------------------------------------------------------------------- /03.yum_repo.md: -------------------------------------------------------------------------------- 1 | cf. http://www.tooyama.org/yum-addrepo-epel.html 2 | 3 | ■EPELをwgetしてインストール(64ビット版CentOS 5の場合) 4 | 5 | ```bash 6 | # wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/x86_64/epel-release-5-4.noarch.rpm 7 | # rpm -ivh epel-release-5-4.noarch.rpm 8 | ``` 9 | 10 | ■EPELをwgetしてインストール(32ビット版CentOS 5の場合) 11 | 12 | ```bash 13 | # wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/i386/epel-release-5-4.noarch.rpm 14 | # rpm -ivh epel-release-5-4.noarch.rpm 15 | ``` 16 | 17 | ■EPELをwgetしてインストール(64ビット版CentOS 6の場合) 18 | 19 | ```bash 20 | wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm 21 | rpm -ivh epel-release-6-8.noarch.rpm 22 | ``` 23 | 24 | ■EPELをwgetしてインストール(32ビット版CentOS 6の場合) 25 | 26 | ```bash 27 | # wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/i386/epel-release-6-8.noarch.rpm 28 | # rpm -ivh epel-release-6-8.noarch.rpm 29 | ``` 30 | 31 | ※すでに前のバージョンが入っている場合はrpm -Uvh hogehogeで。 32 | 33 | 34 | cf. http://www.tooyama.org/yum-addrepo-remi.html 35 | 36 | ■remiをwgetしてインストール(64ビット版CentOS 5の場合) 37 | 38 | ```bash 39 | # wget http://rpms.famillecollet.com/enterprise/remi-release-5.rpm 40 | # rpm -ivh remi-release-5.rpm 41 | ``` 42 | 43 | ■remiをwgetしてインストール(32ビット版CentOS 6の場合) 44 | 45 | ```bash 46 | wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm 47 | rpm -ivh remi-release-6.rpm 48 | ``` 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /04.tmux.md: -------------------------------------------------------------------------------- 1 | tmux は設定ファイル全員で共通になっちゃう(プロセス毎にわけられない)けど、どうする? ちなみに瀬尾は C-t 派 2 | 3 | $ sudo yum install tmux 4 | 5 | $ curl https://raw.github.com/sonots/.dotfiles/ea537ceab931a7a8de171ba230e649b0a7979ef9/.tmux.conf > ~/.tmux.conf 6 | 7 | ``` 8 | -f でファイル指定してもNG? > NGです 9 | ``` 10 | -------------------------------------------------------------------------------- /05.node.md: -------------------------------------------------------------------------------- 1 | nodeenv などでビルドしようかと思ったが、python が必要だったりめんどくさすぎる。バイナリダウンロードで良さそうな気がする 2 | 3 | ## node バイナリのインストール 4 | 5 | http://nodejs.org/download/ からバイナリダウンロード. npm もついてくる 6 | 7 | ``` 8 | cd /tmp 9 | wget http://nodejs.org/dist/v0.10.18/node-v0.10.18-linux-x64.tar.gz 10 | tar zxvf node-v0.10.18-linux-x64.tar.gz 11 | sudo mv node-v0.10.18-linux-x64 /opt/ 12 | sudo ln -s /opt/node-v0.10.18-linux-x64 /opt/node 13 | echo 'export $PATH=/opt/node/bin:$PATH' >> ~/.bash_profile 14 | exec $SHELL -l 15 | ``` 16 | 17 | ## node のビルド 18 | 19 | python いるとかめんどくせーーー 20 | 21 | 1、node をビルドするために python のインストール 22 | 23 | See [04.python.md](04.python.md) 24 | 25 | 2a、nodeenv とかなしでビルド 26 | 27 | http://nodejs.org/download/ から source code を落としてビルド 28 | 29 | ``` 30 | cd /tmp 31 | wget http://nodejs.org/dist/v0.10.18/node-v0.10.18.tar.gz 32 | tar zxvf node-v0.10.18.tar.gz 33 | cd node-v0.10.18 34 | ./configure 35 | make 36 | sudo make install 37 | ``` 38 | 39 | 2b、nodebrew でビルド 40 | 41 | ``` 42 | curl -L git.io/nodebrew | perl - setup 43 | export PATH=$HOME/.nodebrew/current/bin:$PATH 44 | nodebrew install latest 45 | nodebrew use latest 46 | ``` 47 | 48 | うまくいかん!!めんどい!!fuck!! 49 | 50 | 2c、nodeenv でビルド 51 | 52 | ``` 53 | sudo easy_install nodeenv 54 | pyenv rehash 55 | nodeenv --node=0.10.15 --npm=1.3.5 ~/.nodeenv/isucon 56 | source ~/.nodeenv/isucon/bin/activate 57 | ``` 58 | 59 | うまくいったきもするけど、activate とかキモいし、あれ?なんかへん?fuck!! 60 | 61 | 62 | -------------------------------------------------------------------------------- /05.python.md: -------------------------------------------------------------------------------- 1 | http://qiita.com/kakkunpakkun/items/ac6cb84e37d09d768b46 を参考に https://github.com/yyuu/pyenv で入れることにする 2 | 3 | ビルドに必要なものをインストール 4 | 5 | ``` 6 | sudo yum install readline-devel zlib zlib-devel bzip2-devel sqlite sqlite-devel 7 | wget http://curl.haxx.se/ca/cacert.pem 8 | mv cacert.pem /usr/local/etc/openssl/certs/cert.pem 9 | export SSL_CERT_FILE=/usr/local/etc/openssl/certs/cert.pem 10 | ``` 11 | 12 | pyenv をインストール 13 | 14 | ``` 15 | git clone git@github.com:yyuu/pyenv.git ~/.pyenv 16 | echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bash_profile 17 | echo 'eval "$(pyenv init -)"' >> ~/.bash_profile 18 | exec $SHELL -l 19 | ``` 20 | 21 | python をビルド 22 | 23 | ``` 24 | CONFIGURE_OPTS="--with-zlib --with-bz2 --with-ssl --with-readline --with-ncurses --with-expat --with-sqlite3 --with-crypt --with-md5 --with-sha" pyenv install 2.7.5 25 | ``` 26 | 27 | python を使う 28 | 29 | ``` 30 | pyenv global 2.7.5 31 | pyenv rehash 32 | ``` 33 | 34 | http://qiita.com/kakkunpakkun/items/ac6cb84e37d09d768b46 35 | -------------------------------------------------------------------------------- /05.ruby.md: -------------------------------------------------------------------------------- 1 | ビルドに必要なものをインストール 2 | 3 | ``` 4 | sudo yum groupinstall -y "Development tools" 5 | sudo yum -y install openssl openssl-devel readline-devel readline compat-readline5 libxml2-devel libxslt-devel 6 | ``` 7 | 8 | rbenv をインストール 9 | 10 | ``` 11 | git clone https://github.com/sstephenson/rbenv.git ~/.rbenv 12 | echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile 13 | echo 'eval "$(rbenv init -)"' >> ~/.bash_profile 14 | exec $SHELL -l 15 | git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build 16 | ``` 17 | 18 | ruby をビルド 19 | 20 | ``` 21 | # RUBY_CONFIGURE_OPTS="--enable-load-relative" rbenv install 2.0.0-p247 22 | # => CentOS 62 x86_64 でビルド済みのものがあります. 使えるといいんだけど... 23 | git clone https://github.com/sonots/ruby-2.0.0-p247-x86_64.git ~/.rbenv/versions/2.0.0-p247 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /05.ruby_metrics.md: -------------------------------------------------------------------------------- 1 | https://gist.github.com/sonots/3cde1eae9a6ff051e198 2 | 3 | * rack-ltsv_logger 4 | * sinatra-template_metrics 5 | * mysql2-metrics 6 | 7 | Google Spread Sheet でグラフ化 8 | 9 | * https://docs.google.com/spreadsheets/d/1E_1VFHAZD-_uWTtEJsUb8Mh3fUzYsZCUiy5K4Ncmcnw/edit#gid=411726927 10 | * td-agent.conf https://github.com/niku4i/chef-isucon4/blob/master/site-cookbooks/isucon-bootstrap/files/default/td-agent-isucon.conf 11 | 12 | https://github.com/tmm1/stackprof 13 | 14 | ```ruby 15 | # config.ru 16 | require 'stackprof' 17 | use StackProf::Middleware, enabled: true, 18 | mode: :cpu, 19 | interval: 1000, 20 | save_every: 5 21 | ``` 22 | 23 | ``` 24 | $ bundle stackprof tmp/stackprof-cpu-*.dump --text 25 | ``` 26 | 27 | https://github.com/kainosnoema/rack-lineprof 28 | 29 | ```ruby 30 | # config.ru 31 | require 'rack-lineprof' 32 | # use Rack::LineProf, profile: /.*/ 33 | use Rack::LineProf, profile: /app\.rb$|.*erb$/ 34 | ``` 35 | 36 | STDOUT に出るので手でアクセスして確認する(か、全部出しておいてあとで確認するか) 37 | 38 | その他ポイント 39 | 40 | * erb やめると大分あがる (sprintf > gsub) 41 | * sinatra やめて生 rack にすると大分あがる可能性 (時間かかる) 42 | * RACK_ENV=production にするとめっちゃあがる 43 | -------------------------------------------------------------------------------- /05.ruby_newrelic.md: -------------------------------------------------------------------------------- 1 | newrelic を使うモチベーション 2 | 3 | * ホスト全体で集計が取れるので、ISUCON 本戦で良さそう 4 | 5 | * 平均時間順ならあるが、合計時間順でのソート結果が見れなかった。ISUCONではベンチ流している間の合計時間順で見たいけど、一般的にはそんなユースケースないもんな... 6 | 7 | * リクエストの内訳もみれるし 8 | 9 | * テンプレートエンジンの内訳みれなかった。ActiveRecord じゃないと DB の内訳みれなかった。 10 | 11 | あと、5分待たないと全部の集計結果が出てこないような?? 12 | 13 | 結論:ISUCON ではあまり役に立たなさそう。全ホストから nginx, mysql のログをあつめて集計してソートするスクリプト用意したほうが良さそう。 14 | 15 | ## 目次 16 | 17 | * [設定方法](https://github.com/sonots/isucon3_cheatsheet/blob/master/05.ruby_newrelic.md#%E8%A8%AD%E5%AE%9A%E6%96%B9%E6%B3%95) 18 | * [見方](https://github.com/sonots/isucon3_cheatsheet/blob/master/05.ruby_newrelic.md#%E8%A6%8B%E6%96%B9) 19 | * [ホストメトリクス](https://github.com/sonots/isucon3_cheatsheet/blob/master/05.ruby_newrelic.md#%E3%83%9B%E3%82%B9%E3%83%88%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9) 20 | 21 | ## 設定方法 22 | 23 | sinatra でも rails でも gem をインストールして newrelic.yml を置くだけ。 24 | 25 | newrelic.com でアカウント作ってログイン 26 | 27 | ログイン後画面で add more 28 | 29 | ![2014-11-01 20 02 08](https://cloud.githubusercontent.com/assets/2290461/4871454/160c444a-61b7-11e4-933a-b456e99886cb.png) 30 | 31 | 設定方法のページが出て来る 32 | 33 | ![2014-11-01 20 03 54](https://cloud.githubusercontent.com/assets/2290461/4871455/17ad4c9a-61b7-11e4-9724-6cb8bca086f7.png) 34 | 35 | 書いてあるように Gemfile に 36 | 37 | ``` 38 | gem 'newrelic_rpm' 39 | ``` 40 | 41 | を追加して、bundle。sinatra の場合 config.ru あたりで require 42 | 43 | ``` 44 | require 'newrelic_rpm' 45 | ``` 46 | 47 | newrelic.yml をダウンロードして、"My Application" の文字をお好みで変更. That's it. 48 | 49 | # 見方 50 | 51 | 見るのは Overview と Transaction、あとは Ruby VMs ぐらい. あー、Sum Response Time がないから ISUCON では使いづらいかもなー. Most Time Consuming から読み解くのは難しいかもしれない 52 | 53 | Overview 54 | 55 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871503/10875d76-61bb-11e4-9647-072b85fc4d6b.png) 56 | 57 | Transactions (Most Time Consuming) 58 | 59 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871779/cec760fe-61c7-11e4-8bae-7f88fb0e61cc.png) 60 | 61 | Transactions (Slowest Average Resonse Time) 62 | 63 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871781/db6aab36-61c7-11e4-8e7a-b1cd40203655.png) 64 | 65 | FYI: nginx のログから集計とった場合. ベンチ流している間の合計時間順でのソートが見たい 66 | 67 | ``` 68 | $ cat /var/log/nginx/access.log | lltsv -k uri,reqtime -K | ruby stat.rb | lltsv -k sum,count,mean,path -K | sort -n -r 69 | sum count avg 70 | 355.7159999999995 1201 0.29618318068276395 /login 71 | 63.59299999999999 208 0.30573557692307685 /mypage 72 | 23.611000000000107 2195 0.010756719817767702 / 73 | 18.965 1 18.965 /report 74 | 0.0 2403 0.0 /stylesheets/isucon-bank.css 75 | 0.0 2403 0.0 /stylesheets/bootstrap.min.css 76 | 0.0 2403 0.0 /stylesheets/bootflat.min.css 77 | 0.0 2403 0.0 /images/isucon-bank.png 78 | ``` 79 | 80 | Ruby VMs 81 | 82 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871508/44662e92-61bb-11e4-8a0a-782f9e2b7a2e.png) 83 | 84 | 85 | # ホストメトリクス 86 | 87 | cpu usage など、アプリじゃなくてホストのメトリクスもグラフにできる 88 | 89 | https://rpm.newrelic.com/accounts/201984/servers/get_started#platform=rhel 90 | 91 | ![2014-11-01 20 23 42](https://cloud.githubusercontent.com/assets/2290461/4871487/c974cd52-61b9-11e4-8e87-0039d3e93380.png) 92 | 93 | SERVERS タブから見る 94 | 95 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871809/452a48dc-61c9-11e4-81cb-eff42be956ab.png) 96 | 97 | 98 | ![image](https://cloud.githubusercontent.com/assets/2290461/4871807/3c957318-61c9-11e4-90d9-a249bdec5d29.png) 99 | -------------------------------------------------------------------------------- /06.mysql_5.5.md: -------------------------------------------------------------------------------- 1 | 昔の文書 2 | 3 | ## MySQL5.5インストール 4 | 5 | 1、MySQL5.5 をインストール ※ 5.5 は remi にある. 5.6 はない 6 | 7 | ``` 8 | sudo yum upgrade --enablerepo=remi,epel mysql-server mysql-devel mysql 9 | ``` 10 | 11 | 下の /etc/my.cnf コピって起動. 12 | 13 | ## MySQL5.5へのアップグレード 14 | 15 | cf. http://www.softel.co.jp/blogs/tech/archives/2288 16 | 17 | 1、ダンプを作成(念のため) 18 | 19 | ``` 20 | sudo mysqldump -u root -p --all-database > /tmp/all.dump 21 | ``` 22 | 2、my.cnfをバックアップ(念のため) 23 | 24 | ``` 25 | sudo cp /etc/my.cnf /etc/my.cnf.backup 26 | ``` 27 | 28 | 3、MySQLサーバーを停止 29 | 30 | ``` 31 | sudo /etc/init.d/mysql stop 32 | ``` 33 | 34 | 4、MySQL5.1(古い方)をアンインストール 35 | 36 | ``` 37 | sudo yum remove MySQL-server-community 38 | ``` 39 | 40 | 5、MySQL5.5(新しいの)をインストール ※ 5.5 は remi にある. 5.6 はない 41 | 42 | ``` 43 | sudo yum upgrade --enablerepo=remi,epel mysql-server mysql-devel mysql 44 | ``` 45 | 46 | 5.5、ログファイルを綺麗にする(サイズ変えるので) 47 | 48 | ``` 49 | sudo rm -f /var/lib/mysql/ib_logfile[01] 50 | ``` 51 | 52 | 6、mysql_upgrade を実行する 53 | 54 | mysql_upgradeはテーブルをチェックして、必要なら修復してくれる。ユーザー、パスワードが設定してあったら、引数につける。 55 | 56 | ``` 57 | sudo /etc/init.d/mysql start sudo mysql_upgrade -u root -p 58 | ``` 59 | 60 | 7、下の /etc/my.cnf をコピー 61 | 62 | 8、restart 63 | 64 | APPENDIX、ruby の mysql2 の再ビルドが必要. gem uninstall mysql2 => bundle install 65 | 66 | ## ■ /etc/my.cnf 67 | mysql 5.1.38 以上の Innodb Plugin もしくは 5.5 以上が必要 68 | cf. http://nippondanji.blogspot.jp/2010/03/innodb-plugin.html 69 | 70 | 一部解説 71 | cf. http://yakst.com/ja/posts/65 72 | 73 | ``` 74 | [mysqld] 75 | datadir=/var/lib/mysql 76 | tmpdir=/var/tmp 77 | socket=/var/lib/mysql/mysql.sock 78 | user=mysql 79 | # Disabling symbolic-links is recommended to prevent assorted security risks 80 | symbolic-links=0 81 | 82 | slow_query_log = 1 83 | slow_query_log_file = /var/lib/mysql/slow.log 84 | long_query_time = 0 85 | # log-queries-not-using-indexes # sudo mysqldumpslow -s c /var/lib/mysql/slow.log 86 | 87 | max_connections=1024 88 | thread_cache = 600 89 | thread_concurrency = 8 90 | table_cache = 8192 91 | back_log = 10240 92 | 93 | query_cache_size = 0 94 | query_cache_type = 0 95 | 96 | # global buffer 97 | key_buffer_size = 32M 98 | innodb_buffer_pool_size = 4G # メモリ全体の75%ほど 99 | innodb_log_buffer_size = 8M 100 | innodb_additional_mem_pool_size = 10M 101 | 102 | # thread buffer 103 | sort_buffer_size = 1M 104 | myisam_sort_buffer_size = 64K 105 | read_buffer_size = 1M 106 | 107 | # innodb 108 | innodb_log_files_in_group = 2 109 | innodb_log_file_size = 128M # ディスク食うので注意 110 | innodb_flush_log_at_trx_commit = 0 # Don't use 0 except ISUCON. cf. http://www.mysqlpracticewiki.com/index.php/--innodb-flush-log-at-trx-commit 111 | innodb_lock_wait_timeout = 5 112 | innodb_flush_method = O_DIRECT 113 | innodb_adaptive_hash_index = 0 114 | innodb_thread_concurrency = 30 115 | innodb_read_io_threads = 16 116 | innodb_write_io_threads = 16 117 | innodb_io_capacity = 200 118 | innodb_stats_on_metadata = Off 119 | 120 | # innodb plugin for mysql >= 5.1.38, comment out for mysql >= 5.5 because it is default. 121 | # ignore-builtin-innodb 122 | # plugin-load = innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so 123 | 124 | [mysqld_safe] 125 | log-error=/var/log/mysqld.log 126 | pid-file=/var/run/mysqld/mysqld.pid 127 | ``` 128 | 129 | ``` 130 | mysql> show plugins 131 | ``` 132 | InnoDB が出てこなかったら何かおかしい. たぶんコレ cf. http://blog.flatlabs.net/20100727_212649/ 133 | 134 | ``` 135 | $ tail -100 /var/log/mysqld.log 136 | InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytesInnoDB: than specified in the .cnf file 0 1073741824 bytes! 137 | ``` 138 | 139 | ログファイルを綺麗にしないとダメらしい. stop して rm する 140 | ``` 141 | rm /var/lib/mysql/ib_logfile* 142 | ``` 143 | 144 | ## 確認コマンド 145 | 146 | テーブル状態 innodb か、index 貼ってあるか、など 147 | 148 | ``` 149 | mysql> show table status; 150 | ``` 151 | 152 | ## ケースバイケース 153 | 154 | ### Query Cache 155 | Ref: 過去問 http://d.hatena.ne.jp/sfujiwara/20110827/1314460582 156 | 157 | ``` 158 | query_cache_size = 16M # default: 10MB but cache OFF 159 | query_cache_type = 1 # 1 = ON 160 | ``` 161 | 162 | 確認 163 | ``` 164 | mysql> SHOW VARIABLES LIKE 'query_cache%'; 165 | mysql> SHOW STATUS LIKE 'Qcache%'; 166 | ``` 167 | 168 | 更新系クエリ多い場合はOFFの方がいいけど、SELECT多い場合はキャッシュ有効にするとスコアあがるかも 169 | 170 | ### 最終判定前にキャッシュ温める 171 | OS再起動したのちスコア測定するので、mysql起動後にキャッシュ温める戦略 172 | Ref: http://www.songmu.jp/riji/archives/2011/08/isucon.html 173 | Ref: http://d.hatena.ne.jp/hirose31/20100728/1280313859 # こっちのほうが分かりやすい 174 | 175 | ``` 176 | CREATE TABLE _preload LIKE huge_table; 177 | ALTER TABLE _preload ENGINE = BLACKHOLE; 178 | INSERT INTO _preload SELECT * FROM huge_table; 179 | DROP TABLE _preload; 180 | ``` 181 | -------------------------------------------------------------------------------- /06.mysql_5.6.md: -------------------------------------------------------------------------------- 1 | ## MySQL5.6インストール 2 | 3 | 1. yum repository の追加 4 | 5 | ``` 6 | sudo yum install http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm 7 | ``` 8 | 9 | 10 | 2、MySQL5.6 をインストール 11 | 12 | ``` 13 | sudo yum install mysql mysql-devel mysql-server mysql-utilities 14 | ``` 15 | 16 | 下の /usr/my.cnf コピって起動. 17 | 18 | ## MySQL5.6へのアップグレード 19 | 20 | cf. http://www.softel.co.jp/blogs/tech/archives/2288 21 | 22 | 1、ダンプを作成(念のため) 23 | 24 | ``` 25 | sudo mysqldump -u root -p --all-database > /tmp/all.dump 26 | ``` 27 | 2、my.cnfをバックアップ(念のため) 28 | 29 | ``` 30 | sudo cp /etc/my.cnf /etc/my.cnf.backup 31 | ``` 32 | 33 | 3、MySQLサーバーを停止 34 | 35 | ``` 36 | sudo /etc/init.d/mysql stop 37 | ``` 38 | 39 | 4、MySQL5.5(古い方)をアンインストール 40 | 41 | ``` 42 | sudo yum remove MySQL-server-community 43 | ``` 44 | 45 | 5、MySQL5.6(新しいの)をインストール 46 | 47 | ``` 48 | sudo yum upgrade mysql mysql-devel mysql-server mysql-utilities 49 | ``` 50 | 51 | 5.5、ログファイルを綺麗にする(サイズ変えるので) 52 | 53 | ``` 54 | sudo rm -f /var/lib/mysql/ib_logfile[01] 55 | ``` 56 | 57 | 6、mysql_upgrade を実行する 58 | 59 | mysql_upgradeはテーブルをチェックして、必要なら修復してくれる。ユーザー、パスワードが設定してあったら、引数につける。 60 | 61 | ``` 62 | sudo /etc/init.d/mysql start sudo mysql_upgrade -u root -p 63 | ``` 64 | 65 | 7、下の /etc/my.cnf をコピー 66 | 67 | 8、restart 68 | 69 | APPENDIX、ruby の mysql2 の再ビルドが必要. gem uninstall mysql2 => bundle install 70 | 71 | ## ■ /usr/my.cnf 72 | 73 | 一部解説 74 | cf. http://yakst.com/ja/posts/65 75 | 76 | ``` 77 | [mysqld] 78 | log-bin = mysqld-bin 79 | 80 | datadir=/var/lib/mysql 81 | # datadir=/dev/shm/mysql # use tmpfs and store to disk on shutdown and load from dist on shutdown 82 | socket=/var/lib/mysql/mysql.sock 83 | user=mysql 84 | # Disabling symbolic-links is recommended to prevent assorted security risks 85 | symbolic-links=0 86 | 87 | slow_query_log = 1 88 | slow_query_log_file = /var/lib/mysql/slow.log 89 | long_query_time = 0 90 | # log-queries-not-using-indexes # show queries not using index 91 | 92 | # general-log=TRUE 93 | # general-log-file=/var/lib/mysql/query.log 94 | 95 | max_connections=1024 96 | thread_cache = 600 97 | thread_concurrency = 8 98 | #table_cache = 8192 99 | table_open_cache = 8192 100 | back_log = 10240 101 | 102 | query_cache_size = 0 103 | query_cache_type = 0 104 | 105 | # global buffer 106 | key_buffer_size = 32M 107 | innodb_buffer_pool_size = 4G # メモリ全体の75%ほど 108 | innodb_log_buffer_size = 8M 109 | #innodb_additional_mem_pool_size = 10M 110 | innodb_file_per_table = 0 111 | 112 | sync_binlog = 0 113 | 114 | # innodb 115 | innodb_log_files_in_group = 2 116 | innodb_log_file_size = 128M 117 | innodb_flush_log_at_trx_commit = 0 # special change for isucon 118 | innodb_lock_wait_timeout = 5 119 | innodb_flush_method = O_DIRECT 120 | innodb_adaptive_hash_index = 0 121 | innodb_thread_concurrency = 30 122 | innodb_read_io_threads = 16 123 | innodb_write_io_threads = 16 124 | innodb_io_capacity = 200 125 | innodb_stats_on_metadata = Off 126 | 127 | # innodb plugin for mysql >= 5.1.38, comment out for mysql >= 5.5 because it is default. 128 | # ignore-builtin-innodb 129 | # plugin-load = innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so 130 | 131 | [mysqld_safe] 132 | # log-error=/var/log/mysqld.log 133 | #pid-file=/var/run/mysqld/mysqld.pid 134 | pid-file=/var/lib/mysql/mysqld.pid 135 | ``` 136 | 137 | ``` 138 | $ tail -100 /var/log/mysqld.log 139 | InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytesInnoDB: than specified in the .cnf file 0 1073741824 bytes! 140 | ``` 141 | 142 | ログファイルを綺麗にしないとダメらしい. stop して rm する 143 | ``` 144 | rm /var/lib/mysql/ib_logfile* 145 | ``` 146 | 147 | ## 確認コマンド 148 | 149 | テーブル状態 innodb か、index 貼ってあるか、など 150 | 151 | ``` 152 | mysql> show table status; 153 | mysql> show create table xxx; 154 | ``` 155 | 156 | 157 | ## ケースバイケース 158 | 159 | ### Query Cache 160 | Ref: 過去問 http://d.hatena.ne.jp/sfujiwara/20110827/1314460582 161 | 162 | ``` 163 | query_cache_size = 16M # default: 10MB but cache OFF 164 | query_cache_type = 1 # 1 = ON 165 | ``` 166 | 167 | 確認 168 | ``` 169 | mysql> SHOW VARIABLES LIKE 'query_cache%'; 170 | mysql> SHOW STATUS LIKE 'Qcache%'; 171 | ``` 172 | 173 | 更新系クエリ多い場合はOFFの方がいいけど、SELECT多い場合はキャッシュ有効にするとスコアあがるかも 174 | 175 | ### 最終判定前にキャッシュ温める 176 | OS再起動したのちスコア測定するので、mysql起動後にキャッシュ温める戦略 177 | Ref: http://www.songmu.jp/riji/archives/2011/08/isucon.html 178 | Ref: http://d.hatena.ne.jp/hirose31/20100728/1280313859 # こっちのほうが分かりやすい 179 | 180 | ``` 181 | CREATE TABLE _preload LIKE huge_table; 182 | ALTER TABLE _preload ENGINE = BLACKHOLE; 183 | INSERT INTO _preload SELECT * FROM huge_table; 184 | DROP TABLE _preload; 185 | ``` 186 | -------------------------------------------------------------------------------- /06.mysql_repl.md: -------------------------------------------------------------------------------- 1 | http://hs-www.hyogo-dai.ac.jp/~kawano/?Install%20Log%2FCentOS5%2FMySQL_repl 2 | 3 | 非同期レプリ。準同期は 6.0 からかプラグインを入れて試せる。同期レプリは? 4 | 5 | ### master 6 | 7 | repl 用ユーザの作成 8 | 9 | ``` 10 | $ mysql -uroot 11 | mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%' IDENTIFIED BY 'password'; 12 | ``` 13 | 14 | /etc/my.cnf 15 | 16 | ``` 17 | [mysqld] 18 | server-id=1 19 | sync_binlog=0 # 0 is only for isucon! 20 | log-bin=mysqld-bin 21 | log-bin-index=mysqld-bin 22 | relay-log=relay-bin 23 | relay-log-index=relay-bin 24 | 25 | datadir=/var/lib/mysql 26 | tmpdir=/var/tmp 27 | socket=/var/lib/mysql/mysql.sock 28 | user=mysql 29 | # Disabling symbolic-links is recommended to prevent assorted security risks 30 | symbolic-links=0 31 | 32 | slow_query_log = 1 33 | slow_query_log_file = /var/lib/mysql/slow.log 34 | long_query_time = 0.1 35 | log-queries-not-using-indexes # sudo mysqldumpslow -s c /var/lib/mysql/slow.log 36 | 37 | max_connections=1024 38 | thread_cache = 600 39 | thread_concurrency = 8 40 | table_cache = 8192 # 5.5 41 | # table_open_cache = 8192 # 5.6 42 | back_log = 10240 43 | 44 | query_cache_size = 0 45 | query_cache_type = 0 46 | 47 | # global buffer 48 | key_buffer_size = 32M 49 | innodb_buffer_pool_size = 10G # メモリ全体の75%ほど 50 | innodb_log_buffer_size = 8M 51 | innodb_additional_mem_pool_size = 10M 52 | 53 | # thread buffer 54 | sort_buffer_size = 1M 55 | myisam_sort_buffer_size = 64K 56 | read_buffer_size = 1M 57 | 58 | # innodb 59 | innodb_log_files_in_group = 3 60 | innodb_log_file_size = 512M # ディスク食うので注意 61 | innodb_flush_log_at_trx_commit = 0 # Don't use 0 except ISUCON. cf. http://www.mysqlpracticewiki.com/index.php/--innodb-flush-log-at-trx-commit 62 | innodb_lock_wait_timeout = 5 63 | innodb_flush_method = O_DIRECT 64 | innodb_adaptive_hash_index = 0 65 | innodb_thread_concurrency = 30 66 | innodb_read_io_threads = 16 67 | innodb_write_io_threads = 16 68 | innodb_io_capacity = 200 69 | innodb_stats_on_metadata = Off 70 | 71 | # innodb plugin for mysql >= 5.1.38, comment out for mysql >= 5.5 because it is default. 72 | # ignore-builtin-innodb 73 | # plugin-load = innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so 74 | 75 | [mysqld_safe] 76 | log-error=/var/log/mysqld.log 77 | pid-file=/var/run/mysqld/mysqld.pid 78 | ``` 79 | 80 | ### slave 81 | 82 | マスタ設定 83 | ``` 84 | mysql> CHANGE MASTER TO MASTER_HOST = 'master', MASTER_USER = 'repl', MASTER_PASSWORD = 'password'; 85 | ``` 86 | 87 | スレーブを開始する 88 | ``` 89 | mysql> START SLAVE; 90 | mysql> SHOW SLAVE STATUS\G; 91 | ``` 92 | 93 | ``` 94 | [mysqld] 95 | server-id=2 96 | read_only 97 | log-slave-updates 98 | log-bin=mysqld-bin 99 | log-bin-index=mysqld-bin 100 | relay-log=relay-bin 101 | relay-log-index=relay-bin 102 | master-host=ip_addr_of_master 103 | master-user=repl 104 | master-password=password 105 | 106 | datadir=/var/lib/mysql 107 | tmpdir=/var/tmp 108 | socket=/var/lib/mysql/mysql.sock 109 | user=mysql 110 | # Disabling symbolic-links is recommended to prevent assorted security risks 111 | symbolic-links=0 112 | 113 | slow_query_log = 1 114 | slow_query_log_file = /var/lib/mysql/slow.log 115 | long_query_time = 0.1 116 | log-queries-not-using-indexes # sudo mysqldumpslow -s c /var/lib/mysql/slow.log 117 | 118 | max_connections=1024 119 | thread_cache = 600 120 | thread_concurrency = 8 121 | table_cache = 8192 122 | back_log = 10240 123 | 124 | query_cache_size = 0 125 | query_cache_type = 0 126 | 127 | # global buffer 128 | key_buffer_size = 32M 129 | innodb_buffer_pool_size = 10G # メモリ全体の75%ほど 130 | innodb_log_buffer_size = 8M 131 | innodb_additional_mem_pool_size = 10M 132 | 133 | # thread buffer 134 | sort_buffer_size = 1M 135 | myisam_sort_buffer_size = 64K 136 | read_buffer_size = 1M 137 | 138 | # innodb 139 | innodb_log_files_in_group = 3 140 | innodb_log_file_size = 512M # ディスク食うので注意 141 | innodb_flush_log_at_trx_commit = 0 # Don't use 0 except ISUCON. cf. http://www.mysqlpracticewiki.com/index.php/--innodb-flush-log-at-trx-commit 142 | innodb_lock_wait_timeout = 5 143 | innodb_flush_method = O_DIRECT 144 | innodb_adaptive_hash_index = 0 145 | innodb_thread_concurrency = 30 146 | innodb_read_io_threads = 16 147 | innodb_write_io_threads = 16 148 | innodb_io_capacity = 200 149 | innodb_stats_on_metadata = Off 150 | 151 | # innodb plugin for mysql >= 5.1.38, comment out for mysql >= 5.5 because it is default. 152 | # ignore-builtin-innodb 153 | # plugin-load = innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so 154 | 155 | [mysqld_safe] 156 | log-error=/var/log/mysqld.log 157 | pid-file=/var/run/mysqld/mysqld.pid 158 | ``` 159 | 160 | ## Troubleshooting 161 | 162 | 1. /var/log/mysqld.log を見る 163 | 2. データおかしかったら最終手段として sudo rm -r /var/lib/mysql/* && sudo mysql_install_db 164 | 3. master-host 設定は >= 5.5 でもう使えない cf. http://do9b.wordpress.com/2012/06/28/mysql5-5%E3%81%ABupgrade%E3%81%97%E3%81%9F%E3%82%89%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E3%80%82replication/ 165 | -------------------------------------------------------------------------------- /06.mysql_stat.md: -------------------------------------------------------------------------------- 1 | mysql の状態をみるコマンド集。 2 | 3 | ## query log 4 | 5 | /etc/my.cnf 6 | 7 | ``` 8 | log=/var/log/mysql/mysql_query.log 9 | ``` 10 | 11 | ファイルを作っておく必要がある 12 | 13 | ``` 14 | $ sudo mkdir /var/log/mysql 15 | $ sudo touch /var/log/mysql/mysql_query.log 16 | $ sudo chown -R mysql /var/log/mysql 17 | ``` 18 | 19 | /etc/init.d/mysqld #やらなくても出る気がするけどな 20 | 21 | ```diff 22 | - $exec --datadir="$datadir" --socket="$socketfile" \ 23 | + $exec --log --datadir="$datadir" --socket="$socketfile" \ 24 | ``` 25 | 26 | cf. http://itchronicle.com/2012/10/20/63 27 | 28 | ## slow log 29 | 30 | /etc/my.cnf 31 | 32 | ``` 33 | slow_query_log = 1 34 | slow_query_log_file = /var/lib/mysql/slow.log 35 | long_query_time = 0.1 36 | log-queries-not-using-indexes 37 | ``` 38 | 39 | /var/lib/mysql/slow.log を見る. `log-queries-not-using-indexes` で index 使ってないやつがスロークエリとして出る。 40 | 41 | ## mysqldumpslow 42 | 43 | mysqldumpslow を使ってレポートを出す. クエリの発行回数、平均時間(トータル時間)が見れる。-s c でカウント順、-s at で平均時間順、-s t でトータル時間順、-s ar で取得した平均行数順、-s r でトータル行数順にソートできる。 44 | 45 | ``` 46 | $ sudo mysqldumpslow -s t /var/lib/mysql/slow.log 47 | Count: 665 Time=0.06s (40s) Lock=0.00s (0s) Rows=100.0 (66500), isucon[isucon]@localhost 48 | SELECT * FROM memos WHERE is_private=N ORDER BY created_at DESC, id DESC LIMIT N OFFSET N 49 | 50 | Count: 1232 Time=0.02s (25s) Lock=0.00s (0s) Rows=110.3 (135858), isucon[isucon]@localhost 51 | SELECT id, content, is_private, created_at, updated_at FROM memos WHERE user=N ORDER BY created_at 52 | 53 | Count: 1227 Time=0.02s (23s) Lock=0.00s (0s) Rows=1.0 (1227), isucon[isucon]@localhost 54 | SELECT count(*) AS c FROM memos WHERE is_private=N 55 | ``` 56 | 57 | クエリ時間は短いが、クエリ数が多いやつなどもあるので、1度 `long_query_time = 0` にして mysqldumpslow で全クエリを対象に集計してみて、そのあとは 0.1 と `log-queries-not-using-indexes` で tail -F しておくとよさそう。 58 | 59 | 60 | ## mysql の内部状態 61 | 62 | See [06.mysql_tmc.md](06.mysql_tmc.md) 63 | 64 | ## show processlist 65 | これを数回~数十回叩いてみて、 よく出ているクエリは、遅かったり量が多かったりする「チューニング候補」になる。 66 | 67 | ``` 68 | show full processlist; 69 | ``` 70 | 71 | showprocesslist.sh 定期実行するスクリプト cf. http://treeapps.hatenablog.com/entry/20120124/p1 72 | 73 | ```bash 74 | #!/bin/sh 75 | interval=$1 76 | # インターバルの単位は秒で初期値は1秒 77 | test -z $interval && interval=1 78 | watch -n $interval --differences "mysql -uroot -Dtest --table -Be 'show processlist;'" 79 | ``` 80 | 81 | ## pt-query-digest 82 | 83 | ブログ見てるとこれを使ってる人が多いみたい 84 | 85 | - studioさん http://studio3104.hatenablog.com/entry/20121002/1349166827 86 | - きょうへいさん http://muscle27.hateblo.jp/entry/2013/03/19/023638 87 | 88 | インストール 89 | 90 | 91 | ``` 92 | sudo yum localinstall -y http://percona.com/get/percona-toolkit.rpm 93 | ``` 94 | 95 | `long_query_time = 0` にして全クエリを出力した slow log にかけてみる 96 | 97 | ``` 98 | $ pt-query-digest slow.log 99 | # 35.5s user time, 40ms system time, 24.99M rss, 208.00M vsz 100 | # Current date: Sun Sep 14 01:19:01 2014 101 | # Hostname: xxxxxx 102 | # Files: slow.log 103 | # Overall: 154.58k total, 15 unique, 2.53k QPS, 1.97x concurrency ________ 104 | # Time range: 2014-09-14 01:00:00 to 01:01:01 105 | # Attribute total min max avg 95% stddev median 106 | # ============ ======= ======= ======= ======= ======= ======= ======= 107 | # Exec time 120s 2us 123ms 775us 113us 5ms 63us 108 | # Lock time 3s 0 309us 19us 31us 10us 19us 109 | # Rows sent 419.98k 0 208 2.78 0.99 14.68 0.99 110 | # Rows examine 180.24M 0 62.90k 1.19k 0.99 6.93k 0.99 111 | # Query size 5.89M 14 617 39.96 38.53 16.99 38.53 112 | 113 | # Profile 114 | # Rank Query ID Response time Calls R/Call V/M Item 115 | # ==== ================== ============= ====== ====== ===== ============ 116 | # 1 0x4517399CB130EF84 53.0195 44.2% 1249 0.0424 0.01 SELECT memos 117 | # 2 0x4B4B34854741F958 25.4719 21.2% 1263 0.0202 0.00 SELECT memos 118 | # 3 0x08E3E2CA950A9030 22.0707 18.4% 1249 0.0177 0.00 SELECT memos 119 | # 4 0x8ED61FE2C1C9A2A1 8.6435 7.2% 126383 0.0001 0.00 SELECT users 120 | # 5 0x402E13A51340C830 4.8808 4.1% 220 0.0222 0.00 SELECT memos 121 | # MISC 0xMISC 5.8524 4.9% 24219 0.0002 0.0 <10 ITEMS> 122 | 123 | # Query 1: 20.48 QPS, 0.87x concurrency, ID 0x4517399CB130EF84 at byte 26212620 124 | # Scores: V/M = 0.01 125 | # Time range: 2014-09-14 01:00:00 to 01:01:01 126 | # Attribute pct total min max avg 95% stddev median 127 | # ============ === ======= ======= ======= ======= ======= ======= ======= 128 | # Count 0 1249 129 | # Exec time 44 53s 19ms 123ms 42ms 82ms 23ms 28ms 130 | # Lock time 1 44ms 18us 309us 35us 60us 14us 30us 131 | # Rows sent 29 121.97k 100 100 100 100 0 100 132 | # Rows examine 32 58.81M 41.88k 62.90k 48.21k 62.55k 7.64k 42.34k 133 | # Query size 1 108.35k 82 95 88.83 92.72 6.28 92.72 134 | # String: 135 | # Hosts localhost 136 | # Users isucon 137 | # Query_time distribution 138 | # 1us 139 | # 10us 140 | # 100us 141 | # 1ms 142 | # 10ms ################################################################ 143 | # 100ms # 144 | # 1s 145 | # 10s+ 146 | # Tables 147 | # SHOW TABLE STATUS LIKE 'memos'\G 148 | # SHOW CREATE TABLE `memos`\G 149 | # EXPLAIN /*!50100 PARTITIONS*/ 150 | SELECT * FROM memos WHERE is_private=0 ORDER BY created_at DESC, id DESC LIMIT 100 OFFSET 18900\G 151 | 152 | ... 以下デフォルトではトップ5まで 153 | ``` 154 | 155 | tcpdump から集計をとることもできるけど、ISUCON では使わないかな。 156 | 157 | ``` 158 | sudo tcpdump -i bond0 -s 65535 -x -nn -q -tttt 'port 3306' > tcpdump.out 2> /dev/null 159 | ``` 160 | 161 | (ローカルホスト通信の場合) 162 | 163 | ``` 164 | sudo tcpdump -i lo -s 65535 -x -nn -q -tttt 'port 3306' > tcpdump.out 2> /dev/null 165 | ``` 166 | 167 | ``` 168 | pt-query-digest --type=tcpdump tcpdump.out > pt.out 169 | ``` 170 | 171 | みれるものは同じ 172 | -------------------------------------------------------------------------------- /06.mysql_tmc.md: -------------------------------------------------------------------------------- 1 | ## MySQL Too Many Connections (TMC) エラー対策 2 | 3 | mysqld につなごうとすると、以下のエラーが出る場合 4 | ``` 5 | ERROR 1040 (00000): Too many connections 6 | ``` 7 | 8 | ## ポイント 9 | 10 | * /etc/my.cnf max_connections を増やす => [06.mysql.md](06.mysql.md) では比較的大きく 1024 にしてあります! 11 | * 接続する web app 側の worker 数を減らす 12 | * /etc/my.cnf thread_cache_size を大きくする => こちらも大きめに600にしてあります! 13 | * timeout はうーん、ブチ切れても困るしな 14 | 15 | > Yahooでは、thread_cache=150 と wait_time=10 (待機時間10秒)でスレッドの数を減らしているらしい 16 | 17 | ## http://koexuka.blogspot.jp/2010/03/blog-post.html 18 | 19 | #ちょっとコピペしとく 20 | 21 | このエラーは、「コネクションの数が多すぎてもう接続できないよー」というものです。MySQLの設定は 22 | ``` 23 | mysql> show global variables like '%max_connection%'; 24 | +-----------------+-------+ 25 | | Variable_name | Value | 26 | +-----------------+-------+ 27 | | max_connections | 100 | 28 | +-----------------+-------+ 29 | ``` 30 | という状態です。 31 | デフォルト値が「100」なのだそうで、まず単純にコネクション最大数を増やします。 32 | ``` 33 | mysql> set global max_connections = 256; 34 | ``` 35 | コネクション数が増えるので、CPU負荷とメモリ使用量に注意が必要です。 36 | 37 | 他に注目すべきは、 38 | ``` 39 | mysql> show status like '%threads_%'; 40 | +------------------------+--------+ 41 | | Variable_name | Value | 42 | +------------------------+--------+ 43 | | Threads_cached | 6 | 44 | | Threads_connected | 4 | 45 | | Threads_created | 140909 | 46 | | Threads_running | 3 | 47 | +------------------------+--------+ 48 | ``` 49 | うわー!エラいことなっとる! 50 | ここの項目の見方は、 51 | ``` 52 | Threads_cached ... スレッドキャッシュでキャッシュされているスレッドの数。 53 | Threads_connected ... 現在生成されているスレッドの数。 54 | Threads_created ... 起動してから生成されたスレッドの数。この値が大きい場合はthread_cache_sizeの値を大きくした方が良いかも。 55 | Threads_running ... 現在動作中のスレッド数。 56 | ``` 57 | と言うわけで、今回の件ではthread_cache_sizeの値を増やしておきます。補足:thread_cacheはスレッドキャッシュの数です。 58 | MySQLはクライアントからの接続ごとに新しいスレッドを生成し、そのあと破棄します。 59 | このスレッドをすぐに破棄せず、キャッシュしておくことにより再接続時の負荷を軽減することができます。 60 | 61 | ``` 62 | mysql> set global thread_cache_size = 64; 63 | ``` 64 | 65 | と、共にwait_timeoutの状況をチェック。 66 | 67 | ``` 68 | mysql> show global variables like 'wait_timeout%'; 69 | +--------------------------+-------+ 70 | | Variable_name | Value | 71 | +--------------------------+-------+ 72 | | wait_timeout | 28800 | 73 | +--------------------------+-------+ 74 | ``` 75 | 76 | 28800秒、つまり8時間に設定されているので、もう少し縮めて起きます。 77 | 78 | ``` 79 | mysql> set global wait_timeout = 3600; 80 | ``` 81 | 82 | 1時間にしました。 83 | wait_timeoutは接続があってスレッドが生成され待機状態(アイドル状態)になって○秒経過したら接続を切るというもの。 84 | mysql_pconnect()やPDOでの持続的接続とかでも、wait_timeout秒数経過すると切断されます。(と、思う、、) 85 | ちなみに、wait_timeouをmy.cnfに設定する場合は、 86 | ``` 87 | # vim /etc/my.cnf 88 | set-variable = wait_timeout = 3600 89 | ``` 90 | のようにするらしい。 91 | 92 | 93 | 94 | ## http://techs-empty.tumblr.com/post/17701255090/mysql-max-used-connections 95 | 96 | 確認コマンド 97 | 98 | ``` 99 | $ mysqladmin -u root extended-status | grep -E 'Max|Threads' 100 | | Max_used_connections | 101 | 101 | | Threads_cached | 0 | 102 | | Threads_connected | 73 | 103 | | Threads_created | 13750 | 104 | | Threads_running | 1 | 105 | 106 | $ mysqladmin -u root extended-status | grep -E 'Created' 107 | | Created_tmp_disk_tables | 271510 | 108 | | Created_tmp_files | 469 | 109 | | Created_tmp_tables | 600879 | 110 | ``` 111 | 112 | ## メモ 113 | 114 | mysql の cpu とか io で詰まると web app 側の接続がどんどんたまって thread 数が増えていく. 115 | unicorn worker 数以上になるのは、コネクションを閉じていない可能性があるため => 閉じていてもデフォルトでは8時間開放されないっぽい 116 | -------------------------------------------------------------------------------- /06.mysql_tmpfs.md: -------------------------------------------------------------------------------- 1 | mysql のデータファイルを tmpfs においてオンメモリDBにしてみよう 2 | => varnish 時でも 3000 => 3700 tickets に向上(銀の弾丸というほどではないな) 3 | 4 | # tmpfs の設定 5 | 6 | デフォルトではシステムのメモリ容量の半分が tmpfs の最大サイズ 7 | 8 | ``` 9 | $ df -h 10 | Filesystem Size Used Avail Use% マウント位置 11 | tmpfs 3G 0 3G 0% /dev/shm 12 | ``` 13 | 14 | 変えたい場合 cf. http://dan-project.blog.so-net.ne.jp/2012-06-14 15 | 16 | ## tmpfs におく 17 | 18 | /etc/my.cnf 19 | 20 | ```diff 21 | - datadir=/var/lib/mysql 22 | - tmpdir=/var/tmp 23 | + datadir=/dev/shm/mysql 24 | + tmpdir=/dev/shm/tmp 25 | ``` 26 | 27 | 28 | ## 終了時にファイルシステムにコピーする 29 | 30 | /etc/init.d/halt の先頭あたりに 31 | ``` 32 | /usr/bin/rsync -au --delete /dev/shm/mysql/ /var/lib/mysql 33 | ``` 34 | 35 | ## 起動時にファイルシステムからtmpfsにコピーする 36 | 37 | /etc/rc.local の先頭あたりに 38 | 39 | ``` 40 | /usr/bin/rsync -au --delete /var/lib/mysql/ /dev/shm/mysql 41 | mkdir /dev/shm/tmp 42 | chmod a+rwx,o+t /dev/shm/tmp 43 | ``` 44 | -------------------------------------------------------------------------------- /09.unicorn.md: -------------------------------------------------------------------------------- 1 | ## unicorn.conf 2 | 3 | 実質 isucon2 のデフォから変更なし、という結果になった。 4 | 5 | * cf. http://unicorn.bogomips.org/TUNING.html 6 | 7 | listen のオプション. cf. http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen 8 | 9 | * :backlog => number of clients 10 | 11 | * default 1024. sockfd についての保留中の接続のキューの最大長を指定する。 キューがいっぱいの状態で接続要求が到着すると、クライアントは ECONNREFUSED というエラーを受け取る。cf. http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/listen.2.html 12 | 13 | * :rcvbuf => bytes, :sndbuf => bytes 14 | 15 | * Linux 2.4+ have intelligent auto-tuning mechanisms and there is no need to specify them 16 | 17 | * :tcp_no_delay => true or false 18 | 19 | * write時にos/kernelレベルでbufferingしてたまってからsendするのをやめて即座にsendします. 20 | * 小さいパケットはまとめて、それからwriteするようにしましょう. 21 | * cf. kazeburo yapc 2013 http://www.slideshare.net/kazeburo/yapc2013psgi-plack 22 | 23 | * :tcp_nopush => true or false 24 | 25 | * default false. true にすべき(あれ、今はデフォルトtrueじゃなかったけ?) 26 | * cf. kazeburo G-WAN はなぜ速いのか http://blog.nomadscafe.jp/2013/09/benchmark-g-wan-and-nginx.html 27 | 28 | * :tries => seconds 29 | 30 | * Times to retry binding a socket if it is already in use. Default 5 seconds. 31 | 32 | * :delays => seconds 33 | 34 | * Seconds to wait between successive tries. Default: 0.5 seconds 35 | 36 | * :tcp_defer_accept => integer 37 | 38 | * Default 1. コネクションが完了したタイミングではなくデータが到着した段階でプロセスを起こします. 39 | * プリフォーク型のhttpdにおいて処理中となるプロセス数を減らすテクニック 40 | * cf.kazeburo yapc 2013http://www.slideshare.net/kazeburo/yapc2013psgi-plack 41 | 42 | unicorn.conf 43 | 44 | ```ruby 45 | @dir = "/home/isucon/webapp/ruby/" 46 | working_directory @dir 47 | 48 | worker_processes 8 # CPUの数 * 2 ぐらい? 49 | # unicornはノンブロッキングじゃないのでI/Oが詰まるような状況においてはCPUのコア数より増やしたほうが効率が良い. 50 | # メモリが許す限り増やす。どこかで頭打ちにはなると思うが 51 | 52 | # listen "/dev/shm/app.sock", backlog: 8192 # unix domain socket 版. パスは nginx と合わせること 53 | listen "0.0.0.0:5000", tcp_nopush: true, tcp_defer_accept: 1 # 安全というかデフォ 54 | # listen "0.0.0.0:5000", tcp_nopush: true, tcp_defer_accept: 1, tcp_no_delay: true # アプリの実装を気を付ける必要あり 55 | 56 | pid File.join(@dir, "tmp/pids/unicorn.pid") 57 | timeout 60 # default: 60 58 | # stderr_path File.join(@dir, "log/unicorn.stderr.log") 59 | # stdout_path File.join(@dir, "log/unicorn.stdout.log") 60 | 61 | preload_app true # allow copy-on-write-friendly GC to save memory 62 | # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow (for REE, probably not necessary) 63 | GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true 64 | 65 | # 例えば、GC を止める(特に速くならなかった) 66 | # after_fork do |server, worker| 67 | # GC.disable 68 | # end 69 | 70 | # Below is for ActiveRecord 71 | =begin 72 | before_fork do |server, worker| 73 | defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! 74 | old_pid = "#{server.config[:pid]}.oldbin" 75 | if old_pid != server.pid 76 | begin 77 | sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU 78 | Process.kill(sig, File.read(old_pid).to_i) 79 | rescue Errno::ENOENT, Errno::ESRCH 80 | end 81 | end 82 | sleep 1 83 | end 84 | 85 | after_fork do |server, worker| 86 | defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection 87 | end 88 | =end 89 | ``` 90 | 91 | ## 例えばGCを止める 92 | 93 | * cf. http://secondlife.hatenablog.jp/entry/20111006/1317893282 94 | * cf. https://speakerdeck.com/mirakui/high-performance-rails 95 | 96 | 特に速くならなかったので不要。 97 | 98 | ### 設定 99 | 100 | unicorn.conf の最後 or after_fork ブロックに追加 101 | 102 | ```ruby 103 | after_fork do |server, worker| 104 | GC.disable 105 | end 106 | ``` 107 | 108 | Gemfile 109 | 110 | ``` 111 | gem 'unicorn-worker-killer' # https://github.com/kzk/unicorn-worker-killer 112 | ``` 113 | 114 | configu.ru 115 | 116 | ```ruby 117 | require 'unicorn/oob_gc' 118 | use Unicorn::OobGC, 10 # run GC each 10 requests 119 | 120 | require 'unicorn/worker_killer' 121 | # Max Requests per Worker 122 | use Unicorn::WorkerKiller::MaxRequests, 3072, 4096 123 | # Max Memory size (RSS) per worker 124 | use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2)) 125 | ``` 126 | 127 | ----------- 128 | 129 | 尚、OobGC の値を大きく(100とか)すると、 130 | 131 | ``` 132 | 09:01:25 web.1 | Mysql2::Error - Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug: 133 | 09:01:25 web.1 | /home/game/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/mysql2-0.3.11/lib/mysql2/client.rb:44:in `connect' 134 | 09:01:25 web.1 | /home/game/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/mysql2-0.3.11/lib/mysql2/client.rb:44:in `initialize' 135 | ``` 136 | とエラーが出た。Unicorn::WorkerKiller::Oom で調節 .... 難しい。ちょこちょこ Mysql2::Error が出る。 137 | やはり Unicorn::OobGC をそれなりに小さく保たないといけない模様。 138 | 139 | worker が死んで mysql に新規に接続しようとするので、コネクションの数が増えるのかな ... 140 | 尚、WorkerKiller が発動するとログに以下のように出る。 141 | 142 | ``` 143 | 09:10:08 web.1 | W, [2013-09-30T09:10:08.501329 #9605] WARN -- : #: worker (pid: 9605) exceeds memory limit (126382080 bytes > 99290060 bytes) 144 | 09:10:08 web.1 | W, [2013-09-30T09:10:08.501476 #9605] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9605) alive: 15 sec (trial 1) 145 | 09:10:08 web.1 | I, [2013-09-30T09:10:08.538649 #9481] INFO -- : reaped # worker=29 146 | ``` 147 | 148 | -------------------------------------------------------------------------------- /10.nginx.md: -------------------------------------------------------------------------------- 1 | ### 参考 2 | 3 | * http://recipes.sinatrarb.com/p/deployment/nginx_proxied_to_unicorn 4 | * http://nginx.org/en/docs/http/ngx_http_core_module.html 5 | * 追記: kazeburo.conf https://github.com/kazeburo/isucon3qualifier-myhack/blob/master/conf/nginx.conf 6 | * 追記:こちらのサイトも参考になる! http://nodejs.osser.jp/thread/53440873b986bc810f3930e1 7 | 8 | ### /etc/nginx/nginx.conf 9 | 10 | ``` 11 | user nginx; # this sets the user nginx will run as, 12 | worker_rlimit_nofile 65535; # limit of number of open file descriptoers, should be >= 4 * worker_connections 13 | worker_processes 4; # no of cpu cores 14 | # worker_cpu_affinity 0010 1000; 15 | pcre_jit on; # Just In Compile regular expression on conf 16 | 17 | error_log /var/log/nginx/error.log; # setup where nginx will log errors to 18 | pid /var/run/nginx.pid; # and where the nginx process id resides 19 | 20 | events { 21 | use epoll; 22 | worker_connections 10000; 23 | accept_mutex on; # set to on if you have more than 1 worker_processes 24 | #accept_mutex_delay 500ms; 25 | } 26 | 27 | http { 28 | include mime.types; # text/css, text/javascript 29 | # default_type text/html; 30 | sendfile on; # use the kernel sendfile 31 | tcp_nopush on; # prepend http headers before sendfile(), should be on 32 | tcp_nodelay on; # on for keepalive? 33 | etag off; # if not using 34 | send_timeout 10; # クライアントへの応答の送信タイムアウト 35 | keepalive_timeout 120; # 0 to disable keepalive from client. ケースバイケースなので考えて切り替える 36 | # keepalive_requests 6000; 37 | 38 | client_header_timeout 5; 39 | client_body_timeout 30; 40 | client_body_temp_path /dev/shm/client_temp 1 1; 41 | client_max_body_size 10m; 42 | client_body_buffer_size 32k; 43 | client_header_buffer_size 2k; 44 | # reset_timedout_connection on; # 非アクティブクライアントの connection をクローズする 45 | large_client_header_buffers 4 8k; 46 | proxy_connect_timeout 5; 47 | proxy_send_timeout 5; 48 | proxy_read_timeout 60; 49 | proxy_buffering off; 50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 51 | proxy_temp_path /dev/shm/proxy_temp 1 1; 52 | proxy_cache_path /dev/shm/cache levels=1:2 keys_zone=cache-space:20m max_size=300m inactive=10m; 53 | 54 | gzip on; # gzip 圧縮. cpu を食うトレードオフ. クライアントによってはおかしくなることがあったと思う 55 | gzip_vary on; 56 | gzip_min_length 500; 57 | gzip_disable "MSIE [1-6]\.(?!.*SV1)"; 58 | gzip_types text/plain text/xml text/css 59 | text/comma-separated-values 60 | text/javascript application/x-javascript 61 | application/atom+xml image/x-icon; 62 | 63 | log_format ltsv 'host:$remote_addr\t' 64 | 'vhost:$http_host\t' 65 | 'port:$server_port\t' 66 | 'time:$time_iso8601\t' 67 | 'method:$request_method\t' 68 | 'uri:$request_uri\t' 69 | 'protocol:$server_protocol\t' 70 | 'status:$status\t' 71 | 'size:$body_bytes_sent\t' 72 | 'referer:$http_referer\t' 73 | 'ua:$http_user_agent\t' 74 | 'forwardedfor:$http_x_forwarded_for\t' 75 | 'forwardedproto:$http_x_forwarded_proto\t' 76 | 'apptime:$upstream_response_time\t' 77 | 'reqtime:$request_time'; 78 | 79 | # access_log /var/log/nginx/access.log ltsv; 80 | access_log off; 81 | 82 | # use the socket we configured in our unicorn.rb 83 | # http://wiki.nginx.org/HttpUpstreamModule 84 | upstream apps { 85 | ## for unix domain socket 86 | server unix:/dev/shm/app.sock; 87 | 88 | ## for local port keepalive 89 | # server 127.0.0.1:8080; 90 | # keepalive 16; # proxy_http_version 1.1; proxy_set_header Connection ''; も必要. 下参照 91 | 92 | ## for load balancing 93 | # server 127.0.0.1:8080 fail_timeout=0; # 1回失敗したら速攻 unavailable とみなす 94 | # server 127.0.0.2:8080 fail_timeout=0; 95 | # server 127.0.0.3:8080 fail_timeout=0 weight=2; 96 | # max_fails=2 fail_timeout=10; # 10sの間に2回retryしてそれでもつながらなかったら unavailable とみなす 97 | # ip_hash; # 同一IPからのアクセスを同じサーバに振り分ける 98 | # sticky; # sticky session (cookie) を利用して同じサーバに振り分ける。sticky session module をいれてビルドしなおす 99 | # hash $request_uri consistent; # 同じ request_uri なら同じサーバへ 100 | } 101 | 102 | # configure the virtual host 103 | server { 104 | # port to listen for requests on 105 | listen 5000; 106 | # maximum accepted body size of client request 107 | client_max_body_size 4G; 108 | 109 | location / { 110 | ## for local port keepalive 111 | # proxy_http_version 1.1; 112 | # proxy_set_header Connection ""; 113 | 114 | # ヘッダを見てリダイレクトするアプリの場合 115 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 116 | proxy_set_header Host $http_host; 117 | proxy_redirect off; 118 | 119 | # pass to the upstream apps server 120 | proxy_pass http://apps; 121 | } 122 | 123 | location = /favicon.ico { 124 | open_file_cache max=100; 125 | root /home/isucon/webapp/public; 126 | } 127 | 128 | location ~ ^/(img|css|js)/ { 129 | #gzip_static on; 130 | #gzip_types text/css application/x-javascript; 131 | open_file_cache max=100 inactive=360s; 132 | # open_file_cache max=100000 inactive=20s # キャッシュファイル最大数とキャッシュ時間 133 | # open_file_cache_valid 30s; # チェック間隔 134 | # open_file_cache_min_uses 2; # 非アクティブファイルの最小ファイル数 135 | # open_file_cache_errors on; # ファイルのエラーもキャッシュする 136 | root /home/isucon/webapp/public; 137 | } 138 | } 139 | } 140 | ``` 141 | 142 | うーん、796 tikets から 432 tickets に落ちた ... => アプリに処理を回しすぎて 499 になってしまうらしい。アプリを改善しないと始まらない。 143 | 144 | ## アクセスログ集計スクリプト 145 | 146 | https://gist.github.com/sonots/9ae0167d31d7f4b42c9a 147 | -------------------------------------------------------------------------------- /11.varnish.md: -------------------------------------------------------------------------------- 1 | キャッシュしてくれるリバースプロキシサーバだよ。varnish 入れただけで 700 => 3000 に一気にスコアアップ。すごい 2 | 3 | # varnish -> nginx -> unicorn 4 | 5 | ToDo: 最終計測の前に全体をアクセスしておくスクリプトの用意(キャッシュさせたい) 6 | 7 | ## インストール 8 | 9 | cf. https://www.varnish-cache.org/installation/redhat 10 | 11 | 最新版 (3.0.4 now) 12 | 13 | ``` 14 | # $ wegt http://repo.varnish-cache.org/redhat/varnish-3.0/el5/noarch/varnish-release-3.0-1.el5.centos.noarch.rpm 15 | $ wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release-3.0-1.el6.noarch.rpm 16 | $ sudo rpm -Uvh varnish-release-3.0-1.el6.noarch.rpm 17 | $ sudo yum install varnish 18 | ``` 19 | 20 | 主要ファイル 21 | 22 | * /etc/varnish/default.vcl 設定ファイル. デフォルトは localhost:80 に飛ばすだけ 23 | * /etc/sysconfig/varnish 起動オプション. デフォルト 6081 番ポート 24 | * /var/log/varnish/varnishncsa.log アクセスログ. /etc/init.d/varnishncsa 25 | * /var/log/varnish/varnish.log バイナリログ. /etc/initd./varnishlog. varnishlog -r ファイル名で閲覧 26 | 27 | 便利コマンド 28 | 29 | * varnishstat 統計情報 30 | * varnishtop -i txurl 統計情報 31 | 32 | ## バイナリログ 33 | 34 | デフォルトはメモリ上に吐く 35 | 36 | ``` 37 | $ virnishlog 38 | ``` 39 | 40 | ファイルにも保存したい場合、varnishlog daemon を起動させる => その分遅くなるのだと思うし、最終的には止める 41 | 42 | ``` 43 | $ sudo /etc/init.d/varnishlog restart 44 | $ varnishlog -r /var/log/varnish/varnish.log 45 | ``` 46 | 47 | ## アクセスログ 48 | 49 | 行末に cache ヒットしたどうかの情報を追加 cf. http://infra.makeall.net/archives/1773 50 | 51 | /etc/init.d/varnishncsa を以下のように変更して、 52 | 53 | ```shell 54 | $ sudo /etc/init.d/varnishncsa restart 55 | $ tail /var/log/varsnishncsa.log 56 | ``` 57 | 58 | 差分 59 | 60 | ```diff 61 | - DAEMON_OPTS="-a -w $logfile -D -P $pidfile" 62 | + DAEMON_OPTS="-a -w $logfile -D -P $pidfile -F '%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" %{Varnish:time_firstbyte}x %{Varnish:handling}x'" 63 | ``` 64 | 65 | 全文 66 | 67 | ```shell 68 | #! /bin/sh 69 | # 70 | # varnishncsa Control the Varnish NCSA logging daemon 71 | # 72 | # chkconfig: - 90 10 73 | # description: Varnish Cache logging daemon 74 | # processname: varnishncsa 75 | # config: 76 | # pidfile: /var/run/varnishncsa.pid 77 | 78 | ### BEGIN INIT INFO 79 | # Provides: varnishncsa 80 | # Required-Start: $network $local_fs $remote_fs 81 | # Required-Stop: $network $local_fs $remote_fs 82 | # Default-Start: 83 | # Default-Stop: 84 | # Short-Description: start and stop varnishncsa 85 | # Description: Varnish Cache NCSA logging daemon 86 | ### END INIT INFO 87 | 88 | # Source function library. 89 | . /etc/init.d/functions 90 | 91 | retval=0 92 | pidfile="/var/run/varnishncsa.pid" 93 | lockfile="/var/lock/subsys/varnishncsa" 94 | logfile="/var/log/varnish/varnishncsa.log" 95 | 96 | exec="/usr/bin/varnishncsa" 97 | prog="varnishncsa" 98 | 99 | DAEMON_OPTS="-a -w $logfile -D -P $pidfile -F '%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" %{Varnish:time_firstbyte}x %{Varnish:handling}x'" 100 | 101 | # Include varnishncsa defaults 102 | [ -e /etc/sysconfig/varnishncsa ] && . /etc/sysconfig/varnishncsa 103 | 104 | start() { 105 | 106 | if [ ! -x $exec ] 107 | then 108 | echo $exec not found 109 | exit 5 110 | fi 111 | 112 | echo -n "Starting varnish ncsa logging daemon: " 113 | 114 | daemon --pidfile $pidfile $exec "$DAEMON_OPTS" 115 | echo 116 | return $retval 117 | } 118 | 119 | stop() { 120 | echo -n "Stopping varnish ncsa logging daemon: " 121 | killproc -p $pidfile $prog 122 | retval=$? 123 | echo 124 | [ $retval -eq 0 ] && rm -f $lockfile 125 | return $retval 126 | } 127 | 128 | restart() { 129 | stop 130 | start 131 | } 132 | 133 | reload() { 134 | restart 135 | } 136 | 137 | force_reload() { 138 | restart 139 | } 140 | 141 | rh_status() { 142 | status -p $pidfile $prog 143 | } 144 | 145 | rh_status_q() { 146 | rh_status >/dev/null 2>&1 147 | } 148 | 149 | # See how we were called. 150 | case "$1" in 151 | start) 152 | rh_status_q && exit 0 153 | $1 154 | ;; 155 | stop) 156 | rh_status_q || exit 0 157 | $1 158 | ;; 159 | restart) 160 | $1 161 | ;; 162 | reload) 163 | rh_status_q || exit 7 164 | $1 165 | ;; 166 | force-reload) 167 | force_reload 168 | ;; 169 | status) 170 | rh_status 171 | ;; 172 | condrestart|try-restart) 173 | rh_status_q || exit 0 174 | restart 175 | ;; 176 | *) 177 | echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 178 | 179 | exit 2 180 | esac 181 | 182 | exit $? 183 | ``` 184 | 185 | ## 起動オプション /etc/sysconfig/varnish 186 | 187 | * cf. http://blog.xcir.net/index.php/2012/05/varnish%E3%82%92%E4%BD%BF%E3%81%86%E9%9A%9B%E3%81%AB%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AE%E7%BD%A0/ 188 | * cf. http://ijin.github.io/blog/2012/07/03/tuningathon4/ ijinさんの参考にしたほうがいいかも 189 | 190 | 5000番ポートで起動. NFILES や MEMLOCK を調整したほうがいいのかもしれないが、そのままで大体大丈夫そう. ポート枯渇問題もあるのでスレッド数は調節したほうがよさそう? 191 | 192 | /etc/sysconfig/varnish 193 | 194 | 差分 195 | 196 | ```diff 197 | - VARNISH_LISTEN_PORT=6081 198 | + VARNISH_LISTEN_PORT=5000 199 | ``` 200 | 201 | 全文 202 | 203 | 204 | ``` 205 | # Configuration file for varnish 206 | # 207 | # /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this 208 | # shell script fragment. 209 | # 210 | 211 | # Maximum number of open files (for ulimit -n) 212 | NFILES=131072 213 | 214 | # Locked shared memory (for ulimit -l) 215 | # Default log size is 82MB + header 216 | MEMLOCK=82000 217 | 218 | # Maximum number of threads (for ulimit -u) 219 | NPROCS="unlimited" 220 | 221 | # Maximum size of corefile (for ulimit -c). Default in Fedora is 0 222 | # DAEMON_COREFILE_LIMIT="unlimited" 223 | 224 | # Set this to 1 to make init script reload try to switch vcl without restart. 225 | # To make this work, you need to set the following variables 226 | # explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS, 227 | # VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE, or in short, 228 | # use Alternative 3, Advanced configuration, below 229 | RELOAD_VCL=1 230 | 231 | # This file contains 4 alternatives, please use only one. 232 | 233 | ## Alternative 1, Minimal configuration, no VCL 234 | # 235 | # Listen on port 6081, administration on localhost:6082, and forward to 236 | # content server on localhost:8080. Use a fixed-size cache file. 237 | # 238 | #DAEMON_OPTS="-a :6081 \ 239 | # -T localhost:6082 \ 240 | # -b localhost:8080 \ 241 | # -u varnish -g varnish \ 242 | # -s file,/var/lib/varnish/varnish_storage.bin,1G" 243 | 244 | ## Alternative 2, Configuration with VCL 245 | # 246 | # Listen on port 6081, administration on localhost:6082, and forward to 247 | # one content server selected by the vcl file, based on the request. Use a 248 | # fixed-size cache file. 249 | # 250 | #DAEMON_OPTS="-a :6081 \ 251 | # -T localhost:6082 \ 252 | # -f /etc/varnish/default.vcl \ 253 | # -u varnish -g varnish \ 254 | # -S /etc/varnish/secret \ 255 | # -s file,/var/lib/varnish/varnish_storage.bin,1G" 256 | 257 | 258 | ## Alternative 3, Advanced configuration 259 | # 260 | # See varnishd(1) for more information. 261 | # 262 | # # Main configuration file. You probably want to change it :) 263 | VARNISH_VCL_CONF=/etc/varnish/default.vcl 264 | # 265 | # # Default address and port to bind to 266 | # # Blank address means all IPv4 and IPv6 interfaces, otherwise specify 267 | # # a host name, an IPv4 dotted quad, or an IPv6 address in brackets. 268 | # VARNISH_LISTEN_ADDRESS= 269 | VARNISH_LISTEN_PORT=5000 270 | # 271 | # # Telnet admin interface listen address and port 272 | VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 273 | VARNISH_ADMIN_LISTEN_PORT=6082 274 | # 275 | # # Shared secret file for admin interface 276 | VARNISH_SECRET_FILE=/etc/varnish/secret 277 | # 278 | # # The minimum number of worker threads to start 279 | VARNISH_MIN_THREADS=50 280 | # 281 | # # The Maximum number of worker threads to start 282 | VARNISH_MAX_THREADS=1000 283 | # 284 | # # Idle timeout for worker threads 285 | VARNISH_THREAD_TIMEOUT=120 286 | # 287 | # # Cache file location 288 | VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin 289 | # 290 | # # Cache file size: in bytes, optionally using k / M / G / T suffix, 291 | # # or in percentage of available disk space using the % suffix. 292 | VARNISH_STORAGE_SIZE=1G 293 | # 294 | # # Backend storage specification 295 | VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" 296 | # 297 | # # Default TTL used when the backend does not specify one 298 | VARNISH_TTL=120 299 | # 300 | # # DAEMON_OPTS is used by the init script. If you add or remove options, make 301 | # # sure you update this section, too. 302 | DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \ 303 | -f ${VARNISH_VCL_CONF} \ 304 | -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \ 305 | -t ${VARNISH_TTL} \ 306 | -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \ 307 | -u varnish -g varnish \ 308 | -S ${VARNISH_SECRET_FILE} \ 309 | -s ${VARNISH_STORAGE}" 310 | # 311 | 312 | 313 | ## Alternative 4, Do It Yourself. See varnishd(1) for more information. 314 | # 315 | # DAEMON_OPTS="" 316 | ``` 317 | 318 | ## 設定ファイル /etc/varnish/default.vcl 319 | 320 | * cf. https://www.varnish-cache.org/docs/2.1/tutorial/vcl.html 321 | * cf. http://ijin.github.io/blog/2012/07/03/tuningathon4/ POSTを受けたらban(キャッシュクリア)する 322 | 323 | バックエンド 5001 番ポートに流して、GET のみ 60s キャッシュする。尚、varnish はまだ unix domain socket に対応していない。 324 | 325 | /etc/varnish/default.vcl 326 | 327 | ``` 328 | backend default { 329 | .host = "127.0.0.1"; 330 | .port = "5001"; 331 | } 332 | 333 | # import std; # ログ出力 334 | 335 | sub vcl_recv { 336 | # std.log("hoge " + req.url); # ログ出力 337 | if (req.request == "POST") { # && req.url ~ "^/buy") { 338 | ban("req.url ~ /"); # clear cache if POSTed 339 | return(pass); 340 | } 341 | if (req.request != "GET") { 342 | return(pass); # simply pass except GET 343 | } 344 | else { 345 | return(lookup); # Use cache for GET 346 | } 347 | } 348 | 349 | sub vcl_fetch { 350 | if (req.request == "GET" && beresp.status == 200) { 351 | set beresp.ttl = 60s; # 1 min cache 352 | return(deliver); 353 | } 354 | else { 355 | set beresp.ttl = 0s; 356 | return(deliver); 357 | } 358 | } 359 | 360 | sub vcl_error { 361 | if((obj.status >= 100 && obj.status < 200) || obj.status == 204 || obj.status == 304){ 362 | return (deliver); 363 | } 364 | } 365 | 366 | # Below is a commented-out copy of the default VCL logic. If you 367 | # redefine any of these subroutines, the built-in logic will be 368 | # appended to your code. 369 | # sub vcl_recv { 370 | # if (req.restarts == 0) { 371 | # if (req.http.x-forwarded-for) { 372 | # set req.http.X-Forwarded-For = 373 | # req.http.X-Forwarded-For + ", " + client.ip; 374 | # } else { 375 | # set req.http.X-Forwarded-For = client.ip; 376 | # } 377 | # } 378 | # if (req.request != "GET" && 379 | # req.request != "HEAD" && 380 | # req.request != "PUT" && 381 | # req.request != "POST" && 382 | # req.request != "TRACE" && 383 | # req.request != "OPTIONS" && 384 | # req.request != "DELETE") { 385 | # /* Non-RFC2616 or CONNECT which is weird. */ 386 | # return (pipe); 387 | # } 388 | # if (req.request != "GET" && req.request != "HEAD") { 389 | # /* We only deal with GET and HEAD by default */ 390 | # return (pass); 391 | # } 392 | # if (req.http.Authorization || req.http.Cookie) { 393 | # /* Not cacheable by default */ 394 | # return (pass); 395 | # } 396 | # return (lookup); 397 | # } 398 | # 399 | # sub vcl_pipe { 400 | # # Note that only the first request to the backend will have 401 | # # X-Forwarded-For set. If you use X-Forwarded-For and want to 402 | # # have it set for all requests, make sure to have: 403 | # # set bereq.http.connection = "close"; 404 | # # here. It is not set by default as it might break some broken web 405 | # # applications, like IIS with NTLM authentication. 406 | # return (pipe); 407 | # } 408 | # 409 | # sub vcl_pass { 410 | # return (pass); 411 | # } 412 | # 413 | # sub vcl_hash { 414 | # hash_data(req.url); 415 | # if (req.http.host) { 416 | # hash_data(req.http.host); 417 | # } else { 418 | # hash_data(server.ip); 419 | # } 420 | # return (hash); 421 | # } 422 | # 423 | # sub vcl_hit { 424 | # return (deliver); 425 | # } 426 | # 427 | # sub vcl_miss { 428 | # return (fetch); 429 | # } 430 | # 431 | # sub vcl_fetch { 432 | # if (beresp.ttl <= 0s || 433 | # beresp.http.Set-Cookie || 434 | # beresp.http.Vary == "*") { 435 | # /* 436 | # * Mark as "Hit-For-Pass" for the next 2 minutes 437 | # */ 438 | # set beresp.ttl = 120 s; 439 | # return (hit_for_pass); 440 | # } 441 | # return (deliver); 442 | # } 443 | # 444 | # sub vcl_deliver { 445 | # return (deliver); 446 | # } 447 | # 448 | # sub vcl_error { 449 | # set obj.http.Content-Type = "text/html; charset=utf-8"; 450 | # set obj.http.Retry-After = "5"; 451 | # synthetic {" 452 | # 453 | # 455 | # 456 | # 457 | # "} + obj.status + " " + obj.response + {" 458 | # 459 | # 460 | #

Error "} + obj.status + " " + obj.response + {"

461 | #

"} + obj.response + {"

462 | #

Guru Meditation:

463 | #

XID: "} + req.xid + {"

464 | #
465 | #

Varnish cache server

466 | # 467 | # 468 | # "}; 469 | # return (deliver); 470 | # } 471 | # 472 | # sub vcl_init { 473 | # return (ok); 474 | # } 475 | # 476 | # sub vcl_fini { 477 | # return (ok); 478 | # } 479 | ``` 480 | 481 | ## デバグ 482 | 483 | ``` 484 | import std; 485 | sub vcl_recv { 486 | std.log("hoge " + req.url); 487 | } 488 | ``` 489 | 490 | のようにすると、virnishlog で見れる状態に 491 | 492 | ## オブジェクトチートシート 493 | 494 | * req.request => "GET" などのメソッド名 495 | * req.url => "/foo/bar/hoge.jpg" などのパス 496 | * req.http.host => "www.hoge.com" などのホスト名 497 | * req.http.Cookie => Cookieヘッダ文字列 498 | * req.http.Authenticate => Authenticate ヘッダ 499 | * req.http.Authorizaton => Authorization ヘッダ 500 | -------------------------------------------------------------------------------- /12.redis.md: -------------------------------------------------------------------------------- 1 | # redis のインストール 2 | 3 | epel から入れる => 古くてダメだった。2.4.10 4 | 5 | ビルドする 6 | 7 | ``` 8 | wget http://download.redis.io/releases/redis-2.8.17.tar.gz 9 | tar xzf redis-2.8.17.tar.gz 10 | cd redis-2.8.17 11 | make 12 | sudo make install 13 | # /usr/local/bin/redis-server 14 | ``` 15 | 16 | ``` 17 | sudo mkdir -p /var/log/redis && sudo chown isucon.isucon /var/log/redis 18 | sudo mkdir -p /var/lib/redis && sudo chown isucon.isucon /var/lib/redis 19 | ``` 20 | 21 | /etc/redis.conf 22 | 23 | ``` 24 | daemonize no 25 | pidfile /var/run/redis.pid 26 | port 6379 27 | 28 | # replication. スレーブに master の ip port を書くだけ. 尚 async repl しかない. 29 | # slaveof 192.168.1.1 6379 30 | 31 | unixsocket /dev/shm/redis.sock 32 | unixsocketperm 700 33 | 34 | tcp-backlog 511 35 | timeout 0 36 | tcp-keepalive 0 37 | loglevel notice 38 | logfile /var/log/redis/redis.log 39 | 40 | stop-writes-on-bgsave-error yes 41 | 42 | # RDB ファイルをダンプするときに、文字列を圧縮するかどうかを指定 43 | rdbcompression no 44 | rdbchecksum yes 45 | # RDB ファイルの出力先ファイル名を指定 46 | dbfilename dump.rdb 47 | # RDB ファイルの出力先ディレクトリを指定 48 | dir /var/lib/redis 49 | 50 | # 永続化 (save コマンドを打ってアプリで明示的に行うこともできる) 51 | # save 900 1 # 15分間の間に1回更新があったらファイル書き出し 52 | # save 300 10 # 5分間の間に10回更新があったらファイル書き出し 53 | # save 60 10000 # 1分間の間に10000回更新があったらファイル書き出し 54 | 55 | # AOF で永続化 56 | appendonly no 57 | appendfsync no 58 | 59 | # Numbe of max clients. default: 0, that is, unlimited 60 | # maxclient 0 61 | maxmemory 4g 62 | maxmemory-policy noeviction 63 | # The maximum size of virtual memory. default 0, that is, use swap 64 | # vm-max-memory 0 65 | 66 | # microseconds 67 | #slowlog-log-slower-than 10000 68 | #slowlog-max-len 128 69 | # latency-monitor-threshold 0 70 | ``` 71 | 72 | 73 | /etc/supervisord.conf 74 | 75 | 起動確認してからが吉 76 | 77 | ``` 78 | $ redis-server /etc/redis.conf 79 | ``` 80 | 81 | ``` 82 | [program:redis] 83 | directory=/var/lib/redis 84 | command=/usr/local/bin/redis-server /etc/redis.conf 85 | user=isucon 86 | stdout_logfile=/tmp/isucon.redis.log 87 | stderr_logfile=/tmp/isucon.redis.log 88 | autostart=true 89 | ``` 90 | 91 | ``` 92 | $ sudo supervisorctl reload 93 | ``` 94 | -------------------------------------------------------------------------------- /12.redis_resque.md: -------------------------------------------------------------------------------- 1 | # sinatra + resque 2 | 3 | サンプル https://github.com/resque/resque/tree/master/examples/sinatra 4 | 5 | 6 | -------------------------------------------------------------------------------- /12.redis_ruby.md: -------------------------------------------------------------------------------- 1 | http://redis.io/commands 2 | 3 | 参考 4 | 5 | * http://isucon.net/archives/40793620.html 6 | * https://github.com/sorah/isucon4-qualifier-sorah 7 | 8 | ### Gemfile 9 | 10 | ``` 11 | gem 'redis' 12 | gem 'hiredis' 13 | ``` 14 | 15 | ### require 16 | 17 | ``` 18 | require 'hiredis' 19 | require 'redis' 20 | require 'redis/connection/hiredis' 21 | ``` 22 | 23 | ### connection 24 | 25 | ```ruby 26 | def redis 27 | @redis ||= (Thread.current[:isu4_redis] ||= Redis.new(driver: :hiredis, path: '/dev/shm/redis.sock')) 28 | end 29 | # Redis.new(driver: :hiredis, :host => "10.0.1.1", :port => 6380, :db => 15, :timeout => 60) 30 | ``` 31 | 32 | ### 使い方サンプル 33 | 34 | 35 | ```ruby 36 | # 単純な set/get 37 | redis.set key, val 38 | redis.get key 39 | 40 | # multiple set/get 41 | redis.mset key1, val1, key2, val2 42 | 43 | # hash の set/get 44 | redis.hset key, field1, value1 45 | redis.hget key, field1 46 | 47 | # hash を丸っと取り出す 48 | redis.hgetall(key) 49 | 50 | # ハッシュの multiple set/get。 key => {field1 => value, field2 => value} 51 | redis.hmset knextlast, 'at', Time.now.to_i, 'ip', request_ip 52 | redis.hmget knextlast, 'at', 'ip' 53 | 54 | # rename 55 | redis.rename(knextlast, klast) rescue nil # Redis::CommandError 56 | 57 | # INCR 58 | redis.incr kip 59 | redis.incr kuser 60 | 61 | # Evaluate LUA script cached on server. まとめて INCR 62 | MINCR = redis.script(:load, "redis.call('incr', KEYS[1]); redis.call('incr', KEYS[2])") 63 | redis.evalsha MINCR, [kip, kuser], [] 64 | 65 | # 指定したキーパターンにマッチするキーを全て配列として取り出す 66 | redis.keys('isu4:ip:*') 67 | # String 値を取り出す. String じゃない場合はエラー 68 | redis.get(key) 69 | 70 | # ディスクに保存する 71 | redis.save 72 | # 非同期でディスクに保存する 73 | redis.bgsave 74 | 75 | # リクエストを1つずつ処理せずに一気におくって一気にレスポンスを受け取る 76 | redis.pipelined do 77 | redis.set "foo", "bar" 78 | redis.incr "baz" 79 | end 80 | # => ["OK", 1] 81 | 82 | # atomic 83 | redis.multi do 84 | redis.set "foo", "bar" 85 | redis.incr "baz" 86 | end 87 | # => ["OK", 1] 88 | 89 | # enqueue/dequeue. 文字列だけなので hash 入れたりしたい場合は JSON にしないとダメ 90 | redis.rpush key, val # # 後ろに追加. redis.rpush key, val1, val2, val3 とかもできる 91 | redis.lpop key # 先頭から取り出し 92 | # redis.lrange 0, -1 # 先頭から最後まで参照(消さない) 93 | ``` 94 | 95 | 96 | -------------------------------------------------------------------------------- /13.daemontools.md: -------------------------------------------------------------------------------- 1 | # daemontools のインストール 2 | 3 | ``` 4 | $ sudo yum install daemontools 5 | ``` 6 | 7 | # daemontools の初期設定 8 | 9 | centos6系。centos5系は違うので注意 10 | 11 | ``` 12 | $ sudo vi /etc/init/svscan.conf 13 | start on runlevel [2345] 14 | respawn 15 | env PATH=/usr/local/bin/:/sbin:/bin:/usr/sbin:/usr/bin 16 | exec /usr/local/bin/svscanboot 17 | $ sudo initctl reload-configuration && sudo initctl start svscan 18 | ``` 19 | 20 | # ruby app の run script 21 | 22 | /service/isucon 23 | 24 | ``` 25 | #!/bin/sh 26 | exec 2>&1 27 | exec \ 28 | envdir ./env \ 29 | sh -c 'exec \ 30 | setuidgid game \ 31 | /home/isucon/start 32 | ' 33 | ``` 34 | 35 | /home/isucon/start # 直接 /home/isucon/.rbenv/versions/isucon/bin/ruby app.rb でも良いような気もしないでもない 36 | 37 | ``` 38 | #!/bin/sh 39 | export HOME=/home/isucon 40 | [ -f /etc/bashrc ] && source /etc/bashrc 41 | [ -f $HOME/.bash_profile ] && source $HOME/.bash_profile 42 | [ -f $HOME/.bashrc ] && source $HOME/.bashrc 43 | 44 | [[ -d ~/.rbenv/bin ]] && export PATH="$HOME/.rbenv/bin:$PATH" 45 | if which rbenv > /dev/null 2>&1; then eval "$(rbenv init -)"; fi 46 | 47 | [ -z "$APP_ROOT" ] && APP_ROOT="$HOME/webapp/ruby" 48 | cd $APP_ROOT 49 | rbenv local isucon 50 | rbenv rehash 51 | [ -z "$RAILS_ENV" ] && RAILS_ENV=production 52 | 53 | exec \ 54 | sudo RAILS_ENV=$RAILS_ENV bundle exec ruby app.rb \ 55 | ; 56 | ``` 57 | 58 | 59 | -------------------------------------------------------------------------------- /14.golang.md: -------------------------------------------------------------------------------- 1 | # golang のアップデート 2 | 3 | ``` 4 | wget https://storage.googleapis.com/golang/go1.3.1.linux-amd64.tar.gz 5 | tar zxvf go1.3.1.linux-amd64.tar.gz -C ~/local/ 6 | ``` 7 | 8 | # メトリクスの集計 9 | 10 | ``` 11 | go get github.com/sonots/go-template_metrics 12 | go get github.com/sonots/go-sql_metrics 13 | go get github.com/sonots/go-http_metrics 14 | go get github.com/sonots/lltsv 15 | ``` 16 | 17 | 仕込み方はそれぞれの READMEをみてね 18 | 19 | * https://github.com/sonots/go-template_metrics 20 | * https://github.com/sonots/go-sql_metrics 21 | * https://github.com/sonots/go-http_metrics 22 | 23 | ```go 24 | import ( 25 | "flag" 26 | ) 27 | 28 | func main { 29 | verbose := flag.Bool("verbose", false, "verbose print of metrics") 30 | each := flag.Bool("each", false, "print metrics on each request") 31 | disable := flag.Bool("disable", false, "disable metrics") 32 | if *verbose { 33 | http_metrics.Verbose = true 34 | sql_metrics.Verbose = true 35 | template_metrics.Verbose = true 36 | http_metrics.Print(-1) 37 | sql_metrics.Print(-1) 38 | template_metrics.Print(-1) 39 | } else if *each { 40 | http_metrics.Verbose = true 41 | http_metrics.Print(-1) 42 | http_metrics.OnResponse = func() { 43 | sql_metrics.Flush() 44 | template_metrics.Flush() 45 | } 46 | } else if *disable { 47 | http_metrics.Enable = false 48 | sql_metrics.Enable = false 49 | template_metrics.Enable = false 50 | } else { 51 | http_metrics.Print(70) 52 | sql_metrics.Print(70) 53 | template_metrics.Print(70) 54 | } 55 | } 56 | ``` 57 | 58 | 集計の見方 59 | 60 | https://gist.github.com/sonots/0a6211ea5bb5fc1f795c 61 | 62 | # キャッシュテンプレ 63 | 64 | * expire あり 65 | 66 | https://github.com/SpringMT/isucon3_20140923/commit/747b8b7c5c254c27afa216587a0b236304ba5d3f?w=1 67 | 68 | * struct 情報をハッシュにして全キャッシュ。テーブルの内容を全キャッシュするとか 69 | 70 | https://gist.github.com/sonots/cd40381e196d042338c3 71 | 72 | # ライブラリ 73 | 74 | redis client 75 | 76 | https://github.com/simonz05/godis/ 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | isucon3_prepare 2 | =============== 3 | 4 | # ISUCON3 レギュレーションと予選当時の流れ 5 | 6 | - http://isucon.net/archives/cat_1024990.html 7 | - https://gist.github.com/941/191180fd8e33aae121cb 8 | 9 | # 当日の行動 10 | 11 | - 予選 10月5日(土) 10:00 - 18:00 => 10月6日(日) 10:00 - 18:00 に変更しました 12 | - 遅くとも 09:30 にはヒカリエに集まる. 席を近くにする(モニタは借りればよい.休みなので使ってない) 13 | - ご飯は食べておく and 飲み物とか買っておく 14 | 15 | # 次回に向けて(反省) 16 | 17 | 直せそうな所を見つけてすぐ直すよりも、一旦まとめて戦略を錬ってガッとやる方が上位にいけるっぽい 18 | 簡単な改修ポイントがみつかってもまだ手を出さず、最初は洗い出しに専念すること!! 19 | 20 | - AMI3台立ち上げる。1台は本番機でインフラ担当が扱う。公開鍵を渡して登録してもらう。 21 | - 本番機を初期設定する 22 | - レギュレーションを読む 23 | - メトリクスを取る仕込みを入れて time コマンドかまして1回ベンチ通す(ベンチ1回に何秒かかるか見る) 24 | 25 | - mysql で log-queries-not-using-indexes を出すようにしておく 26 | - HTTPリクエストのメトリクスをとえるプロファイラを仕込む (nginx のログで見る手もある) 27 | - SQLクエリのメトリクスをとれるプロファイラを仕込む (long_query_time = 0 で slow.log 出しておき、mysqlslowdump という手もある) 28 | - テンプレートエンジンのメトリクスをとれるプロファイラを仕込む 29 | - lsof してアプリがどこにアクセスしているのかとっておく 30 | - vmstat の結果をとっておく 31 | - iostat の結果をとっておく 32 | - テーブルのスキーマ、インデックス情報をとっておく (show create table memos, explain ....) 33 | - https://gist.github.com/sonots/0a6211ea5bb5fc1f795c 34 | 35 | - 追加: データの特性をみる 36 | 37 | - どのデータが増えてどのデータは固定なのか. 増えるとして何 rows, bytes ぐらい増えるのか 38 | - 初期データは何 rows, bytes ぐらいなのか 39 | 40 | - 追加: benchmarker の動きをみる 41 | 42 | - どの endpoint にどの順番でアクセスしてくるのか。どのアクセス群が一連の流れなのか(あとはそのループのはず) 43 | - リクエストヘッダをみる。**リクエストヘッダには運営からのヒントが書かれている** 44 | 45 | - ToDo: ログに吐くようにしておくべきか 46 | 47 | - 本番機をイメージ化して共有する(待ち時間発生。その間に下) 48 | - メトリクスの結果とコードを見てチューニング箇所を考える 49 | - アプリの画面を触りながら(*Chrome の Developer Tools 開きながら*)議論し、戦略を練る。 50 | - (一人)用意しておいたミドルウェアの設定ファイルを適用していく 51 | - (二人)ガッとやる 52 | - 計測する. 外の情報にもアンテナを貼っておく(途中でレギュレーションに補足が入ったりする) 53 | 54 | - 追加: 明確なチューニングポイントが見えなくなってきた場合、コードレベルでのプロファイリングが必要になってくる(それぐらいしかやることが残っていない) 55 | 56 | - コードにプロファイラを仕込む(golang: pprof, ruby: stackprof) 57 | - フレームワークが遅かったらフレームワークを外すなど 58 | 59 | - 追加: benchmarker の隙を付く、レギュレーションの隙をつく 60 | 61 | - strings benchmarker、tcpdump 62 | - 遅い所はあえて fail したら得点あがるかもとか 63 | - このレスポンスヘッダを benchmarker は解釈するかもとか. リダイレクトを省けるのでは、とか. 64 | - benchmarker 内部で時間かかってそうだから、そこをあえて fail させて飛ばせないかとか 65 | 66 | # 役に立つかもしれない情報 67 | 68 | クエリチューニング: 69 | 70 | - http://www.slideshare.net/kazeburo/isucon-summerclass2014action2final 71 | - まずは検索カラムに index を貼る 72 | - SELECT のカラムを * から必要なカラムに絞る。少数ならば index 貼ってしまって、covering index にしてしまう 73 | - LIMIT ? OFFSET ? は IN に置き換える 74 | - N+1 クエリはまとめて取得できるように書き換える 75 | - 典型例: 二つのテーブルを合体するためのN+1クエリ => Joinに直す 76 | - なぜか index が利かなくて実行計画がおかしい場合は、select ... from table force index(index_name); とする。 77 | 78 | 79 | アイデア: 80 | 81 | - アプリに newrelic か rack-mini-profiler 仕込んだほうがよさそう 82 | - rubinius への置き換え => 使ったことないのでエラー出た時どうしたらいいかわからん 83 | - tmpfs のファイルに書き込んで簡易にプロセス間メモリ共有 84 | - mysql のデータファイルを tmpfs においたらはやそう 85 | 86 | - レギュレーションの「サーバの設定およびデータ構造は任意のタイミングでのサーバ再起動に耐えること」にひっかかるのでだめっぽい T T 87 | - シャットダウン時にディスクにおいて、起動時にディスクから tmpfs に cp するようにすればいいんじゃないのかな 88 | - 実際にやってみたが速くはなったが、varnish のほうが銀の弾丸度では上だな。アプリの処理を丸っと省けるからな。 89 | 90 | 91 | cf. http://blog.nomadscafe.jp/2012/11/isucon2-5.html 92 | 93 | - kazeburo さんは worker に HTML を保存させてそれを serve するようにしたらしい https://github.com/kazeburo/isucon2_hack/blob/master/perl/worker.pl 94 | - nginx にアクセス時に gzip 圧縮するのではなく、その HTML をあらかじめ gzip 圧縮して配信したらしい 95 | - トラッフィク多すぎたので delay を入れたとな ... 96 | 97 | cf. http://www.seeds-std.co.jp/seedsblog/163.html 98 | 99 | memcached の中に入ってるキーを一覧するperlワンライナー 100 | 101 | ``` 102 | perl -MCache::Memcached -e '$s="localhost:11211";$m=new Cache::Memcached({servers=>[$s]});$res=$m->stats("items");$i=$res->{hosts}{$s}{items};@a=split("€n",$i);while(){if($_=~/items:([0-9]+)/){$s{$1}=$_}};foreach $key (keys %s){$cm="cachedump $key 100";$res=$m->stats($cm);print "--- €n".$cm."€n";print $res->{hosts}{$s}{$cm}}' 103 | ``` 104 | 105 | 再起動時にキャッシュに突っ込むシェルスクリプト 106 | 107 | ``` 108 | #!/bin/sh 109 | 110 | for i in `seq 1 1 3000` 111 | do 112 | wget http://app01/article/$i -O - 113 | done 114 | ``` 115 | 116 | デプロイ. てっとりはやく rsync でいいかもね => git pull で 117 | 118 | ``` 119 | #!/bin/sh 120 | rsync -Irp --exclude ".git" --progress . $HOST:isucon/webapp/ruby/ 121 | ssh -t $HOST sudo /etc/init.d/supervisord restart 122 | ``` 123 | -------------------------------------------------------------------------------- /isucon2/01.setup.md: -------------------------------------------------------------------------------- 1 | ISUCON2のセットアットをとりあえず起動させるぞ cf. http://blog.livedoor.jp/techblog/archives/67728751.html 2 | 3 | 4 | ## ■アプリセットアップ 5 | 6 | 1、ruby インストール 7 | 8 | See [../05.ruby.md](../05.ruby.md) 9 | 10 | 2、mysql セットアップ 11 | 12 | See [../06.mysql.md](../06.mysql.md) 13 | 14 | 3、isucon アプリセットアップ 15 | 16 | ちょっとおかしかったとこ直してある 17 | 18 | ``` 19 | git clone git@github.com:sonots/isucon2.git ~/gitrepos/isucon2 20 | cd isucon2 21 | git checkout refs/tags/v1.1 22 | mysql -uroot < webapp/config/database/isucon2.sql 23 | mysql -uroot < webapp/config/database/initial_data.sql 24 | cd webapp/ruby 25 | bundle 26 | ``` 27 | 28 | 起動 (ベンチスクリプトが 5000 番ポートで起動させることを期待している) 29 | 30 | ``` 31 | PORT=5000 bundle exec foreman start 32 | ``` 33 | 34 | http://localhost:5000/admin にアクセスしてデータの初期化 35 | 36 | ## ■性能評価ツールセットアップ 37 | 38 | 1、node インストール 39 | 40 | See [../05.node.md](../05.node.md) 41 | 42 | 2、npm install 43 | 44 | ``` 45 | cd ~/gitrepos/isucon2/tools 46 | npm install 47 | ``` 48 | 49 | # WARN 出るけど気にしない。失敗したらリトライ. npmjs.org とやら貧弱すぎるぞ 50 | 51 | 52 | 3、http_load のビルド 53 | 54 | ``` 55 | cd ~/gitrepos/isucon2/tools/http_load_isucon2 56 | make 57 | ``` 58 | 59 | 4、run! 60 | 61 | ``` 62 | cd ~/gitrepos/isucon2/tools 63 | ./test.sh 127.0.0.1 5000 64 | ``` 65 | 66 | 5、ベンチマーク管理用ツール 67 | 68 | ``` 69 | mysql -uroot < sql/isumaster.sql 70 | node manager.js 71 | node agent.js 72 | ``` 73 | 74 | http://localhost:5001. manager / foo でログイン 75 | 76 | #ポート番号変えたい場合は、manager.js と agent.json の記述を変更 77 | --------------------------------------------------------------------------------