webapp/perl/lib/Isucon3/Web.pm
“/”
my $memos = $self->dbh->select_all(
'SELECT * FROM memos WHERE is_private=0 ORDER BY
created_at DESC, id DESC LIMIT 100'
);
for my $memo (@$memos) {
$memo->{username} = $self->dbh->select_one(
'SELECT username FROM users WHERE id=?',
$memo->{user},
);
}
100回ルーーーープ
webapp/perl/lib/Isucon3/Web.pm
my $memos = $self->dbh->select_all(
'SELECT memos.*,users.username
FROM memos JOIN users ON memos.user = users.id
WHERE memos.is_private=0
ORDER BY memos.created_at DESC,
memos.id DESC
LIMIT 100'
);
“/”, “/recent”
indexがないと
SELECT * FROM memos WHERE is_private=0 ORDER BY
created_at DESC LIMIT 100
memosテーブル
id is_priv
ate
...
0
0
1
0
1
id is_priv
ate
...
0
0
0
ソート
webapp/perl/lib/Isucon3/Web.pm
抽出
CPU負荷高い
indexをつくる
init.sh
cat <<'EOF' | mysql -u isucon isucon
ALTER TABLE memos ADD INDEX (is_private,created_at);
EOF
titleカラムの追加し、
init.sh
事前生成
cat <<'EOF' | mysql -u isucon isucon
ALTER TABLE memos ADD COLUMN title text;
UPDATE memos SET
title = substring_index(content,"n",1);
EOF
MySQLのOFFSET処理のイメージ
1 2 3 4
id title user ... . id title user ... . id title user ... . id title user ... .
5 6 7 8
id title user ... . id title user ... . id title user ... . id title user ... .
9 10 11 12
id title user ... . id title user ... . id title user ... . id title user ... .
13
id title user ... .
10000
id title user ... .
10001 10002 10003 10004
id title user ... . id title user ... . id title user ... . id title user ... .
MySQLのOFFSET処理のイメージ
頑張ってソート
1 2 3 4
id title user ... . id title user ... . id title user ... . id title user ... .
5 6 7 8
id title user ... . id title user ... . id title user ... . id title user ... .
9 10 11 12
id title user ... . id title user ... . id title user ... . id title user ... .
13
id title user ... .
10000
id title user ... .
10001 10002 10003 10004
id title user ... . id title user ... . id title user ... . id title user ... .
必要な個数まで到達
MySQLのOFFSET処理のイメージ
頑張ってソート
1 2 3 4
id title user ... . id title user ... . id title user ... . id title user ... .
5 6 7 8
id title user ... . id title user ... . id title user ... . id title user ... .
9 10 11 12
id title user ... . id title user ... . id title user ... . id title user ... .
13
id title user ... .
10000
id title user ... .
10001 10002 10003 10004
id title user ... . id title user ... . id title user ... . id title user ... .
必要な個数まで到達
廃棄
MySQLのOFFSET処理のイメージ
1 2 3 4 5 6 7 8 9 10 11 12 13
id id id id id id id id id id id id id ・・・・・
・・・・・
9999
10000
id id
10001 10002 10003 10004
id id id id
MySQLのOFFSET処理のイメージ
1 2 3 4 5 6 7 8 9 10 11 12 13
id id id id id id id id id id id id id ・・・・・
10001 10002 10003 10004 廃棄
・・・・・
9999
10000
id id
id id id id
読むデータも、捨て
るデータも少ない
MySQLのインデックスと
データの持ち方
title user ... title user ...
user ... PRIMARY title user ...
title user ...
title user ...
title user ... title user ... title KEY
CLUSTERED INDEX
リーフノードに
データを含む
small large
id
id
id
id
id
id
id
id
MySQLのインデックスと
データの持ち方
SECONDARY KEY
primary keyじゃないkey
リーフノードに
PRIMARY KEYが含まれ、
データはCLUSTERED INDEX
から取得
is_private
id id id id id id id id
created_at
older newer older newer
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT * の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
何度も繰り返す
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
indexだけで
探索が終わる
SELECT id の場合
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
title user ... .
PRIMARY KEY
id
id
id
id
id
id
id
id
SECONDARY KEY
id id id id id id id id
is_private
created_at
indexだけで= “Covering Index”
探索が終わる
(1) IN 句
クエリ1
SELECT id FROM memos
WHERE is_private = 0
ORDER BY created_at DESC, id DESC
LIMIT 100 OFFSET 100000
クエリ2 ID羅列
SELECT * FROM memos
WHERE id IN (10000,10001,10002,1003,....)
ORDER BY created_at DESC, id DESC
(2) SELF JOIN
クエリ
SELECT memos.id, memos.title, memos.is_private,
memos.created_at, users.username
FROM memos, users,
(SELECT id FROM memos WHERE is_private = 0 ORDER
BY created_at DESC, id DESC LIMIT 100) AS t
WHERE t.id = memos.id
AND users.id = memos.user
サブクエリーを使用し
派生テーブル”t”と派生テーブル”t”を作成
元のテーブルをJOIN
init.sh
cat <<'EOF' | mysql -u isucon isucon
ALTER TABLE memos ADD INDEX (is_private,created_at),
ADD INDEX mypage(user,created_at),
ADD INDEX memo_private(user,is_private,created_at)
EOF
webapp/perl/lib/Isucon3/Web.pm
my $memos = $self->dbh->select_all(
"SELECT id FROM memos WHERE user=? $cond ORDER
BY created_at",
$memo->{user},
);
“/memo/xxx”
元は”*”だが、Covering Indexを
狙って”id”に変更
B-Treeでイメージ
PRIMARY KEY
older newer
id
id
id
id
id
id
id
id
memo
memo
memo
memo
memo
memo
memo
memo
BETWEEN 10001 AND 10100
init.sh
cat <<'EOF' | mysql -u isucon isucon
DROP TABLE IF EXISTS public_memos;
CREATE TABLE public_memos (
id INT NOT NULL AUTO_INCREMENT,
memo int DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO public_memos (memo) SELECT id FROM memos
WHERE is_private=0 ORDER BY created_at ASC, id ASC;
EOF
* innodb_autoinc_lock_mode の影響で
InnoDBではauto increment が連続した値にならない可能性がある
webapp/perl/lib/Isucon3/Web.pm
my $total = $self->dbh->select_one(
'SELECT MAX(id) FROM public_memos'
);
my $memos = $self->dbh->select_all(
'SELECT memos.id, memos.title, memos.is_private,
memos.created_at, users.username
FROM memos,users,
(SELECT memo FROM public_memos WHERE id
BETWEEN ? AND ? ORDER BY id DESC) AS t
WHERE t.memo = memos.id
AND users.id=memos.user',
$total-99, $total
);
“/” or “/recent/xxx”
post “/memo”
webapp/perl/lib/Isucon3/Web.pm
my $memo_id = $self->dbh->last_insert_id;
if ( ! scalar($c->req->param('is_private')) ) {
$self->dbh->query('INSERT INTO public_memos
(memo) VALUES (?)',$memo_id);
}
is_private = 0 なら
public_memosにもinsert