MySql - 如何使用索引优化查询?
MySql - How to optimize query with indexes?
我们正在尝试从数据库中获取关注者的最新 10 条通知。我们进行了一些连接,以确保我们为关注者获得正确的通知集。如果他们关注的人(他们的领导者)添加了一个新的 post,那么追随者应该只会收到在他们开始关注领导者之后添加的 post 的通知(向他们展示他们的领导者的老朋友是没有意义的posts 作为新通知)。另一个连接是确保我们得到通知的 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`);
说明:
正确结果(大约需要 9 秒):
SQL 转储:
SQL DUMP TO REPRODUCE LOCALLY 只需在本地创建 speed_test
数据库并导入文件即可查看所有 table 数据的慢速查询问题 (~100K 行).
我们如何优化以上内容以在几毫秒内获得正确的结果?
对于此查询:
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)
。这可能会更快。
你应该试试这个。
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`);
我们正在尝试从数据库中获取关注者的最新 10 条通知。我们进行了一些连接,以确保我们为关注者获得正确的通知集。如果他们关注的人(他们的领导者)添加了一个新的 post,那么追随者应该只会收到在他们开始关注领导者之后添加的 post 的通知(向他们展示他们的领导者的老朋友是没有意义的posts 作为新通知)。另一个连接是确保我们得到通知的 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`);
说明:
正确结果(大约需要 9 秒):
SQL 转储:
SQL DUMP TO REPRODUCE LOCALLY 只需在本地创建 speed_test
数据库并导入文件即可查看所有 table 数据的慢速查询问题 (~100K 行).
我们如何优化以上内容以在几毫秒内获得正确的结果?
对于此查询:
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)
。这可能会更快。
你应该试试这个。
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`);