未使用数据库索引导致查询缓慢

Posted

技术标签:

【中文标题】未使用数据库索引导致查询缓慢【英文标题】:Database index not being used causing slow query 【发布时间】:2014-06-05 12:08:11 【问题描述】:

我有以下两张表:

StudentCourse
- Id, 
- StudentId, 
- CourseId  

StudentIdCourseId 上的唯一索引

StudentCourseCount
- Id, 
- Student1Id, 
- Student2Id, 
- CourseCount  

Student1IdCourseCount 的索引

Student2IdCourseCount 上的索引

当我有CourseId 时,我会列出参加该课程的学生。我想要完成的关键是在一个学生下,我想列出他们以前一起学习过的其他学生。

我正在尝试以下查询:

SELECT * FROM StudentCourseCount sc
INNER JOIN StudentCourse s1 ON s1.course_id = <id> AND sc.student1_id = s1.student_id
INNER JOIN StudentCourse s2 ON s2.course_id = <id> AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1

查询按预期工作;但是,在我的超大表(超过 10,000,000 行)上它非常慢。

当我解释查询时,StudentCourseCount 不使用索引。它正确识别出Student1IdStudent2Id 可能存在索引,但两者都不使用。

执行计划:表:sc 可能的键:Student1Id,Student2Id 键: 空行:28648392

表:c2 键:student_id 行:1

表:c1 键:student_id 行:1

第一个表明显是在扫描,没有用key快速过滤。

【问题讨论】:

你能复制一下你的表的执行计划和索引定义吗? 我只是好奇。 . .哪个应用程序在StudentCourse 表中有 10,000,000 行? 我已经添加了索引并描述了查询执行计划(它没有很好地复制/粘贴,所以我重新编写了重要的部分)。 专业提示:避免在软件中使用SELECT *,尤其是在访问大型表的查询中。返回所有列的需要会使优化器难以巧妙地满足您的查询。如果您重写查询以枚举所需的列,您将帮助我们推荐有用的索引策略。 如果它在 StudentCourseCount 上进行表扫描,我猜WHERE sc.course_count &gt; 1 的选择性不够 【参考方案1】:

似乎您也应该将 course_id 过滤器放在外部选择中。 StudentCourseCount sc 上的唯一过滤器是 course_count。假设你只搜索 1 个 course_id,你应该有 sc.course_count>1 AND sc.course_id = id。否则,您的联接会尝试将过滤器应用于 sc.course_count>1 结果集。

假设值分布均匀,则此查询(或变体)应该是高效的。 10M 行不是很大,足够大,查询需要优化。

【讨论】:

sc 没有课程 ID,它包含学生及其人数。课程 ID 在 s1 和 s2 表中过滤。 啊,那么您似乎想先从 StudentCourse 表中进行选择,而不是 StudentCourseCount。【参考方案2】:

我认为 Brent Baisley 有一个很好的观点,我一开始没有看到 &lt;id&gt;。我猜你想让两个学生都在同一门课程中,这样你就可以在 Join 中链接他们并在 where 子句中获得 course_id=&lt;id&gt; 条件。我认为优化器应该自己做这些事情,但值得一试:

SELECT * FROM StudentCourseCount sc
INNER JOIN StudentCourse s1 ON sc.student1_id = s1.student_id
INNER JOIN StudentCourse s2 ON s2.course_id = s1.course_id AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1 AND s1.course_id = <id> 

【讨论】:

将 s1.course_id 移动到 where 子句中会更慢。所以这似乎没有帮助。 好的,那么问题又来了,您在 StudentCourseCount 和 StudentCourse 中有多少条记录?有多少 StudentCourseCount 记录符合 course_count>1 以及多少 StudentCourse 记录符合 course_id= 这是一个 3 对 1 的场景。计数包含比课程多 3 倍。对于每个课程 ID,它可能匹配 50-100 名学生。这些反过来与计数表中返回的时间相匹配,该课程有 50 名学生,它将返回每个学生与另一名学生上课的 500 个组合。 您能否更具体地了解表格和结果集的大小?如果 StudentCourse 表非常小,我想知道为什么它不用于扫描。【参考方案3】:

这是一个非常大的查询,它返回的结果集非常大。由于返回的数据量很大,我不确定您是否可以对其进行优化。

SELECT *
FROM StudentCourseCount sc INNER JOIN
     StudentCourse s1
     ON s1.course_id = <id> AND sc.student1_id = s1.student_id INNER JOIN
     StudentCourse s2
     ON s2.course_id = <id> AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1;

您想要的表索引是StudentCourseCount(course_count, student_id)StudentCourse(student_id, course_id)

现在,您说此查询有效,我假设您的意思是您喜欢结果。它正在回答以下问题:

获取所有上过课程id并且还上过不止一门课程的学生对

这与:

我想在一个学生下面列出他们以前一起学习过的其他学生。

如果这是您真正的问题,我建议您在 Stack Overflow 上提出另一个问题,以获得更好的查询。

【讨论】:

以上是关于未使用数据库索引导致查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章

一个索引导致查询缓慢

如何解决SQL Server查询速度缓慢的问题

PostgreSQL:未使用的索引导致查询性能不佳?

使用两个索引列时缓慢的 mySQL 查询

mysql查询缓慢原因和解决方案

10-07重新生成和组织索引