【问题标题】:Denormalized access: views or materialized tables?非规范化访问:视图还是物化表?
【发布时间】:2014-10-02 10:13:58
【问题描述】:

我希望创建对数据的非规范化访问,主要用于报告目的(从而避免连接并提高性能)。我有两个解决方案,但我正在寻找(a)其他潜在的解决方案,以及(b)我应该考虑哪些权衡。我正在使用 SQL Server 2008 R2。

在一个解决方案中,我可以在查询上创建一个索引视图,该视图执行我关心的连接。我的理解是,这确实在幕后实现,但很棘手,可能无法保证良好的性能(并且关于视图性能的争论很激烈)。

在另一个解决方案中,我可以构建机制来创建一个表,用我关心的数据填充它,然后在事务中将它换成现有的表。

对我来说,前者似乎很冒险/很神奇;后者看起来很笨拙,容易出错,并且可能会影响查询计划之类的事情。有人可以帮忙解释一下吗?

【问题讨论】:

  • “关于观点表现的争论非常激烈” 在专家中,没有这样的争论。视图被内联到查询计划中。它们对查询性能的贡献为零。初学者经常发现性能问题,随机决定一定是因为有视图。
  • 您到底想解决什么问题?为什么你认为你需要一个视图?
  • @Andrew,问题的第一行陈述了目标:“我希望创建对数据的非规范化访问,主要用于报告目的(从而避免连接并获得性能)。”他不想加入一堆表并即时进行计算,而是希望保留最终结果以提高性能。索引视图是一种持久性形式。
  • 那么您是否尝试过使用连接的查询?数据库经过优化以使用连接,而非规范化实际上会降低性能。

标签: sql-server database-performance


【解决方案1】:

首先,索引视图有两个陷阱:

1) 基表中的更新行必须能够传播到索引视图,而不必引用基表中的任何其他行。这就是你可以使用 SUM() 和 COUNT_BIG() 的原因,因为这些聚合可以通过知道更改的行中的内容来更新,但你不能使用 MIN() 或 MAX(),因为你必须回顾一下基表以确保。

对索引视图的所有看似任意的限制(没有 TVF、没有子查询、没有联合等)归结为上述原因。引擎必须能够在不经常查看整个基表的情况下维护索引。

2) 基表上的 AFTER 触发器仍会在索引视图更新之前得到处理。

这些限制了您仅使用索引视图可以完成的任务的复杂性。


其次,测试非规范化是否真的有用。如果你只是实例化简单的连接并且你已经是 I/O 绑定的,那会使瓶颈变得更糟。另一方面,如果您正在预先计算大型聚合或获取极宽连接的垂直切片,则更有可能是一种改进。


第三,要使用索引视图,请使用如下模式:

CREATE TABLE huge_data_table ( ... )
GO

CREATE VIEW huge_data_table_monthly_summary_index AS
SELECT
  YEAR(...) AS [year_...]
 ,MONTH(...) AS [month_...]
 ,SUM(...) AS [sum_...]
 ,COUNT_BIG(*) AS [count_...]
FROM huge_data_table
GROUP BY YEAR(...),MONTH(...)
GO

CREATE UNIQUE CLUSTERED INDEX UC__xyz
ON huge_data_table_monthly_summary_index
  ([year_...],[month_...])
GO

CREATE VIEW monthly_summary AS
SELECT
  [year_...]
 ,[month_...]
 ,[sum_...]
 ,[count_...]
FROM huge_data_table_monthly_summary_index WITH (NOEXPAND) --force use of the view's index
GO

然后您可以查询monthly_summary 并获取聚合,而无需每次都重新计算它们。每当huge_data_table 更改时,引擎都会自动更新huge_data_table_monthly_summary_index


这可能看起来很神奇,但它确实有效。尽可能使用它来代替触发器/作业/等来同步表。我发现我通常可以将复杂的报告工作分解成更小、更简单的部分,这些部分可以使用索引视图,并且在报告时即时加入中间结果证明速度足以完成工作。

如果这还不足以满足您的需求,您可能处于将日志传送或镜像到单独的报告服务器的领域。手动同步的意义不大。

【讨论】:

    【解决方案2】:

    在优化管道的早期阶段将视图内联到查询计划中。它们既不会伤害也不会提高性能。

    索引视图也是内联的。您是否编写了查询以引用视图或是否粘贴了视图定义都没有关系。在 Enterprise Edition 上,优化器会尝试将部分查询与管道中稍后的索引视图进行匹配。这确实是不可靠的。对于简单的情况,它可以正常工作,但我看到它随机失败。没有保证。

    有一个解决方案:WITH (NOINDEX) 强制优化器不内联视图,而是使用它的索引。这是 100% 可靠的。

    如果您可以将查询模式放入索引视图并能够使用该提示(您可以创建始终包含该提示的包装视图),那么索引视图是一个很好的自动且一致的解决方案。

    对我来说,前者似乎很冒险/很神奇

    有了这个提示,索引视图就没有什么惊喜了。如果有帮助:我有索引视图的生产经验。它们工作正常。

    后者看起来很笨拙,错误 容易,并且可能会影响查询计划之类的事情。有人可以帮忙吗 请解释一下?

    确实如此。每个错误都有可能导致数据损坏。您最好不要忘记写入数据的任何地方,否则您的非规范化数据会变得不一致。


    如果没有充分的理由反对使用索引视图,出于这些原因,我建议您使用它们。

    【讨论】:

    • 我会很感激关于否决票的评论。围绕观点有许多神话。也许投反对票是由于一个这样的神话?!
    • -1 答案与问题无关。 OP 不是在询问关于视图的神话,而是在询问使用非内联索引视图与使用非规范化表的优缺点。
    • @Anon 我明白你的意思。我已经编辑了答案。不过,在我推荐一些东西之前,我必须消除这些神话。
    • 我已经看到视图会损害性能,但只有当有人愚蠢到使用视图调用视图来调用其他视图等时。加入同一个表 7 次而不是一次确实会导致性能问题。
    • @HLGEM 由于使用视图,这不是问题。如果那个人在内联编写了相同的查询,他就会遇到同样的问题。视图可能会导致混乱,但从技术上讲,它们不会导致性能问题。
    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 2015-01-22
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多