ISUCON7äºé¸1æ¥ç®ã«ãã¼ã ãããã¦ãã§åå ãã¦æçµã¹ã³ã¢ã¯205148ã§ãã
Webãµã¼ãã¹ãããæãã«ããã©ã¼ãã³ã¹ãã¥ã¼ãã³ã°ããã³ã³ãã¹ã
ISUCON7äºé¸1æ¥ç®ã« @netmarkjp, @ishikawa84g, @matsuu ã§ãã¼ã ãããã¦ãã«ã¨ãã¦åå ãã¾ãããæçµã¹ã³ã¢ã¯ 205148 ã§ããã
èå¯
netmarkjp
- ä¾å¹´éãã®å½¹å²åæ ããã£ããæ©è½ãã¦æ°æã¡ããã§ãã
- è¦ç¹ãå¤ãããä¼æ©ã¨ã£ãããããæãã«ã§ãã
- å»å¹´ã®ä½ãã§ããªãã£ãç¡å¿µã¯å¤å°ä¾é¤ã§ãã
- ç·´ç¿ããã¡ãã¨æ´»ããã
- ãã³ããå®å®ãã¦ã¦ãããããã£ã
- BGMã¯æ±äº¬ã¹ã«ãã©ãã¤ã¹ãªã¼ã±ã¹ãã©ã§ãã
matsuu
- ãã©ãã£ãã¯ãããã«ããã¯ã«ãªãåé¡ããªããªã解決ã§ããã«ããããCache-Controlã«publicãå ¥ãããã¨ãæãã¤ãã
- 304å¿çãå®å®ãã¦çºçããªãçç±ãçæãããç»åã®æ´æ°æ¥æããµã¼ãæ¯ã«ç°ãªãããã§ãããã¨ã«æ°ã¥ããèªåãè¤ãã¦ãããã
- tcpdumpã§ãã³ããã¼ã¯ã®ãã©ãã£ãã¯ãtcpdumpãã¦ã¿ãã®ã大ããã£ã
- Pixiv社å ISUCONã使ã£ã¦ãã£ãããã¥ã¼ãã³ã°ããã¨ãã®çµé¨ãã¨ã¦ãå½¹ã«ç«ã£ãããããã¨ãPixiv
- Discordãã£ãããçµæ§ããã£ããã²ã¼ã ãããªãã¦ããããã£ãã¤ãã³ãã«ã¨ã¦ãåãã¦ããã¨æãã®ã§ãªã¹ã¹ã¡
- NEW GAME!!ãåãã¦è¦ããã¨ã³ã¸ãã¢ããããã§ã°ãã¨ããããªãã»ã©ã
ishikawa84g
- Microsoft Wireless Display Adapter ã¯ä¾¿å©ã
- ãã ãã6æéãããé£ç¶ç¨¼åããã¨ãã³ã°ãããã¨ãããããã§ãããã°ããå¯ãããã¨å¾©æ´»ããã
- éå§æéã伸ã³ã¦ãããæ
ã¦ããªã©ãã¯ã¹ãã¦éå§ã§æ¥ãã(æµç³ã¿ããªãã©ãã«æ
£ããã¦ãããï¼)
- Discordã§ãããããããNEW GAME!ã¯3話ã¾ã§è¦ã¾ããã
- 常ã«ç·å¼µç¶æ ããå¿ è¦ã¯ãªããç· ããã¨ãããç· ããã°åé¡ãªãï¼
- Discordã®ã³ãã³ãã«FF14ã®Wikiãæ¤ç´¢ããã¨ããã£ãã
æçµæ§æ
[nginx -- (varnish) -- gunicorn]x2 -- [mysql]x1
- å½åæ§æãããã¾ãå¤ãã
- ãã³ãå ã¯å é 2å°
- ãããã¯ã¼ã¯ããã«ããã¯ã®éã¯3å°ã«æ¯ã£ããã©ãçµå±DBãµã¼ãã®CPUã足ããæ»ãã
- æ稿ãããç»åã¯éçãã¡ã¤ã«ã¨ãã¦åºåããnginxã§éçãã¡ã¤ã«ãè¿å´
/icons/*
ã¯ãã¡ã¤ã«ã«åºåãåºæ¬çã«nginxããããªããã°varnishãçµç±ãã¦ã¢ããªã§ãã¡ã¤ã«çæ- ãã®ä»ã®ãã¹ã¸ã®ãªã¯ã¨ã¹ãã¯varnishãçµç±ããç´æ¥gunicornã¸
- varnishã¯
/icons/*
ã®Thunerding Herd対çã¨ãã¦å°å ¥ - ãã®ä»MySQLãnginxãã¢ããªã®ãã¥ã¼ãã³ã°ãå®æ½
äºåæºå
ãã¼ã å ä½å¶ã®æ§ç¯
- ãã£ããã¯Slackã§
- gitãªãã¸ããªã¯ Gitlab.com ã§
- 使ã£ããã¨ããªããã¼ã«ã使ã£ã¦ã¿ããã¨ãããã¨ã§ç°å¢æ§ç¯ã¯itamaeã§ãããã¸ã§ãã³ã°
- åææ§ç¯ä»¥éã¯çæ決æ¦ã®ããitamaeã§ç®¡çããªã
- git pushãã¼ã¹ã®CIãããã¸ã§ãã³ã°ã¯å®æ½ããªã
- äºåç·´ç¿ã§è©¦ãããã®ã®çæ決æ¦ã§ã¯ã©ããã£ã¦ãé ã
- ãµã¼ãã§ç´æ¥Vimã使ã£ã¦ç·¨é
- ä»ã®äººãç·¨éãã¦ãå ´åã¯
vim -R
ã§åç §
- ä»ã®äººãç·¨éãã¦ãå ´åã¯
ç°å¢æ§ç¯ç¨ã¬ã·ããç¨æ
itamaeã使ã£ã¦ä»¥ä¸ã®ãããã¸ã§ãã³ã°ãèªåå
- githubããSSHå ¬ééµãåå¾ãè¨å®
- 以ä¸ã®è§£æãã¼ã«ãã¤ã³ã¹ãã¼ã«
- dstat
- kataribe
- MySQLTuner
- netdata
- Percona Toolkit pt-query-digestã®ãã
- gprof2dot Pythonã®WSGIãããã¡ã¤ã©Werkzeugã®ã°ã©ãåãã¼ã«
- 以ä¸ã®ããã«ã¦ã§ã¢ãã¤ã³ã¹ãã¼ã«
- ãã³ããã¼ã¯ããããåã®åæåå¦ç/ãããå¾ã®è§£æå¦çãå®è¡ããã¹ã¯ãªãããç¨æ
- åæåå¦ç
- MySQLã¹ãã¼ã¯ã¨ãªã¼ãã°ãåé¤ãã¦
mysqladmin flush-logs
- nginxã®ã¢ã¯ã»ã¹ãã°ãåé¤ãã¦
systemctl reload nginx
- ã¢ããªãããã¡ã¤ã©ã®åºåãåé¤ãã¦ã¢ããªã±ã¼ã·ã§ã³ããã°ã©ã ã®åèµ·å
- MySQLã¹ãã¼ã¯ã¨ãªã¼ãã°ãåé¤ãã¦
- 解æå¦ç
- MySQLã¹ãã¼ã¯ã¨ãªã¼ã«å¯¾ãã¦
pt-query-digest
ãå®è¡ - MySQLTunerãå®è¡
- nginxã®ã¢ã¯ã»ã¹ãã°ã«å¯¾ãã¦
kataribe
ãå®è¡ - pythonãããã¡ã¤ã©Werkzeugã®åºåã«å¯¾ãã¦
gprof2dot
ã¨graphviz
ãå®è¡ãã¦ã°ã©ãçæ
- MySQLã¹ãã¼ã¯ã¨ãªã¼ã«å¯¾ãã¦
- åæåå¦ç
éå»åã解ãã¦äºè¡æ¼ç¿
äºåå ¬éã®ã¬ã®ã¥ã¬ã¼ã·ã§ã³ãçèª
大äº
å½æ¥ä½æ¥
9:00 - 10:00
éå
é»æºãæ¤
åãã¢ãã¿ã¼ã®è¨å¶ã¾ã§ãã£ã¦ããã¨ã¯ããã¿ãª
大äºãªã®ã¯æ¨ªä¸¦ã³ã«åº§ããã¨
11:30 æ¼é£
è½ã¡çãã¦ç¾å³ããæ¼é£ããã³ãã¼ã¬ã¼ç»åãåå è ãã£ããã«æä¸
12:00 - 13:00 â»éå§å
ISUCONåå è å°ç¨ãã£ããã§NEW GAME!!ã®è©±é¡ãçãä¸ãã£ãããAmazonビデオã§è¦è´éå§
13:00 éå§äºå®æå»
ãã¼ã¿ã«ãµã¤ãã...ãªã¼ãã³ããªã
13:10 éå¶ããDiscordã§ç·æ¥é£çµ¡
methane - ä»æ¥ åå¾1æ10å ããã¡ãã£ã¨ããããï¼
13:13 éå§
SSHæ¥ç¶ã§ããªããã©ãã«ã«è¦èããããã®ã®ããã®éã«ååãã³ããã¼ã¯å®è¡ã¨å½æ¥ã¬ã®ã¥ã¬ã¼ã·ã§ã³ãçèª
13:30 ãã°ã¤ã³å¾
äºãç¨æãã¦ããç°å¢æ§ç¯ã¬ã·ããå®è¡
並è¡ãã¦ãåãã·ã³ã®ã¹ããã¯ã¨ã使ããã¦ããããã«ã¦ã§ã¢ã確èª
ãµã¼ãã¹ã®ç»é¢ãè¦ããã³ã¼ããèªãã§ãã©ããªã¢ããªãªã®ããã£ããææ¡
ãã¼ã¿ã«ä¸ã§ãµã¼ããéé ã§ä¸¦ãã§ã¦ããå é 2å°ããã©ããªã®ãã¡ãã£ã¨æ··ä¹±ãã
MySQLã®ãã¥ã¼ãã³ã°ã§/dev/shmã«æ ¼ç´ãããã¨ããã¨ããAppArmorã«å¼ã£ããã£ããããAppArmorã調æ´
/etc/apparmor.d/usr.sbin.mysqld
ã«ä»¥ä¸ã追è¨
# ISUCON2017 /dev/shm/ r, /dev/shm/** rw, /proc/** r, /sys/devices/system/node/ r, /sys/devices/system/node/** r,
å¤æ´å¾ãProfileããªãã¼ãããMySQLãåèµ·åããã
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld sudo systemctl restart mysql.service
14:01 webappé ä¸ãgit管çã«
pythonã§å®è£ ããæ¹éã«ãããããpublicãã£ã¬ã¯ããªã¨pythonãã£ã¬ã¯ããªãgitã«çªã£è¾¼ã¿commit
14:08 ã¤ã³ããã¯ã¹è¿½å
ã¹ãã¼ã¯ã¨ãªã¼ããé ããªã¯ã¨ã¹ãã調ã¹ã¦ã¤ã³ããã¯ã¹è¿½å
ALTER TABLE image ADD INDEX (name); ALTER TABLE message ADD INDEX (channel_id);
14:30
ä¸ç¬ã ã1ä½ç²å¾
https://twitter.com/matsuu/status/921609614881202178
netdataè¦ãã®ã¯ãã®é ã¾ã§ã§ããã¨ã¯dstatã¨top
tmuxã§3å°å並ã¹ã¦è¡¨ç¤ºãæä½ã¯tmuxã®synchronized-paneã§æ¥½ã å®æ½
14:33 /icons/ã®éçãã¡ã¤ã«å
DBã«æ ¼ç´ããã¦ããç»åãã¼ã¿ãéçãã¡ã¤ã«ã¨ãã¦åºåããå®è£ ãè¡ã
def get_images(): cur = dbh().cursor() cur.execute("SELECT name, data FROM image") for row in cur.fetchall(): output = "/home/isucon/images/{}".format(row['name']) with open(output, 'wb') as file: file.write(row['data'])
åºåããç»åã¯nginxããç´æ¥å¿çããããã«å¤æ´
location /icons/ { expires 60s; alias /home/isucon/images/; try_files $uri @upstream; } location @upstream { proxy_set_header Host $http_host; proxy_pass http://127.0.0.1:5000; }
14:34é DBã¨ã¢ããªéã®ãã©ãã£ãã¯æ¹å
dstatã§è¦ã¦ããã¨DBã¨WEBã®éã®ãã©ãã£ãã¯ãå¤ãã®ã«ãã³ããã¼ã«ã¼ã¨WEBã®éã®ãã©ãã£ãã¯ãå°ãªããã¨ã«æ°ã¥ãã
åãå 容ã®ç»åãåããã¡ã¤ã«åã§éè¤ç»é²ããã¦ããç¡é§ã«DBããã®åå¾ãçºçãã¦ãããã¨ã«æ°ã¥ããã®ã§1è¡ã ãåå¾ããããä¿®æ£
cur.execute("SELECT * FROM image WHERE name = %s", (file_name,)) cur.execute("SELECT * FROM image WHERE name = %s LIMIT 1", (file_name,))
14:57 éçãã¡ã¤ã«ã®gzip_staticå
éçãã¡ã¤ã«ãäºãgzipãã¦ä¿åãã¦ãããnginxã® gzip_static
ã§å¿çããããã«
gzip_static on; gzip_types image/svg+xml text/css text/javascript application/javascript application/font-wof f application/vnd.ms-fontobject;
15:21é ç»åãçæãã
ç»åãINSERTæãSELECTæã«åºåããããå¤æ´ã
ããããµã¼ããã¨ã«ç»åãçæããå®è£
ã®ãããã®å¾ãã°ããã¹ã³ã¢ã伸ã³æ©ã
- ç»åã«expiresãä»ãã¦ã¿ãâå¤å°æ¹åãããã®ã®æ ¹æ¬è§£æ±ºã«ã¯ãªãã
- /fetchã®1ç§å¾ æ©ãå¤ãã¦ã¿ãâå¹æãªã
- 206ã¬ã¹ãã³ã¹ãè¿ãã¦Rangeãããã¼ã§è¿ãã°ããã®ã§ã¯ï¼â206å¿çãç°¡åã«å®è£ ããæ¹æ³ããããã
- limit_rateã§ä»£ç¨ãããã¿ã¤ã ã¢ã¦ã
- 302ãªãã¤ã¬ã¯ãã§å¿çãé ãããã°ããï¼â302ãªãã¤ã¬ã¯ãã¯ã¨ã©ã¼ã«ãªã£ã
17:24é /fetchã®ãã¥ã¼ãã³ã°
SQLãè¦ç´ãã¦N+1ã解æ¶
SELECT channel.id AS channel_id, CASE WHEN message_id IS NULL THEN (SELECT COUNT(*) FROM message WHERE channel_id = channel.id) ELSE (SELECT COUNT(*) FROM message WHERE channel_id = channel.id AND message_id < id) END AS unread FROM channel LEFT JOIN haveread ON channel.id = haveread.channel_id AND user_id = ?
18:17é ç»åãã¡ã¤ã«çææã«æ´æ°æ¥æãåããã
ãã³ããã¼ã¯ããã®HTTPãªã¯ã¨ã¹ããtcpdumpã§è¨é²ãã¦ã©ã®ãããªãªã¯ã¨ã¹ããå±ãã¦ãã確èªãã¦ã¿ã
tcpdump -w dump tcp port 80
ãã®çµæãWiresharkã«é£ããã¦ç¢ºèªãã¦ããã¨ãããIf-Modified-Sinceãããã¼ãè¦ã¤ãã¦æãã¤ããã
ãç»åãã¡ã¤ã«ã®æ´æ°æ¥æãåããã¦ãªããã304å¿çãè¿ãã¦ããªãã®ã ãã¨ãã¨ã¦ã¬ã«ï¼
def write_image(data, name): output = "/home/isucon/images/{}".format(name) with open(output, 'wb') as file: file.write(data) os.utime(output, (1508559193, 1508559193))
1508559193
㯠2017/10/21 13:13:13
ãISUCON7äºé¸1æ¥ç®ã®éå§æ¥æã
18:33
帯åãå¤å°ãã·ã«ãªã£ãã®ã§webãµã¼ãã®CPUè² è·ãä¸ããããã« gzip off
18:51é /messageã®ãã¥ã¼ãã³ã°
SQLãè¦ç´ãã¦N+1ã解æ¶
if last_message_id > 0: cur.execute("SELECT message.id, name, display_name, avatar_icon, message.created_at, content FROM message JOIN user ON message.user_id = user.id WHERE message.id > %s AND channel_id = %s ORDER BY message.id DESC LIMIT 100", (last_message_id, channel_id)) else: cur.execute("SELECT message.id, name, display_name, avatar_icon, message.created_at, content FROM message JOIN user ON message.user_id = user.id WHERE channel_id = %s ORDER BY message.id DESC LIMIT 100", (channel_id, ))
19:00é ç»åãã¡ã¤ã«çææã®åºåå ã/dev/shm/ã«ãã
with tempfile.TemporaryFile(dir="/dev/shm/") as f:
19:10 Pythonãããã¡ã¤ã©Werkzeugã®å°å ¥
æã¤æãå°ãªããªã£ã¦ããã®ã§Werkzeugã§ã®ãããã¡ã¤ã«ãå®æ½ã
from werkzeug.contrib.profiler import ProfilerMiddleware app.wsgi_app = ProfilerMiddleware(app.wsgi_app, profile_dir="/tmp/profile")
gprof2dotã使ã£ã¦dotãã¡ã¤ã«ãçæããgraphvizã使ã£ã¦pngãã¡ã¤ã«ãçæã
gprof2dot -f pstats --colour-nodes-by-selftime --show-samples /tmp/profile/* > profile.dot dot -Tpng profile.dot > profile.png
19:48é rapidjsonã®å°å ¥
Werkzeugã«ãããããã¡ã¤ã«ã®çµæãJSONçæã«æéãããã£ã¦ãããã¨ãå¤æãããããflask.jsonify()ãrapidjsonã«å·®ãæ¿ãã
20:15é WSGIãµã¼ããmeinheldã«å·®ãæ¿ãâå·®ãæ»ã
WSGIãµã¼ããgunicornããmeinheldã¸å·®ãæ¿ãã¦ã¿ããã®ã®å¤§å¹ ã«æ§è½ãä¸ããæ念ã
20:27
sleep対å¿ã§gunicornã®ã¯ã¼ã«ã¼ãæ¸ããã¦ã¹ã¬ãããå¤§å¹ ã«UP
/etc/systemd/system/isubata.python.service
ã§--workers=1 --threads=1000
ã¨è¨å®
20:31 MySQLãã©ã¡ã¼ã¿èª¿æ´
ã¢ããªå´ã§ Too many connections
ãåºã¦ããã®ã§MySQLè¨å®å¤æ´
max_connections = 8192
㨠open_files_limit ãè¨å®- /lib/systemd/system/mysql.service ã«
LimitNOFILE=65535
ã追è¨
20:35é ãã³ãã¬ã¼ãã¨ã³ã¸ã³Jinja2ã®ãã¥ã¼ãã³ã°
jinja2ã®bytecode_cacheãæå¹ã«
app.jinja_options = app.jinja_options.copy()
app.jinja_options['bytecode_cache'] = jinja2.FileSystemBytecodeCache()
20:49 ç»åãã¼ã¿ã®è¨ç½®å¨ããä¿®æ£
ã¢ãããã¼ããããç»åãtmpãã¡ã¤ã«ã«åºåããéã«æçµçãªè¨ç½®å ´æã«ãã¼ããªã³ã¯
os.link(f.name, output)
20:50éã åèµ·åãã¹ã
åèµ·åãã¹ãå®æ½
åèµ·åå¾ã«ãã³ãå®è¡ãããã¹ã³ã¢ãä¹±é«ä¸ããã®ã§ããæãã«ãªãã¾ã§ç¹°ãè¿ããã³ãå®è¡
ä¸è² è·å´ãå®å®ãã¦ã¦é«éã§å©ãã£ã
21:07ããã¹ãã¹ã³ã¢ãã§ãã¾ã§ãã³ããã¼ã¯ãç¹°ãè¿ã
æ¬æ¥ã®ãã¹ãã¹ã³ã¢ 205148
ãåºã¦æ声
21:13 çµäº
ãç²ãæ§ã§ããã