规范化一个非常大的表

Posted

技术标签:

【中文标题】规范化一个非常大的表【英文标题】:Normalizing an extremely big table 【发布时间】:2013-01-23 09:54:19 【问题描述】:

我面临以下问题。我有一张非常大的桌子。这张桌子是以前参与该项目的人的遗产。该表位于 MS SQL Server 中。

该表具有以下属性:

    它有大约 300 列。它们都有“文本”类型,但其中一些最终应该代表其他类型(例如,整数或日期时间)。因此,在使用这些文本值之前,必须将它们转换为适当的类型 该表有超过 100 百万行。表的空间很快就会达到 1 TB 该表没有任何索引 该表没有任何已实现的分区机制。

正如您可能猜到的那样,不可能对该表运行任何合理的查询。现在人们只在表中插入新记录,但没有人使用它。所以我需要重组它。我计划创建一个新结构并用旧表中的数据重新填充新结构。显然,我将实现分区,但这不是唯一要做的事情。

表格的一个最重要的特性是那些纯文本的字段(即它们不必转换为另一种类型)通常具有频繁重复的值。因此,给定列中值的实际变化范围是 5-30 个不同的值。这引发了进行规范化的想法:对于每个这样的文本列,我将创建一个附加表,其中包含可能出现在该列中的所有不同值的列表,然后我将在这个附加表中创建一个 (tinyint) 主键和然后将在原始表中使用适当的外键,而不是将这些文本值保留在原始表中。然后我会在这个外键列上放一个索引。这样处理的列数约为100。

它提出了以下问题:

    这种规范化真的会提高对这 100 个字段中的一些字段施加条件的速度吗?如果我们忘记保留这些列所需的大小,是否会由于使用 tinyint-columns 替换初始文本列而提高性能?如果我不进行任何规范化并简单地在这些初始文本列上放置一个索引,那么性能是否会与计划的 tinyint-column 上的索引相同? 如果我执行所描述的规范化,那么构建一个显示文本值的视图将需要将我的主表与大约 100 个附加表连接起来。一个积极的时刻是我将为“主键”=“外键”对进行这些连接。但是仍然应该加入相当多的表。这里有一个问题:对这个视图进行的查询的性能与对初始非规范化表的查询的性能相比是否会更差? SQL Server 优化器是否真的能够以允许利用规范化优势的方式优化查询?

对不起,这么长的文字。

感谢您的每一条评论!

PS 我创建了一个关于加入 100 个表的相关问题; Joining 100 tables

【问题讨论】:

你需要几加仑的咖啡来解决这个问题。 您应该问的第一个问题是“表有主键吗?”。第二个“如果不是,哪一列(组合)可以用作主键?”。然后,您可以继续查找依赖项并进行规范化。查找表的想法很有价值。 你提到人们不再查询表,所以你想解决任何问题吗?我的意思是,由于空间的原因,目前是否难以备份数据库,或者您确实需要从该表中报告,等等?请告诉我们您希望如何使用此表以及您的重构目标。 @iCoffee - 在您执行此类任务之前确认信息对您的用户有价值 - IMO。 "这引发了进行规范化的想法:对于每个这样的文本列,我将创建一个附加表,其中包含可能出现在该列中的所有不同值的列表,然后我将创建一个(tinyint)主键...” 您似乎认为规范化和代理键的使用是一回事。他们不是。规范化意味着您可以通过基于函数、多值和连接依赖关系投影新表来有效地减少起始表中的列数。 【参考方案1】:
    在头脑中和纸上构建一个规范化的数据库结构 构建数据库(带索引) 解构那个单体。事情看起来不会那么糟糕。我猜是重复了很多(我的意思是很多)数据 创建 SQL 插入语句以将数据插入数据库中 首先用霰弹枪去找那些制造噩梦的人。玩得开心。

【讨论】:

【参考方案2】:

除了针对数据运行的查询速度之外,您还会发现规范化数据的其他好处...例如大小和可维护性,仅此一项就可以证明对其进行规范化...

但是,它也可能会提高查询速度;目前单行包含 300 个文本列是巨大的,几乎可以肯定超过 8,060 byte limit for storing the row data page... 而是存储在 ROW_OVERFLOW_DATALOB_DATA 分配单元中。

通过规范化减少每行的大小,例如用TINYINT 外键替换冗余文本数据,以及将不依赖于该大表主键的列删除到另一个表中,数据应该不再溢出,您还可以在每页存储更多行。

至于通过执行JOIN 来获取规范化数据所增加的开销...如果您正确索引表,这不应该增加大量开销。但是,如果它确实增加了不可接受的开销,那么您可以根据需要选择性地对数据进行反规范化。

【讨论】:

迈克尔,感谢您的 cmets!这 8KB 是实际发生的点。【参考方案3】:

这是否值得努力取决于价值观的持续时间。如果值是州缩写(2 个字符)或国家代码(3 个字符),则生成的表将比现有表更大。请记住,您需要包含引用表的主键。这通常是一个整数并占用四个字节。

这样做还有其他充分的理由。拥有具有有效值列表的引用表可以保持数据库的一致性。参考表既可用于验证输入,也可用于报告目的。可以包含其他信息,例如“长名称”或类似的东西。

此外,SQL Server 会将 varchar 列溢出到其他页面上。它不会溢出其他类型。您只有 300 列,但最终您的记录数据可能会接近单页数据的 8k 限制。

而且,如果您决定继续,我建议您在列中查找“主题”。可能存在可以组合在一起的多组列。 . .详细的停止代码和停止类别、简短的企业名称和完整的企业名称。您正在走上对数据建模的道路(一件好事)。但要谨慎处理非常低级别的事情(管理 100 个参考表)而不是识别一组合理的实体和关系。

【讨论】:

戈登,非常感谢您发表意见!根据其含义对列进行分组这一点确实非常有帮助。【参考方案4】:

1) 系统当前必须对大量数据进行全表扫描,从而导致性能问题。优化的许多方面可以提高这种性能。将列转换为正确的数据类型不仅可以通过减小每条记录的大小来显着提高性能,而且可以使数据正确。如果查询列,您当前正在查看与字段中的文本进行比较的文本。仅使用索引可以改善这一点,但更改为查找将允许从足够小的表中查找 ID 值以保存在内存中,然后使用它来仅扫描整数值,这是一个更快的过程。 2) 如果数据被规范化为第三范式等,那么您可以看到以数据完整性为名的性能受损的实例。如果引擎在不首先投影数据的情况下无法计算出如何限制行,这将是一个最大的问题。但是,如果确实发生了这种情况,则执行计划可以识别出这种情况,并且可以修改查询以降低发生这种情况的可能性。

还有一点需要注意的是,听起来如果数据库结构合理,它可能能够缓存在内存中,因为数据量会大大减少。如果是这样,那么性能会大大提高。

提高性能的快速方法可能是添加索引。但是,这会进一步增加整个数据库的大小,并且不能解决存储重复数据的问题和可能的数据完整性问题。

还可以进行一些其他更改 - 如果并非总是需要大量数据,则可以将其分离到相关表中,并仅在需要时进行查找。不用于查找其他表的字段特别适用于此,因为连接可以在一个小得多的表上,同时保留一个相当简单的结构,当您确定您实际的数据时只查找附加数据需要。这显然不是一个正确规范化的结构,但可能是提高性能的一种快速而肮脏的方式(在添加索引之后)。

【讨论】:

大卫,您关于更仔细查看缓存过程的观点非常有希望。感谢您分享您的想法!

以上是关于规范化一个非常大的表的主要内容,如果未能解决你的问题,请参考以下文章

用嵌套列表和嵌套字典列表展平一个非常大的 Json

在 php 中解析非常大的 XML 文件

数据规范化和编写查询

我应该创建一个 Factless 事实表还是非规范化我的表

J2EE规范标准

在 Rails 中简化宽的、非规范化的表