【问题标题】:Database Design: Partitioned Table vs Normalized Table数据库设计:分区表与规范化表
【发布时间】:2017-07-04 04:56:24
【问题描述】:

我有两个表:tblIssuetblIssueSubscriber 用于我的时事通讯应用程序。

这是我的标准化设计:

 tblIssues (newsletter issues masterlist)
 --------------------
 IssueId int PK
 PublisherCode varchar(10)
 IssueDesc varchar(50)


 tblIssueSubscribers (newsletter subscribers)
 -----------------
 IssueId int FK
 EmailAddress varchar(100)

但是 tblIssueSubscriber 预计每周会保存数十万甚至数百万条记录,并且会经常访问它,这就是我倾向于表分区的原因。我的设计是根据 PublisherCode 对 tblIssueSubscriber 进行分区(我们的主列表中有 8 个 publisherCode)。

 tblIssues
 --------------
 IssueId int PK
 PublisherCode varchar(10)
 IssueDesc varchar(50)


 tblIssueSubscribers
 -----------------
 IssueId int FK
 PublisherCode varchar(10)
 EmailAddress varchar(100)

然后根据 PublisherCode 对其进行分区

 CREATE PARTITION FUNCTION [PartitionPublisher] (varchar(10)) AS RANGE RIGHT FOR VALUES  ('PUBLISHER1', 'PUBLISHER2', 'PUBLISHER3', 'PUBLISHER4', 'PUBLISHER5', 'PUBLISHER6', 'PUBLISHER7', 'PUBLISHER8');

我知道表分区会增加复杂性,所以我的问题是,

是否值得对 tblIssueSubscriber 进行分区,还是我应该坚持 标准化设计?

【问题讨论】:

  • 订阅者会收到多个问题吗?如果是这样,您可能希望将订阅者数据标准化为 tblSubscribers 并避免重复电子邮件地址。那么 tblIssueSubscribers 将只是两个表之间的连接表。
  • 是的,我们只是通过 Excel 文件下载订阅者(电子邮件)列表(1 个问题 = 1 个 Excel 文件 = 订阅者列表)并将其上传到 db,这样我就无法避免重复添加到数据库中的电子邮件因为我们不维护订阅者的电子邮件地址...
  • @Andrew,你能解释一下为什么他应该在这个问题上正常化,还是只是一个典型的下意识反应,认为 3NF 是上帝?
  • OP 没有提及来自 Excel 等平面源的订阅者数据。如果订阅者和订阅列表由他的应用程序管理,那么对其进行规范化很可能是有意义的。为什么不需要复制数百万个电子邮件地址?保存加入?如果没有可用的订户信息主列表,那么显然规范化不是一种选择。

标签: sql database-design


【解决方案1】:

首先,我认为尺寸是红鲱鱼。这不是一个非常有用的论点,因为所有大小都是相对的,并且有理由使用分区而不考虑大小。

性能只是部分原因。 Ronnis 提出了一些很好的观点,但并不止于此。

对表进行分区有两个原因。一是性能,一是维护。

让我们从维护开始。

一般来说,在数据库中删除是一件“坏事”。假设您错误地插入了 100 万行,然后又删除了 100 万行。这些删除中的每一个都会被记录下来,生成 UNDO 和 REDO 记录,这不仅会浪费空间,而且需要时间,不仅在删除时进行,而且在“播放”时再次进行时间点恢复。那么有什么比删除更好的呢?截断(或删除)。当您描述的表格不断增长时,您有时希望摆脱旧记录。这就是为什么我说大小无关紧要——如果你想在那个表中保留一年,你需要删除超过 12 个月的记录——不管那个大小是什么。在添加记录 1 年后,您可能拥有 300MB 或 500GB 的表 - 无论您需要/想要开始删除。因此,您始终可以删除 insert_dt

还有其他维护好处,例如单独备份分区或重建索引或移动到新表空间等。不确定您使用的是什么 RDBMS,但大多数情况下您可以通过分区交换加载数据。这使您可以在所有数据都加载完毕并准备就绪之前不对最终表格进行任何更改。

就性能而言......

这里的关键是,任何在 where 子句中不包含分区键的查询很可能会比分区之前执行得更差。这不是 GO_FASTER = TRUE 类型的设置。我见过人们实施分区并粉碎他们的系统。 Ronnis 的帖子是单个分区表中性能指南的基础知识。如果你有多个表在同一个键上分区,一些 RDBMS 可以并行化它们之间的连接。

【讨论】:

  • 我只做一个年度分区表(tblIssueSubscriber2011,tblIssueSubscriber2012...)怎么样?并且只使用视图来加入年表。谢谢
  • 这是 SQL Server 在 2000 年使用的方式。他们转向真正的分区的原因是它更好。 (它更好的很多原因)。那么为什么要在技术上倒退呢?
【解决方案2】:

查询模式将决定您是否会从分区中受益。

如果您的应用程序主要是关于单行查询(通常是主键或索引访问),您不会看到分区表带来的性能提升。

如果您的应用程序主要处理所有数据发布者,那么您将受益于通过在执行表扫描时消除表的较大部分来进行分区。

【讨论】:

  • 该表的功能是检索订阅者当天的问题,所以我会说它的单行访问.. 但频率是,我们可以有成千上万的订阅者同时访问它。
【解决方案3】:

这实际上取决于该数据库文件将变得有多大,您将在其中拥有多少记录以及您使用的是什么机器。粗略计算一下你认为它会变成多大。

大概,假设数据库文件将增长到 300 MB?

这没什么...我个人不会对其进行分区。我知道我们的一些数据库客户使用分区,当他们期望数据库增长到超过 500 GB 并且最终可能达到 4 TB 时,他们开始分区。在这种情况下,是分区。但我怀疑你不会去那附近的任何地方。

另外,你以后可以随时分区,不是吗?

我会推荐一台 64 位机器,运行 Linux 或 Windows server 2008/Win7。更多的内存总是好的。

【讨论】:

  • 正如我所说,该表每天可以有 100 万条记录,所以在 1 年内它可以有 3.65 亿条记录,但我不知道磁盘空间有多大(MB) ,但我认为它肯定会在 1 年内超过 300mb...