sql - 查询 250k 行需要 53 秒

标签 sql linq-to-sql query-optimization

运行此查询的机器是在数据中心运行的专用服务器。

AMD Opteron 1354 四核 2.20GHz
2GB 内存
Windows Server 2008 x64(是的,我知道我只有 2GB 的 RAM,当项目上线时我将升级到 8GB)。

所以我仔细检查并在一个表中创建了 250,000 个虚拟行,以真正对 LINQ to SQL 生成的一些查询进行压力测试,并确保它们不会太糟糕,我注意到其中一个花费了荒谬的时间。

我使用索引将这个查询缩短到 17 秒,但为了这个答案从头到尾我删除了它们。只有索引是主键。

Stories table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NOT NULL,
[CategoryID] [int] NOT NULL,
[VoteCount] [int] NOT NULL,
[CommentCount] [int] NOT NULL,
[Title] [nvarchar](96) NOT NULL,
[Description] [nvarchar](1024) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[UniqueName] [nvarchar](96) NOT NULL,
[Url] [nvarchar](512) NOT NULL,
[LastActivityAt] [datetime] NOT NULL,

Categories table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[ShortName] [nvarchar](8) NOT NULL,
[Name] [nvarchar](64) NOT NULL,

Users table --
[ID] [int] IDENTITY(1,1) NOT NULL,
[Username] [nvarchar](32) NOT NULL,
[Password] [nvarchar](64) NOT NULL,
[Email] [nvarchar](320) NOT NULL,
[CreatedAt] [datetime] NOT NULL,
[LastActivityAt] [datetime] NOT NULL,

目前在数据库中有 1 个用户、1 个类别和 250,000 个故事,我尝试运行此查询。
SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt

查询运行需要 52 秒,CPU 使用率徘徊在 2-3%,Membery 是 1.1GB,900MB 空闲但磁盘使用率似乎失控。它是@ 100MB/秒,其中 2/3 写入 tempdb.mdf,其余部分从 tempdb.mdf 读取。

现在是有趣的部分......
SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
INNER JOIN Users ON Users.ID = Stories.UserID

SELECT TOP(10) *
FROM Stories
INNER JOIN Users ON Users.ID = Stories.UserID
ORDER BY Stories.LastActivityAt

SELECT TOP(10) *
FROM Stories
INNER JOIN Categories ON Categories.ID = Stories.CategoryID
ORDER BY Stories.LastActivityAt

所有这三个查询几乎都是即时的。

第一个查询的执行计划。
http://i43.tinypic.com/xp6gi1.png

执行其他 3 个查询的计划(按顺序)。
http://i43.tinypic.com/30124bp.png
http://i44.tinypic.com/13yjml1.png
http://i43.tinypic.com/33ue7fb.png

任何帮助将非常感激。

添加索引后的执行计划(再次下降到 17 秒)。
http://i39.tinypic.com/2008ytx.png

我从每个人那里得到了很多有用的反馈,谢谢你,我尝试了一个新的角度。我查询我需要的故事,然后在单独的查询中获取类别和用户,3 次查询只用了 250 毫秒……我不明白这个问题,但如果它有效并且暂时不低于 250 毫秒,我会坚持下去。这是我用来测试的代码。
DBDataContext db = new DBDataContext();
Console.ReadLine();

Stopwatch sw = Stopwatch.StartNew();

var stories = db.Stories.OrderBy(s => s.LastActivityAt).Take(10).ToList();
var storyIDs = stories.Select(c => c.ID);
var categories = db.Categories.Where(c => storyIDs.Contains(c.ID)).ToList();
var users = db.Users.Where(u => storyIDs.Contains(u.ID)).ToList();

sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

最佳答案

尝试在 Stories.LastActivityAt 上添加索引。我认为执行计划中的聚集索引扫描可能是由于排序。

编辑:
由于我的查询立即返回,行只有几个字节长,但已经运行了 5 分钟,并且在添加 2K varchar 后仍在运行,我认为 Mitch 有道理。它是无用地混洗的数据量,但这可以在查询中修复。

尝试将 join、sort 和 top(10) 放在 View 或嵌套查询中,然后再连接到故事表以获取仅用于所需 10 行的其余数据。

像这样:

select * from 
(
    SELECT TOP(10) id, categoryID, userID
    FROM Stories
    ORDER BY Stories.LastActivityAt
) s
INNER JOIN Stories ON Stories.ID = s.id
INNER JOIN Categories ON Categories.ID = s.CategoryID
INNER JOIN Users ON Users.ID = s.UserID

如果您在 LastActivityAt 上有一个索引,它应该运行得非常快。

关于sql - 查询 250k 行需要 53 秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/608992/

相关文章:

javascript - 高级搜索功能 - 为最终用户提供查询构建器

java - JOOQ 中带有列表的 IN 谓词仅返回第一个值

mysql - 使用 View 进行递归 cte

c# - LINQ options.loadwith问题

mysql - Linq-查询主体必须以 C# 中的 select 子句或 group 子句结尾

C# Linq 到 SQL :cannot add an entity that already exists

mysql - 具有相同参数的格式相同的查询在MySQL 8.0中具有完全不同的执行时间

mysql - 如何从多个表中只获取特定记录?

mysql - 更新mysql中从1开始的具有增量整数值的列的值

MySQL:大表需要join,如何拆分优化?