我有一张包含大约2千万条记录的表格。
结构就像:
EventId UNIQUEIDENTIFIER
SourceUserId UNIQUEIDENTIFIER
DestinationUserId UNIQUEIDENTIFIER
CreatedAt DATETIME
TypeId INT
MetaId INT
表每天收到大约10万条记录。
我在除MetaId之外的每一列上都有索引,因为它没有在'where'子句中使用
问题是当我想要拿起例如。所需SourceUserId的最新100条记录
查询有时需要4分钟才能执行,这是不可接受的。
例如
SELECT TOP 100 * FROM Events WITH (NOLOCK)
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461'
AND
(
TypeId IN (2, 3, 4)
OR
(TypeId = 60 AND SrcMemberId != DstMemberId)
)
ORDER BY CreatedAt DESC
我无法进行分区等操作,因为我使用的是标准版的SQL Server,而且Enterprise太贵了。
我也认为表格很小而且很慢。
我认为问题在于ORDER BY子句,因为db必须经历更大的数据集。
有任何想法如何让它更快?
也许关系数据库对于那种数据不是一个好主意。
始终通过CreatedAt DESC
订购数据感谢您的阅读。
PabloX
答案 0 :(得分:15)
您可能希望为此类查询创建复合索引 - 当查询运行缓慢时,很可能选择扫描CreatedAt列上的索引并对SourceUserId值执行残差过滤器,现实你想要发生的是直接跳转到正确订购的给定SourceUserId的所有记录 - 为此,你需要主要在SourceUserId上创建一个复合索引(执行相等性检查),然后在CreateAt上创建(以保留给定SourceUserId值内的顺序)。您可能还想尝试添加TypeId,具体取决于此列的选择性。
因此,最有可能提供最佳可重复性能的2(尝试它们并进行比较)将是:
与往常一样,在确定索引的方式/内容/位置时还需要考虑许多其他注意事项,正如Remus在单独的答案中讨论的那样,一个重要的考虑因素是覆盖查询与保持查找。此外,您还需要考虑写入卷,possible fragmentation impact (if any),单例查找与大型顺序扫描等等。
答案 1 :(得分:6)
我在每列上都有索引,除了 MetaId
非覆盖索引可能会达到'tipping point',查询将恢复为表扫描。只是在每个列上添加索引,因为它在where子句中使用并不等于良好的索引设计。以您的查询为例,一个好的100%覆盖索引将是:
INDEX ON (SourceUserId , CreatedAt) INCLUDE (TypeId, SrcMemberId, DstMemberId)
以下索引也很有用,尽管它仍然会导致查找:
INDEX ON (SourceUserId , CreatedAt) INCLUDE (TypeId)
最后一个索引没有任何包含的列可以帮助,但是可能会被忽略(取决于列统计和基数估计):
INDEX ON (SourceUserId , CreatedAt)
但是,对于您的查询,SourceUSerId和CreatedAt上的单独索引基本上没用。
答案 2 :(得分:5)
该表具有基于GUID值构建的索引,这表明可能会影响性能的一系列问题:
这里有一些关于如何调查和解决这些问题的资源:
答案 3 :(得分:1)
我建议在2个sep var表中获取数据
INSERT INTO @Table1
SELECT * FROM Events WITH (NOLOCK)
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461'
AND
(
TypeId IN (2, 3, 4)
)
INSERT INTO @Table2
SELECT * FROM Events WITH (NOLOCK)
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461'
AND
(
(TypeId = 60 AND SrcMemberId != DstMemberId)
)
然后从选择,有序和顶部应用联合。限制来自get go的数据。
答案 4 :(得分:1)
我建议使用UNION:
SELECT TOP 100 x.*
FROM (SELECT a.*
FROM EVENTS a
WHERE a.typeid IN (2, 3, 4)
UNION ALL
SELECT b.*
FROM EVENTS b
WHERE b.typeid = 60
AND b.srcmemberid != b.dstmemberid) x
WHERE x.sourceuserid = '15b534b17-5a5a-415a-9fc0-7565199c3461'
答案 5 :(得分:1)
我们通过移动到事件表的BIGINT IDENTITY键实现了轻微的收益;通过将其用作群集主键,我们可以作弊并将其用于日期排序。
答案 6 :(得分:0)
我会确保CreatedAt正确编入索引
答案 7 :(得分:0)
您可以使用UNION将查询拆分为两个以避免OR(这可能导致您的索引不被使用),例如
SElect * FROM(
SELECT TOP 100 * FROM Events WITH (NOLOCK)
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461'
AND TypeId IN (2, 3, 4)
UNION SELECT TOP 100 * FROM Events WITH (NOLOCK)
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461'
AND TypeId = 60 AND SrcMemberId != DstMemberId
)
ORDER BY CreatedAt DESC
另外,检查uniqueidentifier索引是否不是CLUSTERED。
答案 8 :(得分:0)
如果每天添加100K记录,则应检查索引碎片。 并相应地重建或重组它。 更多信息 : SQLauthority