【问题标题】:Optimal Strategy to Resolve Performance in Search Operations - SQL Server 2008解决搜索操作性能问题的最佳策略 - SQL Server 2008
【发布时间】:2023-03-11 00:05:01
【问题描述】:

我正在开发一个越来越受欢迎的移动网站,这导致一些关键数据库表的增长 - 我们开始在访问这些表时看到一些性能问题。不是数据库专家(现阶段也没有钱聘请任何人),我们正在努力了解导致性能问题的原因。我们的表没有那么那么大,因此 SQL Server 应该能够很好地处理它们,并且我们已经完成了我们在优化查询方面所知道的一切。所以这是(伪)表结构:

[user] (approx. 40,000 rows, 37 cols):

id               INT (pk)
content_group_id INT (fk)
[username]       VARCHAR(20)
...

[content_group] (approx. 200,000 rows, 5 cols):

id    INT (pk)
title VARCHAR(20)
...

[content] (approx. 1,000,000 rows, 12 cols):

id                  INT (pk)
content_group_id    INT (fk)
content_type_id     INT (fk)
content_sub_type_id INT (fk)
...

[content_type] (2 rows, 3 cols)

id INT (pk)
...

[content_sub_type] (8 rows, 3 cols)
id              INT (pk)
content_type_id INT (fk)
...

我们预计这些行数会大幅增长(尤其是 user、content_group 和 content 表)。是的,用户表有很多列——我们已经确定了一些可以移动到其他表中的列。我们还对受影响的表应用了一些索引,这些索引也有所帮助。

最大的性能问题是我们用于搜索用户的存储过程(包括在 content_group_id 字段上连接到内容表)。我们尝试使用各种不同的方法修改 WHEREAND 子句,我们认为我们已经尽可能地完善它们,但仍然太慢了。

我们尝试过的另一件没有帮助的事情是在用户表和内容表上放置一个索引视图。当我们这样做时没有明显的性能提升,因此我们放弃了这个想法,因为拥有视图层固有的额外复杂性。

那么,我们有哪些选择?我们可以想到一些,但都各有利弊:

表结构的非规范化

在用户表和内容表之间添加多个直接外键约束 - 因此每个内容子类型的内容表会有不同的外键。

优点:

  • 通过使用其主键加入内容表将更加优化。

缺点:

  • 我们现有的存储过程和网站代码会有很多变化。
  • 维护多达 8 个额外的外键(更实际地,我们只使用其中的 2 个)不会像当前的单个键那样容易。

表结构的更多非规范化

只需将我们需要的字段从内容表直接复制到用户表中即可。

优点:

  • 不再连接内容表 - 这大大减少了 SQL 必须做的工作。

缺点

  • 同上:用户表中需要维护的额外字段,SQL 和网站代码的更改。

创建中间层索引层

使用类似 Lucene.NET,我们会在数据库之上放置一个索引层。从理论上讲,这将提高所有搜索的性能,同时减少服务器上的负载。

优点:

  • 这是一个很好的长期解决方案。 Lucene 的存在是为了提高搜索引擎的性能。

缺点:

  • 短期内会有更大的开发成本 - 我们需要尽快解决这个问题。

所以这些是我们想出的东西,在这个阶段,我们认为第二种选择是最好的 - 我知道非规范化有它的问题,但有时最好牺牲架构纯度以获得性能提升,因此我们准备支付这笔费用。

还有其他方法可能对我们有用吗?我上面概述的方法是否有任何其他优点和/或缺点可能会影响我们的决定?

【问题讨论】:

  • 你能发布其中一个慢查询(加上执行计划)吗?
  • 另外,SQL Server 版本是多少?
  • 我可以告诉您,我们使用的是 SQL Server 2008 - 但是,在与同事讨论后,我们决定实际查询包含的细节过于敏感,我们无法发布。我可以告诉你的是,大部分查询成本是使用 content_sub_type_id 从内容表中进行非聚集索引查找。接下来是针对内容表的 content_group_id 哈希匹配 - 这些占性能影响的 75%。第三个代价高昂的操作是针对用户表的 id 字段的 != 子句。抱歉,我不能比这更具体了。

标签: sql-server sql-server-2008 search database-performance


【解决方案1】:

非聚集索引使用 content_sub_type_id。接下来是哈希匹配 content_group_id 针对内容表

此描述表明您的昂贵查询根据来自content_type 的字段过滤了content 表:

select ...
from content c
join content_type ct on c.content_type_id = ct.id
where ct.<field> = <value>;

这种表格设计,以及您刚才看到的由此产生的问题,实际上是很常见的。问题的出现主要是由于查找表的选择性非常低(content_type 有 2 行,因此 content_type_id 在内容中的选择性可能是 50%,非常大)。您可以尝试多种解决方案:

1) 在聚集索引上组织content 表,以 content_type_id 作为前导键。这将允许连接进行范围扫描,并避免为投影完整性进行键/书签查找。作为聚集索引的更改,它会对其他查询产生影响,因此必须仔细测试。 content 上的主键显然必须使用非聚集约束来强制执行。

2) 预先读取content_type_id 的值,然后在contentcontent_type 之间没有连接的情况下制定查询:

select ...
from content c
where c.content_type_id = @contentTypeId;

这仅在 content_type_id 的选择性很高(许多不同的值,每个值很少行)时才有效,我怀疑这是您的情况(您可能只有很少的内容类型,每个都有很多条目)。

3) 将 content_Type 非规范化为内容。您提到了非规范化,但是您将内容非规范化为用户的提议对我来说毫无意义。删除 content_type 表,将 content_type 字段拉入 content 表本身,并解决所有非规范化问题。

4) 在物化视图中预加入。你说你已经尝试过了,但我怀疑你尝试了正确的物化视图。您还需要了解,只有企业版自动使用物化视图索引,所有其他版本都需要NOEXPAND 提示:

create view vwContentType 
with schemabinding
as 
select content_type_id, content_id
from dbo.content c
join dbo.content_type_id ct on c.content_type_id = ct.content_type_id;

create unique clustered index cdxContentType on vwContentType (content_type_id, content_id);

select ...
from content c
join vwContentType ct with (noexpand)
on ct.content_id = c.content_id
where ct.content_type_id = @contentTypeId;

解决方案 2)、3) 和 4) 大多是学术性的。鉴于 content_type_id 的选择性非常低,您唯一有机会的解决方案是使其成为 content 聚集索引中的前导键。我没有将分析扩展到 content_Sub_type,但我敢打赌它只有 8 行存在同样的问题,这需要将它也推入聚集索引(可能作为第二个主要键)。

【讨论】:

  • 莱姆斯,感谢您的回复。今天早上我一直在研究选项 1,它看起来很有希望。我的问题是,将 content_group_id 添加到聚集索引中是否也是一个好主意?根据searchsqlserver.techtarget.com/tip/… 本质上,group、type 和 sub_type id 代表一个复合主键——尽管目前它并不明确。我是否正确地假设使用这种方法会导致内容表高度碎片化?
  • 好的,所以我在上面提到的键上应用了聚集索引 - 根据您对选项 1 的初步建议,我们看到处理时间减少了 50% - 你是对的钱。非常感谢莱姆斯!赞成并接受...
  • @Zac:碎片化确实会增加,但是碎片化是可以在必要时通过重组/重建操作来解决的。
  • @Zac:减少 50%,因为全表扫描可能被两种内容类型之一的范围扫描所取代。这仍然很大,当您继续进行时,您会发现半表扫描是不可接受的。但我希望你有这个想法,你和你的团队可以进一步增强它。实验。并在这里阅读所有内容:msdn.microsoft.com/en-us/library/ms190804.aspx
  • 谢谢,感谢 Remus 的帮助。我认为 50% 的减少可能是 sub_type ,因为内容类型大约 95% 倾向于一个值 - 而子类型倾向于 2 个值。但是,是的,我想我现在明白了这个原则。也为链接干杯。
猜你喜欢
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多