MySQL:左JOIN和orderby的组合很慢

MySQL:左JOIN和orderby的组合很慢,mysql,join,left-join,query-performance,Mysql,Join,Left Join,Query Performance,有两个表:posts(~5000000行)和关系(~8000行) 帖子栏目: ------------------------------------------------- | id | source_id | content | date (int) | ------------------------------------------------- --------------------------- | source_id | user_id | ----

有两个表:
posts
(~5000000行)和
关系(~8000行)

帖子
栏目:

-------------------------------------------------
|  id  |  source_id  |  content  |  date (int)  |
-------------------------------------------------
---------------------------
|  source_id  |  user_id  |
---------------------------
关系
列:

-------------------------------------------------
|  id  |  source_id  |  content  |  date (int)  |
-------------------------------------------------
---------------------------
|  source_id  |  user_id  |
---------------------------
我编写了一个MySQL查询,用于
帖子
中获取与特定用户相关的10个最新行

SELECT      p.id, p.content
FROM        posts AS p
LEFT JOIN   relations AS r
ON          r.source_id = p.source_id
WHERE       r.user_id = 1
ORDER BY    p.date DESC
LIMIT       10
但是,执行它需要约30秒

我已经在
关系中为
(source\u id,user\u id)
(user\u id)
(source\u id)
(date)
(date,source\u id)
设置了索引

解释
结果:

如何优化查询?

试试这个

    SELECT p.id, p.content FROM posts AS p
    WHERE p.source_id IN (SELECT source_id FROM relations WHERE user_id = 1)
    ORDER BY  p.date DESC
    LIMIT       10

您可以在posts表的date列上添加索引,我相信这将有助于加快订单的速度


在使用一些额外的where语句进行排序之前,您还可以尝试减少结果的数量。例如,如果你知道有可能有十个记录与正确的用户ID今天,你可以限制日期,直到今天(或N天回来取决于你的实际数据)。< / P> < P>我会考虑以下:-< /P> 首先,您只需要与用户相关的文章中最近的10行。因此,
内部联接
应该很好

SELECT      p.id, p.content
FROM        posts AS p
JOIN        relations AS r
ON          r.source_id = p.source_id
WHERE       r.user_id = 1
ORDER BY    p.date DESC
LIMIT       10
如果要获取不具有
关系
映射的记录,则需要
左连接。因此,执行
LEFT JOIN
会导致对左表进行完整的表扫描,根据您的信息,左表包含约5000000行。这可能是您查询的根本原因

为进一步优化,考虑将<<代码> 子句移动到<<代码> < /Cord>子句> < /P>

SELECT      p.id, p.content
FROM        posts AS p
JOIN        relations AS r
ON          (r.source_id = p.source_id AND r.user_id = 1)
ORDER BY    p.date DESC
LIMIT       10

WHERE子句将外部联接呈现为仅内部联接(因为在外部联接的伪记录中,user_id将始终为null,而不是1)

如果您真的希望这是一个外部连接,那么它是完全多余的,因为
posts
中的每个记录在
关系中都有或没有匹配项。你的问题将是

select id, content 
from posts 
order by "date" desc limit 10;
如果您不希望这是一个真正的外部联接,而是希望在
关系中匹配
,那么我们讨论的是表中的存在,
存在
子句中,因此:

select id, content
from posts
where source_id in
(
  select source_id
  from relations
  where user_id = 1
)
order by "date" desc
limit 10;
关系(user\u id,source\u id)
上应该有一个索引,按照这个顺序,我们可以先选择
user\u id
1,然后得到所有所需
source\u id
的数组,然后再进行查找

当然,你还需要一个关于
posts(source\u id)
的索引,因为
source\u id
是一个id。你甚至可以通过一个复合索引
posts(source\u id,date,id,content)
来加快速度,这样就不必再读取表本身了——所有需要的信息都已经在索引中了

更新:以下是相关的
存在查询:

select id, content
from posts p
where exists
(
  select *
  from relations r
  where r.user_id = 1
  and r.source_id = p.source_id
)
order by "date" desc
limit 10;

我会尝试使用关系综合指数:

INDEX source_user (user_id,source_id)
并将查询更改为:

SELECT      p.id, p.content
FROM        posts AS p
INNER JOIN   relations AS r 
ON ( r.user_id = 1 AND r.source_id = p.source_id )
ORDER BY    p.date DESC
LIMIT       10


执行它需要更长的时间(60秒)。子查询总是比join慢。我认为它很慢,因为它可能包含重复的行。无论如何。。感谢您将其呈现为内部连接!尝试将WHERE替换为AND。请发布“解释选择…”的结果再仔细考虑一下,您可能需要在r.user_id上单独添加一个索引,并从关系和左连接中选择posts@JanPapenbrock将WHERE替换为,并返回与用户无关的结果。已将解释结果添加到帖子中。抱歉,忘了提及–在
posts
中已有
date
索引。我明白你的意思,但是,没有什么可以限制它——日期可以是任意的。作者提到,在筛选(user\u id)和排序(date)列上同时使用多列索引可能会产生比单独使用多列索引更好的结果。您可能考虑了
source\u id
,而不是
user\u id
,因为它位于另一个表中。我为
(source\u id,date)
添加了一个索引,但是性能仍然很差。哎呀!这是在深夜使用堆栈溢出时发生的情况。我还以为他们在同一张桌子上呢。这给了我一个想法:如果user_id和source_id是1对1的关系,那么您可以修改您的模式,将user_id与source_id切换。或者,您可以对这两个表进行非规范化(请参阅)。我认为这取决于您多么希望这个查询运行得更快。希望这有帮助!我尝试了你的两个建议,但是,仍然是30秒的死刑。哇。。这太令人惊讶了。我对综合指数有点不确定。我曾经因为综合指数而表现不佳。因此,我会盲目地尝试删除
关系处的
(source\u id,user\u id)
帖子处的
(date,source\u id)
的复合索引。我还尝试了不同的索引变体,但这些都没有任何效果。可能我脑海中唯一想到的是,如果你的查询像“批处理”一样,每30秒执行一次?你能看到
explain
计划是否有任何改进吗?
explain
对于你的查询是完全相同的。没有批量的东西,因为如果我取消订单,它就像闪电一样快。这个问题在某种程度上是错误的。谢谢你,但是我以前有过这样的回答:-对性能没有影响。哦,我没有看到这个。那么您确实有提到的索引,但查询仍然很慢?我认为这是MySQL中的一大缺陷。你用的是旧版本吗?我将添加相关的
EXISTS
查询,可能MySQL在
中遇到了问题。是的,我尝试了您建议的索引。与存在的
相同
。MySQL版本–最新版本,SSD服务器。我尝试通过删除
orderby