# Query_time: 186.225664 Lock_time: 0.000000
# Rows_sent: 15 Rows_examined: 23 536 988
select m.id, m.title, avg(r.rating)
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
join ratings r on (r.movie_id = m.id)
where g.name = 'Comedy'
and t.name = 'Zombie'
group by m.id, m.title
order by avg(r.rating) desc
# Query_time: 194.333533 Lock_time: 0.000000
# Rows_sent: 15 Rows_examined: 633 731
select m.id, m.title, avg(r.rating)
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
join ratings r on (r.movie_id = m.id)
where g.name = 'Comedy'
and t.name = 'Zombie'
group by m.id, m.title
order by avg(r.rating) desc
# Query_time: 1.264987 Lock_time: 0.000000
# Rows_sent: 15 Rows_examined: 634 202
select m.id, m.title, avg(r.rating)
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
join ratings r on (r.movie_id = m.id)
where g.name = 'Comedy'
and t.name = 'Zombie'
group by m.id, m.title
order by avg(r.rating) desc
Удалось ускорить в 147.79 раз! Победа?
Что-то не похоже :(
# Query_time: 1.264987 Lock_time: 0.000000
# Rows_sent: 15 Rows_examined: 634 202
select m.id, m.title, avg(r.rating)
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
join ratings r on (r.movie_id = m.id)
where g.name = 'Comedy'
and t.name = 'Zombie'
group by m.id, m.title
order by avg(r.rating) desc
# Query_time: 14.323418 Rows_examined: 23 618 206 (без индексов)
# Query_time: 25.683016 Rows_examined: 81 575 (с индексами)
# Query_time: 0.178307 Rows_examined: 82 046 (с покрывающим индексом)
select m.id, m.title, m.cnt, avg(r.rating)
from (
select m.id, m.title, count(*) as cnt
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
where g.name = 'Comedy‘
and t.name = 'Zombie‘
group by m.id, m.title
) m
join ratings r on (r.movie_id = m.id)
group by m.id, m.title, m.cnt
order by avg(r.rating) desc
alter table movies
add column rating_sum float8 default 0 not null,
add column rating_cnt int default 0 not null;
update movies m join (
select movie_id, count(*) as rating_cnt, sum(rating) as rating_sum
from ratings
group by movie_id
) r on (m.id = r.movie_id)
Set
m.rating_cnt = r.rating_cnt,
m.rating_sum = r.rating_sum;
create trigger after insert ...;
create trigger after update ...;
create trigger after delete ...;
# Query_time: 0.015626 Lock_time: 0.000000
# Rows_sent: 15 Rows_examined: 813
select m.id, m.title, case
when m.rating_cnt > 0
then m.rating_sum / m.rating_cnt
else null
end
from movies m
join movie_genres gm on (gm.movie_id = m.id)
join genres g on (g.id = gm.genre_id)
join movie_tags tm on (tm.movie_id = m.id)
join tags t on (t.id = tm.tag_id)
where g.name = 'Comedy'
and t.name = 'Zombie'
group by m.id, m.title
Уменьшение времени блокировок за счет избавления от крупных запросов вида «обновить всё».
До:
DELETE FROM work_logs
WHERE created_at > ADDDATE(DATE(NOW()), INTERVAL -90 DAY);
После (выполнять в цикле):
BEGIN;
DELETE FROM work_logs
WHERE created_at > ADDDATE(DATE(NOW()), INTERVAL -90 DAY)
LIMIT 10000;
COMMIT;
До:
BEGIN;
INSERT INTO spent_daily
SELECT :day, level, SUM(spent_daily) FROM players
GROUP BY level;
UPDATE players SET spent_daily = 0;
END;
UPDATE players SET
spent_day = spent_day + :delta,
money = money - :delta
WHERE id = :id;
После:
BEGIN;
INSERT INTO spent_daily
SELECT :day, level, SUM(CASE
WHEN spent_day = :day THEN spent_prev
WHEN spent_day = :day - 1 THEN spent_last
END) FROM players;
END;
UPDATE players SET
spent_prev = CASE
WHEN spent_day = :day THEN spent_prev
WHEN spent_day = :day - 1 THEN spent_last
ELSE 0
END,
spent_last = CASE
WHEN spent_day = :day THEN spent_last ELSE 0
END + :delta,
spent_day = :day,
money = money - :delta
WHERE id = :id;
Массовая замена сгенерированных ресурсов на один с мета-данными.
Решение «в лоб»:
UPDATE items SET res_id = 73534, level = 1, meta = 1001
WHERE res_id = 40477;
UPDATE items SET res_id = 73534, level = 1, meta = 1201
WHERE res_id = 40478;
UPDATE items SET res_id = 73534, level = 2, meta = 1031
WHERE res_id = 40479;
…
UPDATE items SET res_id = 73534, level = 80, meta = 7641
WHERE res_id = 70477;
Более красивое решение:
CREATE TEMPORARY TABLE tmp_items (
res_id INT,
level SMALLINT,
meta VARBINARY(64)
) ENGINE = MEMORY;
INSERT INTO items VALUES
(1, 1001, 40477),
(1, 1201, 40478),
…
(80, 7641, 70477);
UPDATE items I JOIN tmp_items t ON (i.res_id = t.res_id)
SET
i.res_id = 73534,
i.level = t.level,
i.meta = t.meta;
Загрузка данных через LOAD DATA гораздо быстрее:
load data local infile 'tags.csv'
into table movie_tags
fields
terminated by ','
enclosed by '"'
escaped by ''
lines
terminated by '\r\n'
ignore 1 lines
(user_id, movie_id, tag, @created)
set created = from_unixtime(@created);
К следующему рубежному контролю надо будет сделать задание №3.
К рубежному контролю будут допущены только те, кто сделал ДЗ №3.