优化相对基本的JOIN和GROUP BY查询

时间:2017-07-19 07:49:26

标签: mysql sql performance indexing query-optimization

我有一个相对基本的查询,可以获取每个对话的最新消息:

SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message` 
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id` 
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id` 
ORDER BY `max_add_time` DESC
LIMIT 12

message表包含911000条以上的记录,conversation表包含大约680000条。此查询的执行时间在4到10秒之间,具体取决于服务器上的负载。这太长了。

以下是EXPLAIN结果的屏幕截图:

enter image description here

原因显然是MAX和/或GROUP BY,因为以下类似的查询只需要10毫秒:

SELECT COUNT(*) 
FROM `message` 
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id` 
WHERE (`message`.`status`=0) 
AND (`message`.`user_id` <> 1) 
AND ((`conversation`.`sender_user_id` = 1 OR `conversation`.`receiver_user_id` = 1))

相应的EXPLAIN结果:

enter image description here

我尝试在两个表中添加不同的索引而没有任何改进,例如:conv_msg_idx(add_time, conversation_id) message似乎根据第一个EXPLAIN结果使用,但查询仍然大约需要10秒钟才能执行。

非常感谢任何改进索引或查询以帮助缩短执行时间的帮助。

修改

我已将查询更改为使用INNER JOIN

SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message` 
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id` 
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id` 
ORDER BY `max_add_time` DESC
LIMIT 12

但是执行时间仍然是~6秒。

5 个答案:

答案 0 :(得分:0)

SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message` 
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id` 
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id` 
ORDER BY `max_add_time` DESC
LIMIT 12

如果你的逻辑没有受到影响,你可以试试INNER JOIN

答案 1 :(得分:0)

您应该在WHERE子句中的列上创建多列索引,并且您要选择哪些列(conversation_id除外)。 (reference) conversation_id应该是两个表中的索引。

答案 2 :(得分:0)

尽量避免在Sql查询中使用'或'这会使获取速度变慢。而是使用union或任何其他方法。

SELECT message.conversation_id,MAX(message.add_time)AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE(conversation.sender_user_id = 1 AND conversation.status!= -1))GROUP BY conversation_id 联合

SELECT message.conversation_id,MAX(message.add_time)AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE((conversation.receiver_user_id = 1 AND conversation.status!= -2))GROUP BY conversation_id ORDER BY max_add_time DESC LIMIT 12

答案 3 :(得分:0)

您可以通过避免使用max()来修改此查询

   select * from(
select row_number() over(partition by conversation_id  order by add_time desc)p1
                )t1  where t1.p1=1

答案 4 :(得分:0)

不是依赖于单个表message,而是有两个表:一个用于message,一个用于保存消息线程状态的另一个thread。 / p>

是的,添加新邮件需要更多工作 - 在thread更新一两列。

但它消除了导致此查询失败的GROUP BYMAX

执行此拆分时,请查看新表中是否有其他列更好。