Следующий запрос предназначен для получения списка непрочитанных сообщений пользователем. Он включает в себя 3 таблицы: recipients
содержит отношение пользователей к идентификаторам сообщений, messages
содержит сами сообщения, а message_readers
содержит список сообщений, которые пользователи прочитали, какие сообщения.
Надежный запрос занимает 4,9 секунды - это серьезно сказывается на нашей производительности и вызывает особую тревогу, поскольку мы надеемся, что со временем база данных будет на несколько порядков больше. Конечно, это по своей сути тяжелый запрос, но набор данных крошечный, и интуитивно кажется, что он должен быть намного быстрее. На сервере достаточно памяти (32 ГБ), чтобы вся база данных всегда загружалась в ОЗУ, и на коробке больше ничего не работает.
Все таблицы крошечные:
recipients: 23581
messages: 9679
message_readers: 2685
Сам запрос:
SELECT
m.*
FROM
messages m
INNER JOIN recipients r ON r.message_id = m.id
LEFT JOIN message_readers mr ON mr.message_id = m.id
WHERE
r.id = $user_id
AND (mr.read_by_id IS NULL OR mr.read_by_id <> $user_id)
План объяснения довольно прост:
+----+-------------+-------+--------+-----------------------------------+-----------------------------------+---------+--------------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------------------+-----------------------------------+---------+--------------------------------+-------+-------------+
| 1 | SIMPLE | r | ref | index_recipients_on_id | index_recipients_on_id | 768 | const | 11908 | Using where |
| 1 | SIMPLE | m | eq_ref | PRIMARY | PRIMARY | 4 | db.r.message_id | 1 | Using index |
| 1 | SIMPLE | mr | ALL | NULL | NULL | NULL | NULL | 2498 | Using where |
+----+-------------+-------+--------+-----------------------------------+-----------------------------------+---------+--------------------------------+-------+-------------+
На message_readers.read_by_id
есть индекс IS, но я полагаю, он не может использовать его из-за условия IS NULL.
Я использую все настройки по умолчанию, кроме следующих:
key_buffer=4G
query_cache_limit = 256M
query_cache_size = 1G
innodb_buffer_pool_size=12G
Спасибо!