MySql-如何使用索引优化查询?

MySql-如何使用索引优化查询?,mysql,sql,select,left-join,innodb,Mysql,Sql,Select,Left Join,Innodb,我们正在尝试从数据库中获取跟踪者的最新10个通知。我们做了一些连接来确保为跟随者获得正确的通知集。如果他们跟随的人(他们的领导者)添加了一个新帖子,那么追随者应该只获得在他们开始跟随领导者之后添加的帖子的通知(没有意义将他们领导者的旧帖子显示为新通知)。另一个连接是确保我们在时间获得通知的read\u,以便跟随者知道它是否已经被读取。这是查询,但它需要9秒,速度太慢了。理想情况下,只需几毫秒,特别是索引: 查询: SELECT nf.id, nf.uuid, nf.leader_id, nf.d

我们正在尝试从数据库中获取跟踪者的最新10个通知。我们做了一些连接来确保为跟随者获得正确的通知集。如果他们跟随的人(他们的领导者)添加了一个新帖子,那么追随者应该只获得在他们开始跟随领导者之后添加的帖子的通知(没有意义将他们领导者的旧帖子显示为新通知)。另一个连接是确保我们在时间获得通知的
read\u,以便跟随者知道它是否已经被读取。这是查询,但它需要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`);
索引:

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`);
解释:

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秒):

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`);

SQL转储:

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`);
只需在本地创建
speed\u test
数据库并导入文件,就可以看到所有表数据(~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;
我会推荐关于
用户跟随者(领导者id、跟随者id、跟随者状态、创建时间)的索引
通知跟随者读取(通知跟随者id、跟随者id、读取时间)
。索引中列的顺序很重要

注意,我将第一个
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\u 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\u follows(follower\u id,follow\u status,leader\u id,created\u at)
notification\u followers(leader\u id,created\u 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`);

六羟甲基三聚氰胺六甲醚。。。实现了查询/索引,但需要约7秒。顺便说一句,您的查询缺少我添加到您的查询中的
限制10
,但仍然需要很长时间才能获得结果。因此,使用原始索引建议重写的查询在follower_id
14
(需要几毫秒)上运行得非常快,但当我将其更改为follower_id
1
,没有返回结果,这是正确的,但需要~4s才能返回结果。有没有办法让它像
14
那样更快地卷土重来?顺便说一句,我试着用相应的索引替换内部子查询,但是对于
14
我的上一条评论有任何反馈吗?@Wonka。不是真的。不清楚为什么id的选择很重要,除非“1”有很多、更多的行与之关联。事实上,1没有任何行与之关联,这就是为什么奇怪的是,它需要超过几毫秒才能返回它没有结果。不管怎样,您的查询确实帮助提高了速度,因此感谢您对查询和索引进行了重新构造:)这是一个内部联接,对吗?不确定,但是使用当前的
左联接返回正确的结果
通知被读取后,通知\u followers\u id是否会存在于通知\u followers\u read表中?还是始终可用?读取后。您的查询不会在问题中返回正确的结果。我已对您的问题发表了评论,并根据该假设进行了工作,现在,我已经根据您的评论进行了更正。因此,id
14
返回结果需要约4秒,而不是几毫秒。另一方面,id
1
返回正确的结果(没有项目)需要几毫秒。因此,这与戈登·林诺夫(Gordon Linoff)的上述回答(见我的评论)正好相反。我希望两者都能得到几毫秒,不管是使用id
14
还是
1