2016ǯ05��31��

pixiv private isucon 2016 ��ά (2/5)

¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÅÐÏ¿

����������:

���ץ���ɤ�

�����������ɤ��ɤߤʤ�����������ι������İ����Ƥ����ޤ���

  • �ե졼������ goji ��ȤäƤ��롣 goji �Υ����Ȥò¸«¤ï¿½ï¿½Æ±ï¿½ï¿½Ì¾ï¿½ï¿½ï¿½Ç¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½Õ¥ì¡¼ï¿½ï¿½ï¡¼ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½Ä¾ï¿½ï¿½ï¿½ï¿½ï¿½ß¤ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½É¡ï¿½ï¿½ï¿½ï¿½ï¿½Ï¸Å¤ï¿½ï¿½ï¿½ï¿½ï¿½È¤Ã¤Æ¤ï¿½ß¤ï¿½ï¿½ï¿½ï¿½Ç¤ï¿½ï¿½ï¿½
  • ������� Gorilla �Υ饤�֥��� memcached store ���Ȥ߹�碌�Ƥ���
  • �ƥ�ץ졼�Ȥ������󥰤Τ��Ӥ˥���ѥ��뤷�Ƥ�Τ��Ť�����
  • �����ȤΥ桼�����ò¤¤¤ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½æ¡¼ï¿½ï¿½ï¿½ï¿½ï¿½Æ¡ï¿½ï¿½Ö¥ë¤«ï¿½ï¿½ï¿½ï¿½ï¿½Ä¥ï¿½Ã¤Æ¤ï¿½Î¤ï¿½ JOIN ������å��夬����������
  • �����ǡ����� MySQL ���ͤù���Ǥ�Τϡ��ե�����˽Ф�ɬ�פ����ꤽ����(�ե�����ʤ����KB�����ɤ�Ǥ��֤��뤱��ɡ�DB���ȴ�ñ����ˡ���Ȱ��٤����Τ������ɤ�Ǥ��ޤ��Τǡ�����˥����������褿�Ȥ��˥��꤬­��ʤ��ʤ�ޤ�����

�ɤ�Ǥ���Ȳ����������ݥ���Ȥ���������ФƤ����ΤΡ������ʤꤢ��������¤�������ȥХ��ä��Ȥ��˺���ޤ����Ϥ���ϴ���Ū������ȥܥȥ�ͥå��˹ʤä����塼�˥󥰤Ǥ���

�ǽ��DB�Υ��ͥ������ס�������Ǥ��Ƥʤ��Τ����ꤷ�Ƥ����ޤ��礦��2�����ʤ�Ǥ���ʤˤ������󥳥ͥ�����󤢤äƤ�����ʤ��ϥ����Ȥꤢ�������礤¿���8�ˤ��Ƥ����ޤ���

+    db.SetMaxOpenConns(8)
+    db.SetMaxIdleConns(8)
     defer db.Close()

���������������ɤ�

�������Ѱդ��Ƥ��������������������ץġ���ʼ���Ƥ⤤���Ǥ��������Τ�������������ISUCON�ε�����õ���Ƥߤ���ɤ��ġ��뤬�Ҳ𤵤�Ƥ���Ϥ��Ǥ��ˤ򡢥������������䥢�ץ��URL�롼�ƥ��󥰤�����򻲹ͤ˥������ޥ������ޤ��礦��

�ͤξ��ϼ������ץȤǡ����Τ褦�ʽ��פ򤷤ޤ�����

Request by count
8028 GET /image/*
644 GET /
551 GET /js/jquery-2.2.0.js
551 GET /css/style.css
551 GET /js/jquery.timeago.ja.js
551 GET /favicon.ico
551 GET /js/jquery.timeago.js
551 GET /js/main.js
450 POST /login
299 GET /posts/*
235 GET /@user
130 POST /
98 GET /login
90 POST /comment
79 POST /register
78 GET /admin/banned
56 GET /posts?max_created_at=
50 GET /logout
1 GET /initialize

Request by total time (total avg [sec])
198.379 0.308041925466 GET /
168.858 0.021033632287 GET /image/*
47.605 0.202574468085 GET /@user
34.491 0.0766466666667 POST /login
30.444 0.543642857143 GET /posts?max_created_at=
15.095 0.0504849498328 GET /posts/*
7.068 0.0894683544304 POST /register
6.7 0.0515384615385 POST /
6.51 0.0118148820327 GET /favicon.ico
6.483 0.0117658802178 GET /js/jquery-2.2.0.js
5.784 0.010497277677 GET /js/jquery.timeago.js
5.213 0.0094609800363 GET /js/jquery.timeago.ja.js
5.095 0.00924682395644 GET /css/style.css
4.997 0.00906896551724 GET /js/main.js
2.478 0.0275333333333 POST /comment
2.257 0.0230306122449 GET /login
1.314 0.0168461538462 GET /admin/banned
1.131 0.02262 GET /logout
0.061 0.061 GET /initialize

Request by out bytes (total avg)
2485959550 309661 GET /image/*
142499069 258619 GET /js/jquery-2.2.0.js
12206011 18953 GET /
4287247 18243 GET /@user
4105501 7451 GET /js/jquery.timeago.js
1912146 34145 GET /posts?max_created_at=
1108574 3707 GET /posts/*
980229 1779 GET /css/style.css
487635 885 GET /js/main.js
357048 648 GET /js/jquery.timeago.ja.js
195356 434 POST /login
177772 1814 GET /login
149872 272 GET /favicon.ico
34286 434 POST /register
23530 181 POST /
22200 444 GET /logout
16833 187 POST /comment
13104 168 GET /admin/banned
161 161 GET /initialize

����ǹͤ���Τϡ���˼���2���Ǥ���

  • �ȥåץڡ����� /image/ �˳ݤ�����֤����ֽŤ����ä˥ȥåץڡ�����ǽ�˥��塼�˥󥰤���Τ��ɤ�������
  • �Ӱ�� /image/ ������Ū���Ӱ�ͥå��ˤʤ�褦���ä��顢�����Ϻư��̤��ˤ����Τǡ��إå�����Ŭ�ڤ����ꤹ��Х��饤����Ȥ� "304 Not Modified" ���֤���褦�ˤʤä��礭�ʥ֥졼�����롼�����뤫�⤷��ʤ���

pprof

�Ȥꤢ���� app �� CPU ���Ť��Τǡ�CPU�ץ��ե����뤫��Ϥ�ޤ��礦�� pprof �δ���Ū�ʻȤ����ϡ������� Blog ��񤤤��ΤǤ���򻲾Ȥ��Ƥ���������

diff --git a/app.go b/app.go
index 861ff03..eb52f47 100644
--- a/app.go
+++ b/app.go
@@ -9,6 +9,7 @@ import (
        "io/ioutil"
        "log"
        "net/http"
+       _ "net/http/pprof"
        "net/url"
        "os"
        "os/exec"
@@ -826,6 +827,8 @@ func main() {
+
+        db.SetMaxOpenConns(8)
+        db.SetMaxIdleConns(8)
        defer db.Close()

+       go http.ListenAndServe(":3000", nil)
+
        goji.Get("/initialize", getInitialize)
        goji.Get("/login", getLogin)
        goji.Post("/login", postLogin)
$ go tool pprof app http://localhost:3000/debug/pprof/profile
Fetching profile from http://localhost:3000/debug/pprof/profile
Please wait... (30s)
Saved profile in /home/isucon/pprof/pprof.localhost:3000.samples.cpu.001.pb.gz
Entering interactive mode (type "help" for commands)
(pprof) top 10
14440ms of 29650ms total (48.70%)
Dropped 546 nodes (cum <= 148.25ms)
Showing top 10 nodes out of 214 (cum >= 7370ms)
      flat  flat%   sum%        cum   cum%
    2270ms  7.66%  7.66%     8650ms 29.17%  runtime.mallocgc
    2220ms  7.49% 15.14%     2220ms  7.49%  runtime.memclr
    2150ms  7.25% 22.39%     2840ms  9.58%  runtime.scanobject
    1930ms  6.51% 28.90%     2060ms  6.95%  syscall.Syscall
    1280ms  4.32% 33.22%     1280ms  4.32%  runtime.heapBitsSetType
    1240ms  4.18% 37.40%     1270ms  4.28%  runtime.(*mspan).sweep.func1
    1000ms  3.37% 40.78%     1000ms  3.37%  runtime.memmove
     930ms  3.14% 43.91%     5070ms 17.10%  database/sql.convertAssign
     750ms  2.53% 46.44%     2240ms  7.55%  time.parse
     670ms  2.26% 48.70%     7370ms 24.86%  github.com/go-sql-driver/mysql.(*textRows).readRow

����������Ť��Ǥ��͡����� MySQL ���������ä���̤�ѡ������Ƥ���ʬ���Ť���

����������Ť���硢����Υ��������Ȥ��Ƥ���ʬ�򸫤Ĥ����٤��Ƥ�����ˡ�⤢��ޤ�������äȹ⤤�쥤�䡼�ǤΥ��ץ�Υ��塼�˥󥰤�������Ǥ������ξ�� -cum ���ץ�����Ȥäƥȥåץ�����˥ץ��ե�����򸫤ޤ���

(pprof) top -cum 40
10.25s of 29.65s total (34.57%)
Dropped 546 nodes (cum <= 0.15s)
Showing top 40 nodes out of 214 (cum >= 2.22s)
      flat  flat%   sum%        cum   cum%
         0     0%     0%     28.99s 97.77%  runtime.goexit
         0     0%     0%     25.69s 86.64%  net/http.(*conn).serve
         0     0%     0%     25.40s 85.67%  net/http.(*ServeMux).ServeHTTP
...
         0     0%     0%     25.08s 84.59%  github.com/zenazn/goji/web/middleware.Recoverer.func1
         0     0%     0%     22.42s 75.62%  github.com/zenazn/goji/web.netHTTPHandlerFuncWrap.ServeHTTPC
         0     0%     0%     20.61s 69.51%  main.getIndex
         0     0%     0%     20.26s 68.33%  github.com/jmoiron/sqlx.(*DB).Select
         0     0%     0%     20.26s 68.33%  github.com/jmoiron/sqlx.Select
...

��̤ˤ���ꥯ�����ȥϥ�ɥ顼�� getIndex �����Ǥ��������˽��椷�ޤ��礦��

�����������ɤι�ñ�̤Υץ��ե�����򸫤�ˤϥǥХå�����ɬ�פʤΤǡ� pprof ��ư���˥��ץ�ΥХ��ʥ����ꤷ�Ƥ���ɬ�פ�����ޤ��� �Х��ʥ����ꤷ˺��� URL �����ǥץ��ե�������äƤ��ޤä����⡢��ư�����Ȥ��� "Saved profile in ..." ��ɽ������Ƥ���ѥ��˥ץ��ե������̤���¸����Ƥ���Τǡ����٥ץ��ե�����󥰤��ʤ��Ƥ� go tool pprof app <�ץ��ե������̤Υѥ�> ������פǤ���

isucon:~/private_isu/webapp/golang$ go tool pprof app  /home/isucon/pprof/pprof.localhost:3000.samples.cpu.001.pb.gz
Entering interactive mode (type "help" for commands)
(pprof) list main.getIndex
Total: 29.65s
ROUTINE ======================== main.getIndex in /home/isucon/private_isu/webapp/golang/app.go
         0     20.61s (flat, cum) 69.51% of Total
         .          .    388:
         .          .    389:   http.Redirect(w, r, "/", http.StatusFound)
         .          .    390:}
         .          .    391:
         .          .    392:func getIndex(w http.ResponseWriter, r *http.Request) {
         .      120ms    393:   me := getSessionUser(r)
         .          .    394:
         .          .    395:   results := []Post{}
         .          .    396:
         .     18.39s    397:   err := db.Select(&results, "SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` ORDER BY `created_at` DESC")
         .          .    398:   if err != nil {
         .          .    399:           fmt.Println(err)
         .          .    400:           return
         .          .    401:   }
         .          .    402:
         .      1.34s    403:   posts, merr := makePosts(results, getCSRFToken(r), false)
         .          .    404:   if merr != nil {
         .          .    405:           fmt.Println(merr)
         .          .    406:           return
         .          .    407:   }
         .          .    408:
         .          .    409:   fmap := template.FuncMap{
         .          .    410:           "imageURL": imageURL,
         .          .    411:   }
         .          .    412:
         .          .    413:   template.Must(template.New("layout.html").Funcs(fmap).ParseFiles(
         .          .    414:           getTemplPath("layout.html"),
         .          .    415:           getTemplPath("index.html"),
         .          .    416:           getTemplPath("posts.html"),
         .          .    417:           getTemplPath("post.html"),
         .       70ms    418:   )).Execute(w, struct {
         .          .    419:           Posts     []Post
         .          .    420:           Me        User
         .          .    421:           CSRFToken string
         .          .    422:           Flash     string
         .      690ms    423:   }{posts, me, getCSRFToken(r), getFlash(w, r, "notice")})
         .          .    424:}
         .          .    425:
         .          .    426:func getAccountName(c web.C, w http.ResponseWriter, r *http.Request) {
         .          .    427:   user := User{}
         .          .    428:   uerr := db.Get(&user, "SELECT * FROM `users` WHERE `account_name` = ? AND `del_flg` = 0", c.URLParams["accountName"])

����ε����κǸ�� myprofiler �Ǹ��Ĥ����� posts ����� LIMIT �ʤ������꤬�Ť�����Ǥ��͡�MySQL ¦�����Ǥʤ����������̤η�̤�����������ƥѡ������Ƥ� app ¦�Ǥ���ֽŤ��ʤäƤ���褦�Ǥ���

���Υ�����η�̤����Ѥ��Ƥ��� makePosts() ����򸫤Ƥߤ�ȡ��롼�פν�ü�ˤ���� if ʸ������ޤ���

        if len(posts) >= postsPerPage {
            break
        }

���μ����� continue ʸ��ʤ��Τǡ����Τޤ� postsPerPage �� LIMIT ���ޤ��礦�� (��: ���줬��ǧ��­�Ǹ�ǥϥޤ�ޤ�)

diff --git a/app.go b/app.go
index eb52f47..3dd9804 100644
--- a/app.go
+++ b/app.go
@@ -394,7 +394,7 @@ func getIndex(w http.ResponseWriter, r *http.Request) {

        results := []Post{}

-       err := db.Select(&results, "SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` ORDER BY `created_at` DESC")
+       err := db.Select(&results, "SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` ORDER BY `created_at` DESC LIMIT 20")
        if err != nil {
                fmt.Println(err)
                return

���ơ���¬�Ǥ��������Ϸ�¬�ȥץ��ե�������̤ˤ������������Ǥ��������֤�����̵���Τǥץ��ե����뤷�ʤ����¬�Ǥ���

������:

{"pass":true,"score":12848,"success":22272,"fail":1201,"messages":["1�ڡ�����ɽ�����������ο���­��ޤ��� (GET /)","������쥯����URL������������ޤ���: expected '^/$', got '/login' (GET /login)"]}

����顣���顼���ǤƤ��ޤ��������ץ�Υ����򸫤�ȡ�

fork/exec /bin/bash: cannot allocate memory

���꤬­��ʤ��� fork �˼��Ԥ��Ƥޤ�����

top:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
11931 isucon    20   0  406248 183372   9052 S  74.8 17.9   0:19.55 app
  720 mysql     20   0 1226416 215076  10404 S  65.5 21.0   3:24.11 mysqld

CPU ����Ψ�� app �� mysql �������������Ǥ��͡�­���Ƥ�200�ˤ������Ϥ��ʤ��Τǡ�CPU�ʳ�����ʬ���ͥå��ˤ�Ϥ���Ƥ������Ǥ���

myprofiler:

  17 SELECT * FROM `posts` WHERE `id` = N
  12 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `created_at` <= S ORDER BY `created_at` DESC
   4 SELECT * FROM `users` WHERE `id` = N
   3 SELECT * FROM `users` WHERE `id` = ?

posts ���� id �����1�쥳���ɼ������Ť��ʤ�ޤ����� N+1 ���ʡ�

�����ץ������ƤӽФ������

fork �˼��Ԥ���Τϡ�¿ʬ�ʥ���åפ��ʤ������ǡ��˥����С����ߥåȤ����դˤʤäƤ��ơ�fork �����ִ֤� app ��Ʊ�������Υ������äƤ�⤦1�ץ���������ʤ�������Ȼפ��ޤ���

CPU����Ψ��2�����ʤΤ�200%�������Ϥ��ʤ��Τ� fork �Τ��� (Go �Υ�󥿥�����Թ�� fork ���뤿��ˤ��� goroutine ���ö�ߤ�ʤ��Ȥ����ʤ����Ԥ����֤�ȯ������) ���⤷��ʤ��Τǡ���������̺︺�Τ���˲����ե������DB�����ڤ�Ф����⳰���ץ������ƤӽФ����������ͥ�褷�ޤ��礦��

�����ץ��������ɤ�Ǥ�Ȥ����򸫤�ȡ� openssl dgst -sha512 ���ޥ�ɤ� sha512 �����������Ȥ�׻����Ƥ���褦�Ǥ��� Go �� sha512 ��׻�������ˡ��Ĵ�٤ơ�Ʊ�����Ϥ򤹤�ץ�������񤤤Ƥߤޤ���

$ cat t.go < app.go
package main

import (
        "crypto/sha512"
        "fmt"
        "io/ioutil"
        "os"
)

func main() {
        data, _ := ioutil.ReadAll(os.Stdin)
        fmt.Printf("%x\n", sha512.Sum512(data))
}
isucon@ip-10-0-1-202:~/private_isu/webapp/golang$ go run t.go < t.go
b252b55576a4f4e321d5761b6dc980b8ee41fd9767765c785861a73b736f6a59aa7f40f7ba0d92c508dea32a20bc800e0198056c3dc8b33d5ac445f27b163d17
isucon@ip-10-0-1-202:~/private_isu/webapp/golang$ openssl dgst -sha512 < t.go
(stdin)= b252b55576a4f4e321d5761b6dc980b8ee41fd9767765c785861a73b736f6a59aa7f40f7ba0d92c508dea32a20bc800e0198056c3dc8b33d5ac445f27b163d17

����򻲹ͤ� app.go ��񤭴����ޤ���

@@ -125,14 +125,7 @@ func escapeshellarg(arg string) string {
 }

 func digest(src string) string {
-       // openssl�ΥС������ˤ�äƤ� (stdin)= �Ȥ����Τ��Ĥ��ΤǼ��
-       out, err := exec.Command("/bin/bash", "-c", `printf "%s" `+escapeshellarg(src)+` | openssl dgst -sha512 | sed 's/^.*= //'`).Output()
-       if err != nil {
-               fmt.Println(err)
-               return ""
-       }
-
-       return strings.TrimSuffix(string(out), "\n")
+       return fmt.Sprintf("%x", sha512.Sum512([]byte(src)))
 }

���ơ��ץ��ե��������¬�Ǥ������ץ�Υ����ˤ� fork ���顼���ä��ޤ�����

������:

{"pass":true,"score":26755,"success":24050,"fail":119,"messages":["1�ڡ�����ɽ�����������ο���­��ޤ��� (GET /)"]}

�����󡢤ޤ����顼�ФƤޤ��͡������ LIMIT ­�����Τ��ޥ������ʡ�

top:

Tasks:  84 total,   1 running,  83 sleeping,   0 stopped,   0 zombie
%Cpu(s): 54.2 us, 20.7 sy,  0.0 ni, 15.0 id,  1.4 wa,  0.0 hi,  8.3 si,  0.4 st
KiB Mem:   1022972 total,   958404 used,    64568 free,    18292 buffers
KiB Swap:        0 total,        0 used,        0 free.   425600 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  720 mysql     20   0 1357488 210336   4476 S  78.6 20.6   5:19.05 mysqld
  763 isucon    20   0  619612 198632   9120 S  70.2 19.4   0:13.54 app
 2904 www-data  20   0   92028   4088   1928 S  11.3  0.4   0:29.40 nginx
 2572 isucon    20   0   34344  11148   2160 S   7.3  1.1   0:28.48 tmux

idle �� 15% �ޤDz����äƤޤ��������̤ꡣ ���ȡ� tmux �� 7% �ޤ���ޤ����� Goji �Υ�������å�������Τǡ��Ǹ�ˤ��ڤ�ʤ��Ȥ����ޤ���͡�

myprofiler:

##  2016-05-25 20:18:44.06 +0900
  13 SELECT * FROM `posts` WHERE `id` = N
   9 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `created_at` <= S ORDER BY `created_at` DESC
   9 SELECT * FROM `comments` WHERE `post_id` = N ORDER BY `created_at` DESC LIMIT N
   6 SELECT * FROM `users` WHERE `id` = ?
   6 SELECT `id` FROM `posts` WHERE `user_id` = N
   4 SELECT * FROM `users` WHERE `id` = N
   4 SELECT COUNT(*) AS `count` FROM `comments` WHERE `post_id` = ?
   2 SELECT COUNT(*) AS `count` FROM `comments` WHERE `post_id` = N
   2 SELECT * FROM `comments` WHERE `post_id` = ? ORDER BY `created_at` DESC LIMIT N
   2 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `user_id` = N ORDER BY `created_at` DESC

������

makePosts() ��ľ���Ƥߤ�ȡ���Ƥ����桼�����������������Ƥ���������ؤ��ɲä򤷤ʤ��褦�ʥ����ɤˤʤäƤ��ޤ����� (Go ���� early return (continue) ���ǥ������Ȥ����Ȥ�¿���ΤǤ�����ʬ��ƨ���Ƥ��ޤ���)

        if p.User.DelFlg == 0 {
            posts = append(posts, p)
        }

SELECT ���ʳ��Ƕ����� JOIN ���Ƥ�����������Υե��륿��󥰤�¹Ԥ��Ƥ��ޤ��ޤ���

@@ -394,7 +387,7 @@ func getIndex(w http.ResponseWriter, r *http.Request) {
 
        results := []Post{}
 
-       err := db.Select(&results, "SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` ORDER BY `created_at` DESC LIMIT 20")
+       err := db.Select(&results, "SELECT posts.`id`, `user_id`, `body`, `mime`, posts.`created_at` FROM `posts` INNER JOIN `users` ON posts.user_id=users.id WHERE users.del_flg = 0 ORDER BY `created_at` DESC LIMIT 20")
        if err != nil {
                fmt.Println(err)
                return

������:

{"pass":true,"score":27399,"success":23615,"fail":0,"messages":[]}

���٤Ϥ������̤�ޤ�����

top:

Tasks:  83 total,   4 running,  79 sleeping,   0 stopped,   0 zombie
%Cpu(s): 53.9 us, 23.6 sy,  0.0 ni,  9.8 id,  4.2 wa,  0.0 hi,  8.2 si,  0.2 st
KiB Mem:   1022972 total,   948340 used,    74632 free,    12604 buffers
KiB Swap:        0 total,        0 used,        0 free.   486056 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  720 mysql     20   0 1359224 209144   4576 S  85.9 20.4   6:06.96 mysqld
  910 isucon    20   0  487224 139892   8884 S  67.9 13.7   0:09.58 app
 2904 www-data  20   0   91852   2828    844 R  10.7  0.3   0:35.40 nginx
 2572 isucon    20   0   34472  10100    928 S   6.7  1.0   0:33.64 tmux

����ä� mysql ���Ť��Ǥ��͡�

myprofiler:

  15 SELECT * FROM `posts` WHERE `id` = N
   7 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `created_at` <= S ORDER BY `created_at` DESC
   6 SELECT * FROM `comments` WHERE `post_id` = N ORDER BY `created_at` DESC LIMIT N
   5 SELECT * FROM `users` WHERE `id` = ?
   5 INSERT INTO `comments` (`post_id`, `user_id`, `comment`) VALUES (?,?,?)
   5 SELECT COUNT(*) AS `count` FROM `comments` WHERE `post_id` = N
   5 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `user_id` = N ORDER BY `created_at` DESC
   4 INSERT INTO `posts` (`user_id`, `mime`, `imgdata`, `body`) VALUES (?,?,?,?)
   4 INSERT INTO `users` (`account_name`, `passhash`) VALUES (?,?)
   3 SELECT * FROM `users` WHERE `id` = N

JOIN ��­�������ɡ�����Ǥ�ޤ� post �� id �ǰ����Τ��Ť��ߤ����Ǥ���

pprof top40 -cum:

...
         0     0%  0.38%     13.88s 76.18%  github.com/zenazn/goji/web/middleware.AutomaticOptions.func1
     0.01s 0.055%  0.44%      8.85s 48.57%  github.com/zenazn/goji/web.netHTTPHandlerFuncWrap.ServeHTTPC
         0     0%  0.44%      6.11s 33.53%  main.getIndex
     0.02s  0.11%  0.55%      4.92s 27.00%  github.com/jmoiron/sqlx.(*DB).Get
     0.01s 0.055%   0.6%      4.90s 26.89%  github.com/jmoiron/sqlx.Get
         0     0%   0.6%      4.78s 26.23%  github.com/zenazn/goji/web.handlerFuncWrap.ServeHTTPC
     0.02s  0.11%  0.71%      4.65s 25.52%  main.makePosts
     3.82s 20.97% 21.68%      4.06s 22.28%  syscall.Syscall
...

pprof list makePosts:

ROUTINE ======================== main.makePosts in /home/isucon/private_isu/webapp/golang/app.go
      20ms      4.65s (flat, cum) 25.52% of Total
         .          .    173:}
         .          .    174:
         .          .    175:func makePosts(results []Post, CSRFToken string, allComments bool) ([]Post, error) {
         .          .    176:   var posts []Post
         .          .    177:
      10ms       10ms    178:   for _, p := range results {
         .      1.05s    179:           err := db.Get(&p.CommentCount, "SELECT COUNT(*) AS `count` FROM `comments` WHERE `post_id` = ?", p.ID)
         .          .    180:           if err != nil {
         .          .    181:                   return nil, err
         .          .    182:           }
         .          .    183:
         .          .    184:           query := "SELECT * FROM `comments` WHERE `post_id` = ? ORDER BY `created_at` DESC"
         .          .    185:           if !allComments {
         .       20ms    186:                   query += " LIMIT 3"
         .          .    187:           }
         .          .    188:           var comments []Comment
         .      1.38s    189:           cerr := db.Select(&comments, query, p.ID)
         .          .    190:           if cerr != nil {
         .          .    191:                   return nil, cerr
         .          .    192:           }
         .          .    193:
         .          .    194:           for i := 0; i < len(comments); i++ {
         .      770ms    195:                   uerr := db.Get(&comments[i].User, "SELECT * FROM `users` WHERE `id` = ?", comments[i].UserID)
         .          .    196:                   if uerr != nil {
         .          .    197:                           return nil, uerr
         .          .    198:                   }
         .          .    199:           }
         .          .    200:
         .          .    201:           // reverse
         .          .    202:           for i, j := 0, len(comments)-1; i < j; i, j = i+1, j-1 {
         .          .    203:                   comments[i], comments[j] = comments[j], comments[i]
         .          .    204:           }
         .          .    205:
      10ms       10ms    206:           p.Comments = comments
         .          .    207:
         .      1.35s    208:           perr := db.Get(&p.User, "SELECT * FROM `users` WHERE `id` = ?", p.UserID)
         .          .    209:           if perr != nil {
         .          .    210:                   return nil, perr
         .          .    211:           }
         .          .    212:
         .          .    213:           p.CSRFToken = CSRFToken
         .          .    214:
         .          .    215:           if p.User.DelFlg == 0 {
         .       60ms    216:                   posts = append(posts, p)
         .          .    217:           }
         .          .    218:           if len(posts) >= postsPerPage {
         .          .    219:                   break
         .          .    220:           }
         .          .    221:   }

���֤Τ����äƤ륯���꤬�����ȼ����������1�쥳���ɼ�����������Υ�����ʤΤǡ���ñ�ʥ���������̤��ꤲ�Ƥ�Τ�����ǽŤ��ΤǤ��礦��

�ץ졼���ۥ�������륯����ϡ��ǥե���ȤǤ� prepared statement ��Ȥ��ΤƤˤ��Ƥ��� (prepare, execute, close ��3���ޥ��) �Τǡ�����Ū�� prepared statement ������Ѥ��륳���ɤ�񤯤��� prepared statement ��Ȥ�ʤ��褦�ˤ���Τ������Ǥ��� �ǹ���ǽ���ܻؤ��ʤ�����Ѥ����������Ǥ�������������ñ�ʤΤ� prepared statement ��Ȥ�ʤ����Ǥ��� dsn �� interpolateParams=true ��­�������� (�ͤ�����������ǽ�Ǥ��� ����)

diff:

@@ -805,7 +805,7 @@ func main() {
        }

        dsn := fmt.Sprintf(
-               "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=Local",
+               "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=Local&interpolateParams=true",
                user,
                password,
                host,

������:

{"pass":true,"score":30076,"success":25687,"fail":0,"messages":[]}

30k �������

top:

Tasks:  83 total,   5 running,  78 sleeping,   0 stopped,   0 zombie
%Cpu(s): 63.1 us, 17.4 sy,  0.0 ni,  8.5 id,  3.4 wa,  0.0 hi,  7.4 si,  0.2 st
KiB Mem:   1022972 total,   960140 used,    62832 free,    13192 buffers
KiB Swap:        0 total,        0 used,        0 free.   472760 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  720 mysql     20   0 1359224 209132   4128 S  79.7 20.4   6:56.71 mysqld
 1010 isucon    20   0  514368 157192   8888 S  66.8 15.4   0:08.93 app
 2904 www-data  20   0   91876   2824    844 R  11.3  0.3   0:41.86 nginx
 1015 isucon    20   0   21176  18648   3444 R   8.3  1.8   0:01.31 myprofiler
 2572 isucon    20   0   34920  10472    916 S   7.3  1.0   0:38.28 tmux

myprofiler:

  18 SELECT * FROM `posts` WHERE `id` = N
  11 INSERT INTO `posts` (`user_id`, `mime`, `imgdata`, `body`) VALUES (N,S,_binaryS,S)
   9 SELECT * FROM `users` WHERE `id` = N
   7 SELECT * FROM `comments` WHERE `post_id` = N ORDER BY `created_at` DESC LIMIT N
   6 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `user_id` = N ORDER BY `created_at` DESC
   5 SELECT `id`, `user_id`, `body`, `mime`, `created_at` FROM `posts` WHERE `created_at` <= S ORDER BY `created_at` DESC
   4 SELECT COUNT(*) AS `count` FROM `comments` WHERE `post_id` = N
   2 INSERT INTO `users` (`account_name`, `passhash`) VALUES (S,S)
   1 SELECT * FROM `users` WHERE `account_name` = S AND `del_flg` = N
   1 INSERT INTO `comments` (`post_id`, `user_id`, `comment`) VALUES (N,N,S)

pprof:

(pprof) top40 -cum
4.23s of 18.58s total (22.77%)
Dropped 583 nodes (cum <= 0.09s)
Showing top 40 nodes out of 317 (cum >= 2.07s)
      flat  flat%   sum%        cum   cum%
         0     0%     0%     17.10s 92.03%  runtime.goexit
...
         0     0%  0.22%      8.84s 47.58%  github.com/zenazn/goji/web.netHTTPHandlerFuncWrap.ServeHTTPC
         0     0%  0.22%      5.66s 30.46%  main.getIndex
         0     0%  0.22%      4.90s 26.37%  github.com/zenazn/goji/web.handlerFuncWrap.ServeHTTPC
         0     0%  0.22%      4.04s 21.74%  github.com/jmoiron/sqlx.(*DB).Get
     0.04s  0.22%  0.43%      4.04s 21.74%  github.com/jmoiron/sqlx.Get
         0     0%  0.43%      3.73s 20.08%  html/template.(*Template).Execute
     0.03s  0.16%  0.59%      3.50s 18.84%  main.makePosts
         0     0%  0.59%      3.32s 17.87%  github.com/jmoiron/sqlx.(*DB).Select
         0     0%  0.59%      3.32s 17.87%  github.com/jmoiron/sqlx.Select
     0.93s  5.01%  5.60%      3.23s 17.38%  runtime.mallocgc
         0     0%  5.60%      3.12s 16.79%  text/template.(*Template).Execute
     0.08s  0.43%  6.03%      3.12s 16.79%  text/template.(*state).walk
     0.01s 0.054%  6.08%      3.07s 16.52%  text/template.(*state).walkTemplate
         0     0%  6.08%      3.02s 16.25%  text/template.(*state).walkRange
         0     0%  6.08%      3.02s 16.25%  text/template.(*state).walkRange.func1
     0.13s   0.7%  6.78%      2.96s 15.93%  runtime.systemstack

���������ƥ�ץ졼�ȤνŤ�����̤˸����Ϥ�ޤ��������ޤ�DB�������Ť���

�ޤȤ�

������: 4745 (�������) -> 14208 (����) -> 30076 (����)

���ơ����������ץ��ե�����ǽŤ��Ȥ������٤����塼�˥󥰤ϡ����������ҹʤ�˶�Ť��Ƥ��ޤ����� ����Ϥ�äȥɥ饹�ƥ��å��ʥ��塼�˥󥰤˰ܤäƤ��������Ȼפ��ޤ���

����� pprof �� myprofiler ��Ȥä����塼�˥󥰤ξҲ�ΰտޤ����ä��ΤǤ���äȤ�ꤹ���Ƥ��ޤ��ޤ����� �ºݤˤϤ�ä��������¤��ɬ�פȤ���褦�ʥ��塼�˥󥰤�ͤ��Ƽ¹Ԥ��ʤ��ȡ���Ⱦ�˻��֤�­��ʤ��ƥ����ϤˤʤäƤ��ޤ��Τǵ���Ĥ��ޤ��礦��


@methane

songofacandy at 11:10��Comments(0)��TrackBack(0)��ISUCON | golang

�ȥ�å��Хå�URL

���ε����˥����Ȥ���

̾��:
URL:
  ����òµ­²ï¿½: ɾ��: ��    ��
 
 
 
Blog�⸡��
�ǿ�����
Archives
���Υ֥����ˤĤ���
DSAS�Ȥϡ�KLab �����ۤ����Ѥ��Ƥ��륳��ƥ�ĥ����ӥ��Ѥ�Linux�١����Υ���ե�Ǥ�������5����Υǡ������󥿤ˤƹ��ۤ������Ѥ��Ƥ��ޤ������桹��DSAS����Ȥ��䤹�����������ˡ������Ƥ����ϤDZ��ѤǤ��뤳�Ȥ��ܻؤ��ơ��������ɤ˶Ф���Ǥ��ޤ���
���Υ֥����Ǥϡ������ DSAS �ǻȤäƤ��뵻�ѤξҲ�䡢�¸����Ƥߤ���̤���𡢥ȥ�֥�˴������ޤ줿���ηи��̤ʤɡ���������������������򿥤�ޤ��ƾҲ𤷤Ƥ��������Ȼפ��ޤ���
�ǿ�������
<%==comments[n].author%>
<% } %>