MySql - Как оптимизировать запрос с помощью индексов? - PullRequest
0 голосов
/ 22 мая 2018

Мы пытаемся получить последние 10 уведомлений для подписчика из базы данных.Мы делаем несколько объединений, чтобы гарантировать, что мы получим правильный набор уведомлений для подписчика.Если подписчик (его лидер) добавил новое сообщение, подписчик должен получать уведомление только о тех сообщениях, которые были добавлены после того, как он начал следить за лидером (не имеет смысла показывать им старые посты своего лидера в качестве новых уведомлений).Другое объединение - убедиться, что мы получаем время уведомления read_at, чтобы последователь знал, было ли оно уже прочитано или нет.Вот запрос, но он занимает ~ 9 секунд , что слишком медленно.В идеале это займет всего несколько мс, особенно с индексами:

Запрос:

SELECT nf.id, nf.uuid, nf.leader_id, nf.data, nf.created_at, nfr.read_at
FROM notification_followers nf
LEFT JOIN user_follows uf ON uf.leader_id = nf.leader_id AND uf.follower_id = 14 AND uf.follow_status = 'follow'
LEFT JOIN notification_followers_read nfr ON nf.id = nfr.notification_followers_id AND nfr.follower_id = 14
WHERE (nf.created_at > uf.created_at)
ORDER BY nf.id DESC
LIMIT 10

Индексы:

ALTER TABLE `notification_followers` ADD INDEX `nf_lid_ca_id_idx` (`leader_id`,`created_at`,`id`);
ALTER TABLE `user_follows` ADD KEY`uf_fid_lid_fs_ca_idx` (`follower_id`,`leader_id`,`follow_status`,`created_at`)
ALTER TABLE `notification_followers_read` ADD INDEX `nfr_fid_nfid_ra_idx` (`follower_id`,`notification_followers_id`,`read_at`);

Объясните:

enter image description here

Правильные результаты (за ~ 9 секунд):

enter image description here

SQL DUMP:

SQL DUMP для локального воспроизведения просто создать speed_testбазы данных локально и импортируйте файл, чтобы увидеть проблему медленных запросов в режиме реального времени со всеми данными таблицы (~ 100K строк) .

Как можно оптимизировать вышеприведенное, чтобы получить правильные результаты в течение нескольких мс

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Вы должны попробовать это.

SELECT nf.id, nf.uuid, nf.leader_id, nf.data, nf.created_at, nfr.read_at 
FROM notification_followers nf 
JOIN user_follows uf ON uf.leader_id = nf.leader_id and nf.created_at > uf.created_at AND uf.follow_status = 'follow'  AND uf.follower_id = 14 
LEFT JOIN notification_followers_read nfr ON nf.id = nfr.notification_followers_id AND nfr.follower_id = 14 
ORDER BY nf.id DESC
LIMIT 10;

Создать индексы на.

ALTER TABLE `notification_followers` ADD INDEX `nf_lid_ca_id_idx`(`leader_id`,`created_at`,`id`);
ALTER TABLE `user_follows` ADD KEY`uf_fid_lid_fs_ca_idx`(`leader_id`,`created_at`,`follow_status`,`follower_id`)
ALTER TABLE `notification_followers_read` ADD INDEX `nfr_fid_nfid_ra_idx`(`notification_followers_id`,`follower_id`,`read_at`);
0 голосов
/ 23 мая 2018

Для этого запроса:

SELECT nf.id, nf.uuid, nf.leader_id, nf.data, nf.created_at, nfr.read_at
FROM notification_followers nf JOIN
     user_follows uf 
     ON uf.leader_id = nf.leader_id AND uf.follower_id = 14 AND
        uf.follow_status = 'follow' LEFT JOIN 
     notification_followers_read nfr
     ON nf.id = nfr.notification_followers_id AND nfr.follower_id = 14
WHERE nf.created_at > uf.created_at
ORDER BY nf.id DESC
LIMIT 10;

Я бы порекомендовал индексы на user_follower(leader_id, follower_id, follow_status, created_at) и notification_followers_read(notification_followers_id, follower_id, read_at).Порядок столбцов в индексах имеет значение.

Обратите внимание, что я изменил первое JOIN на внутреннее соединение, потому что предложение WHERE все равно превращает его в одно.

Хммм, давайте попробуем переписать запрос:

SELECT nf.id, nf.uuid, nf.leader_id, nf.data, nf.created_at,
       (SELECT nfr.read_at
        FROM notification_followers_read nfr
        WHERE nf.id = nfr.notification_followers_id AND nfr.follower_id = 14
       ) nfr
FROM (SELECT nf.*
      FROM notification_followers nf 
      WHERE EXISTS (SELECT 1
                    FROM user_follows uf 
                    WHERE uf.leader_id = nf.leader_id AND uf.follower_id = 14 AND
                          uf.follow_status = 'follow' AND nf.created_at > uf.created_at
                   )
      ORDER BY nf.id DESC
      LIMIT 10
     ) nf;

Для этого вы должны быть уверены, что у вас есть индекс и на notification_followers(id).

В зависимости отваши данные, внутренний подзапрос может быть быстрее при таком подходе:

FROM (SELECT nf.*
      FROM user_follows uf JOIN
           notification_followers nf 
           ON uf.leader_id = nf.leader_id AND nf.created_at > uf.created_at
      WHERE uf.follower_id = 14 AND uf.follow_status = 'follow' 
      ORDER BY nf.id DESC
      LIMIT 10
     ) nf

Для этого индексы user_follows(follower_id, follow_status, leader_id, created_at) и notification_followers(leader_id, created_at, id).Это может быть быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...