【问题标题】:Please help explain if I'm destroying my DB Schema for the sake of performance :(请帮助解释我是否为了性能而破坏我的数据库架构:(
【发布时间】:2010-11-09 05:39:20
【问题描述】:

我已经在 Sql 2008(之前是 05 年)上生产了近 3 年的数据库。一直很好,但性能不是很好。所以我正在调整架构和查询以帮助加快一些事情。此外,一些主表包含大约 1-3 行,每个表(给你一个大小的估计)。

这是一个示例数据库图(Soz,在 NDA 下,所以我无法显示原件):-

alt text http://img11.imageshack.us/img11/4608/dbschemaexample.png

注意事项(与我的问题直接相关):-

  • 车辆可以有 0 个(NULL)或 1 个无线电。 (左外连接)
  • 车辆可以有 0 个(NULL)或 1 个杯托(左外连接)
  • 车辆有 1 种轮胎类型(内连接)。

首先,这看起来像normalised database schema。我很烂和 DB 理论,所以我猜这是 3NF(至少)...著名的遗言 :)

现在,这正在扼杀我的数据库性能,因为这两个外连接和内连接被调用了很多,而且在许多语句中还有一些连接。

为了尝试解决这个问题,我想我可以尝试索引视图。创建视图是小菜一碟。但是索引它不起作用->无法使用连接或自引用表创建索引视图(也是另一个问题:()。

所以,我已经哭了好几个小时(以及 /wristsdyed hairwrote an emo song 关于它并把它放在 myfailspace 上)并做了以下......

  1. 在每个“可选”外部连接表(在本例中为 Radios 和 CupHolders)中添加了一个新行。 ID = 0,其余数据 = 'Unknown Blah' 或 0。
  2. 更新父表,使任何 NULL 数据现在都有一个 0。
  3. 更新从外连接到内连接的关系。

现在,这行得通。我什至可以制作索引视图,现在速度非常快。

所以……我很痛苦。这与我所学的一切背道而驰。我觉得脏。独自的。被感染了。

这是一件坏事吗?这是为了性能而对数据库进行非规范化的常见场景吗?

我很想对此有一些想法,请:)

PS。谷歌随机找到的那些图片——所以不是我。

【问题讨论】:

  • 你是说内连接比外连接快吗?不应该——你能举个例子来说明什么是慢的吗?
  • 顺便说一句:您并没有影响您的规范化 - 关于在表格中使用 null 存在很多争论,有些人会将您的更改视为一种改进。
  • 您现有的 Vehicles 表上有哪些索引。一个运行缓慢的查询示例也很有用。
  • @TonyLee - 我通常发现内部联接总是比外部联接快。 ?? @EvilRyry - 我会在 PK 和每个 FK 上有索引,有各种排列。
  • 一般来说,outer 会返回更多的行(那些为空的),但在你的情况下,我猜你已经以其他方式修改了 select 以改进性能。

标签: sql-server database-design normalization denormalization


【解决方案1】:

“...所以我正在调整架构和查询以帮助加快某些事情...” - 我会对此有所不同。看来你正在放慢速度。 (开个玩笑。)

我喜欢数据库程序员博客。他有两列支持和反对规范化,您可能会发现它们很有帮助:

  1. http://database-programmer.blogspot.com/2008/10/argument-for-normalization.html
  2. http://database-programmer.blogspot.com/2008/10/argument-for-denormalization.html

我不是 DBA,但我认为证据就在您的眼前:性能更差。我看不出将这些 1:1 关系拆分为单独的表格有什么好处,但我很乐意接受指导。

在更改任何内容之前,我会要求 SQL Server 对每个缓慢的查询进行解释计划,并使用该信息来查看应该更改的确切内容。不要猜测,因为规范化大师告诉过你。获取数据以备份您正在做的事情。您正在做的事情听起来像是在没有分析的情况下优化中间层代码。直觉不是很准确。

【讨论】:

  • 我几乎每次在 SSMS 中运行查询时都使用可视化的查询计划。通过两个表之间的连接,我将获得 200 万行加入另一个大量数据。我在父表和子表上也有很多索引。只是需要将两个表连接起来才能得到结果。另外,我没有拆分表,只是更改了关系和空值。当我运行索引视图时,它们的速度更快。
【解决方案2】:

空值通常不用于索引。您所做的是提供一个标记值,以便该列始终具有一个值,从而可以更有效地使用您的索引。

您也没有更改数据库的结构,因此我不会将其称为非规范化。我已经用日期值做到了这一点,其中您有一个“结束日期”空值表示尚未结束。取而代之的是,我将其设置为将来允许索引的已知日期方式。

我认为这很好。

【讨论】:

  • 感谢您的评论。您是 100% 正确的,我只是从空值更改为哨兵值(以前从未听说过这样使用的词)。我觉得很脏,我看到有多少其他人这样做-> 你这样做。所以我并不孤单,看来:) 另外,我认为 NULLS 不会影响索引.. ??
  • null 没有被索引,因为 null != null 所以甚至不需要查看索引,它比使用标记值更快。
【解决方案3】:

数据库应该始终在 3NF 中设计和初始实施。但是世界是一个现实的地方,而不是理想的地方,出于性能原因,可以恢复到 2NF(甚至 1NF)。不要为此自责,现实世界中实用主义总是胜过教条主义。

如果您的解决方案提高了性能,那么它就是一个很好的解决方案。拥有一台真正的收音机(例如),由没有人制造并且没有任何功能的想法并不坏 - 以前已经做过很多很多,相信我 :-) 你会这样做的唯一原因将该字段用作 NULL 是为了查看哪些车辆没有无线电,这些查询之间几乎没有区别:

select Registration from vehicles where RadioId is null
select Registration from vehicles where RadioId = 0

我的第一个想法是简单地将四个表合并为一个并挂起重复数据问题。 DBMS 的大多数问题源于性能不佳,而不是存储空间不足。

如果您当前的非规范化架构也变得缓慢,也许可以将其作为您的后备位置。

【讨论】:

  • 通过创建索引视图,我必须将所有 4 个表合并为一个,同时保持对它的规范化(在我可悲的 db 理论中)理解。要进行索引视图,我必须将它们全部设为内部连接,因此使用零 Id 而不是 NULL。所以你是说这仍然是一种可以接受的做法,即使不是很普遍?
  • 这是可以接受的并且比你想象的更普遍。数据库不是设计出来的,然后就不管了,它们会根据其中的数据不断调整。例如,我们收集统计数据,如果需要,每六个月重新对我们的模式进行基线分析(尽管由于数据类型已经稳定,我们不得不改变任何东西已经有几年了)以保持性能高。
  • 使用“特殊”值(在我的例子中,Id=0)而不是 null 来用内部联接替换外部联接是可以接受的做法?
  • 是的,只要它被记录在某处,以及为什么,你这样做了。这应该在数据库的设计文档中,我会在 id=0 的 nvarchar(50) 字段中添加一个简短的注释,以防设计文档消失:-)。
【解决方案4】:

我遇到了同样的表现与学术卓越的问题。我们有一个包含 300 列和 91000 条记录的客户数据库的大视图。我们使用外部连接来创建视图,性能很差。我们已经考虑通过在我们加入的列(而不是 null)上放入值为 0 的虚拟记录来更改内部连接,以在视图上启用唯一索引。

我必须同意,如果性能很重要,有时必须做一些奇怪的事情来实现它。最终,支付我们账单的人并不关心架构是否完美。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 2010-11-29
    • 1970-01-01
    相关资源
    最近更新 更多