规范化导致许多基本和理想的特征,包括审美愉悦。此外,它在理论上也是“正确的”。在这种情况下,非规范化被用作一种折衷方案,一种实现性能的校正。除了性能之外,还有什么原因可以使数据库非规范化?
14 回答
非规范化的两个最常见原因是:
- 表现
- 无知
前者应通过剖析进行验证,而后者应使用卷起的报纸进行更正;-)
我想说一个更好的口头禅是“为了正确而规范化,为了速度而去规范化——而且只有在必要时”
要完全理解原始问题的重要性,您必须了解系统开发中的团队动态,以及不同角色/类型的人倾向于采取的行为(或不当行为)类型。规范化很重要,因为它不仅仅是对设计模式的冷静辩论——它还与随着时间的推移如何设计和管理系统有很大关系。
数据库人员受过培训,认为数据完整性是最重要的问题。我们喜欢考虑数据 100% 的正确性,这样一旦数据在数据库中,您就不必考虑或处理它是否存在逻辑错误。这种思想流派高度重视规范化,因为它会导致(迫使)团队掌握数据和系统的底层逻辑。举一个简单的例子——客户只有一个姓名和地址,还是可以有多个?需要有人做出决定,系统将依赖于一致应用的规则。这听起来像是一个简单的问题,但是当你设计一个相当复杂的系统时,将这个问题乘以 500 倍,你就会看到问题——规则不能仅仅存在于纸上,它们必须得到执行。一个良好规范化的数据库设计(在唯一性约束、外键、检查值、逻辑执行触发器等的额外帮助下)可以帮助您拥有定义良好的核心数据模型和数据正确性规则,这非常重要,如果当许多人在系统的不同部分(不同的应用程序、报告等)上工作并且随着时间的推移不同的人在系统上工作时,您希望系统按预期工作。或者换一种说法——如果你没有某种方法来定义和操作执行一个可靠的核心数据模型,你的系统就会很糟糕。当许多人在系统的不同部分(不同的应用程序、报告等)工作并且随着时间的推移不同的人在系统上工作时,如果您希望系统按预期工作,这一点非常重要。或者换一种说法——如果你没有某种方法来定义和操作执行一个可靠的核心数据模型,你的系统就会很糟糕。当许多人在系统的不同部分(不同的应用程序、报告等)工作并且随着时间的推移不同的人在系统上工作时,如果您希望系统按预期工作,这一点非常重要。或者换一种说法——如果你没有某种方法来定义和操作执行一个可靠的核心数据模型,你的系统就会很糟糕。
其他人(通常是经验较少的开发人员)不这么看。他们认为数据库充其量是一个受制于他们正在开发的应用程序的工具,或者在最坏的情况下是一个需要避免的官僚机构。(请注意,我说的是“经验不足”的开发人员。优秀的开发人员与数据库人员一样意识到对可靠数据模型和数据正确性的需求。他们可能会在实现这一目标的最佳方法上有所不同,但是以我的经验,只要数据库团队知道他们在做什么并且可以对开发人员做出响应,就可以合理地在数据库层中做这些事情)。这些经验不足的人通常是推动非规范化的人,这或多或少是为了设计和管理数据模型而进行快速而肮脏的工作的借口。这就是您最终获得具有应用程序屏幕和报告的 1:1 数据库表的方式,每个表都反映了不同开发人员的设计假设,并且表之间完全缺乏完整性/连贯性。在我的职业生涯中,我曾多次经历过这种情况。开发系统是一种令人沮丧且效率极低的方式。
因此,人们对正常化有强烈感觉的一个原因是,这个问题是他们强烈关注的其他问题的替代品。如果你被卷入关于正常化的辩论,请考虑各方可能带来辩论的潜在(非技术)动机。
话虽如此,这是对原始问题的更直接答案:)
将您的数据库视为由尽可能接近逻辑设计的核心设计(高度规范化和约束)和解决其他问题(如稳定的应用程序接口和性能)的扩展设计组成是很有用的。
您应该希望约束和规范化您的核心数据模型,因为不这样做会损害数据的基本完整性以及系统正在构建的所有规则/假设。如果你让这些问题远离你,你的系统很快就会变得很糟糕。根据需求和真实数据测试您的核心数据模型,然后疯狂地迭代直到它工作。这一步感觉更像是澄清需求而不是构建解决方案,它应该这样做。使用核心数据模型作为强制功能,为所有相关人员获得有关这些设计问题的明确答案。
在继续扩展数据模型之前完成您的核心数据模型。使用它,看看你能用它走多远。根据数据量、用户数量和使用模式,您可能永远不需要扩展数据模型。看看您可以通过索引以及可以在 DBMS 中打开的 1,001 个与性能相关的旋钮获得多远。
如果您真正利用 DBMS 的性能管理功能,那么您需要考虑以增加非规范化的方式扩展您的数据模型。请注意,这不是非规范化您的核心数据模型,而是添加处理 denorm 数据的新资源。例如,如果有一些大型查询会破坏您的性能,您可能需要添加一些表来预先计算这些查询将产生的数据——本质上是预先执行查询。以保持非规范化数据与核心(规范化)数据的一致性的方式执行此操作非常重要。例如,在支持它们的 DBMS 中,您可以使用 MATERIALIZED VIEW 来自动维护 denorm 数据。如果您的 DBMS 没有此选项,
以一种连贯的方式选择性地对数据库进行非规范化以应对现实的性能挑战与仅仅使用弱数据设计并将性能作为其理由之间存在天壤之别。
当我与中低经验的数据库人员和开发人员一起工作时,我坚持他们会产生一个绝对规范化的设计……然后可能会涉及少数更有经验的人来讨论选择性非规范化。非规范化在您的核心数据模型中或多或少总是不好的。在核心之外,如果你以一种经过深思熟虑和连贯的方式进行非规范化,那么它就没有任何问题。
换句话说,从一个正常的设计去规范化到一个在保留正常的同时添加一些非规范的设计——在处理数据的物理现实的同时保留它的基本逻辑——是好的。没有正常设计核心的设计——甚至不应该被称为非规范化,因为它们从一开始就没有被规范化,因为它们从来没有有意识地以一种有规律的方式设计——是不好的。
不要接受这样的术语,即弱、无纪律的设计是“非规范化”设计。我相信故意/仔细非规范化数据与导致非规范化数据的普通旧糟糕数据库设计之间的混淆是许多关于非规范化辩论的根本原因。
非规范化通常意味着检索效率的一些改进(否则,为什么要这样做),但是在修改(插入、更新,有时甚至是删除)操作期间验证数据的复杂性方面付出了巨大的代价。大多数情况下,额外的复杂性被忽略(因为它太难描述了),导致数据库中的虚假数据,通常直到后来才被发现 - 例如当有人试图找出公司破产的原因和事实证明,数据是自不一致的,因为它是非规范化的。
我认为口头禅应该是“为了正确而规范化,只有当高级管理层愿意将你的工作交给别人时才去规范化”,此时你应该接受去新的牧场的机会,因为目前的工作可能不会持续下去,只要你会的。
或者“仅当管理层向您发送一封电子邮件,为您将造成的混乱无罪时才进行非规范化”。
当然,这假设您对自己的能力和对公司的价值充满信心。
咒语几乎总是过分简化其主题。这是一个很好的例子。
规范化的优点不仅仅是理论上的或美学的。对于 2NF 及以后的每一次偏离正常形式,当您不遵循正常形式时会发生更新异常,而当您遵循正常形式时会消失。从 1NF 出发是一个完全不同的蠕虫罐头,我不打算在这里处理它。
这些更新异常通常属于插入新数据、更新现有数据和删除行。您通常可以通过巧妙、棘手的编程来解决这些异常情况。那么问题是使用聪明、棘手的编程的好处是值得的。有时代价是错误。有时代价是失去适应性。有时成本实际上是,不管你信不信,糟糕的表现。
如果你学习了各种范式,你应该认为你的学习是不完整的,直到你理解了伴随的更新异常。
将“非规范化”作为指导方针的问题在于它没有告诉您该做什么。有无数种方法可以对数据库进行非规范化。他们中的大多数人都很不幸,这是慈善的。最愚蠢的方法之一是每次你想加速某些特定查询时,一次简单地反规范化一个步骤。如果不了解应用程序的历史,您最终会得到一个无法理解的疯狂的杂烩。
许多“当时似乎是个好主意”的非规范化步骤后来被证明是非常糟糕的举动。
当您决定不完全规范化时,这是一个更好的选择:采用一些产生某些好处的设计规范,即使该设计规范背离了完全规范化。例如,星型模式设计,广泛用于数据仓库和数据集市。这是一种更加连贯和有纪律的方法,而不是仅仅通过奇思妙想进行非规范化。您将从星型模式设计中获得特定的好处,您可以将它们与您将遭受的更新异常进行对比,因为星型模式设计与规范化设计相矛盾。
通常,许多设计星型模式的人都在构建辅助数据库,它不与 OLTP 应用程序交互。保持这种数据库最新的最困难的问题之一是所谓的 ETL(提取、转换和加载)处理。好消息是,所有这些处理都可以集中在少数几个程序中,处理规范化 OLTP 数据库的应用程序程序员不必学习这些东西。有一些工具可以帮助 ETL,将数据从规范化的 OLTP 数据库复制到星型模式数据集市或仓库是一个很好理解的案例。
一旦您构建了星型模式,如果您选择了维度,明智地命名了列,特别是选择了粒度,那么将此星型模式与 Cognos 或 Business Objects 等 OLAP 工具一起使用几乎就像玩游戏一样简单一个视频游戏。这使您的数据分析师可以专注于分析数据,而不是了解数据容器的工作方式。
除了星型模式之外,还有其他一些偏离规范化的设计,但星型模式值得特别一提。
Don't forget that each time you denormalize part of your database, your capacity to further adapt it decreases, as risks of bugs in code increases, making the whole system less and less sustainable.
Good luck!
维度模型中的数据仓库通常以(非规范化)星型模式建模。这些类型的模式(通常)不用于在线生产或事务系统。
根本原因是性能,但事实/维度模型还允许一些时间特征,例如在传统 ER 样式模型中可行的缓慢变化的维度,但可能非常复杂和缓慢(生效日期、存档表、活动记录, ETC)。
规范化与性能无关。我真的不能说它比 Erwin Smout 在这个线程中所做的更好: 规范化数据库对资源的影响是什么?
大多数 SQL DBMS 在不损害逻辑模型的情况下对更改数据的物理表示的支持有限,所以不幸的是,这就是为什么您可能会发现有必要进行道德化的原因之一。另一个是许多 DBMS 对多表完整性约束没有很好的支持,因此作为实现这些约束的解决方法,您可能被迫将无关属性放入某些表中。
数据库规范化不仅仅是为了理论上的正确性,它还可以帮助防止数据损坏。我当然不会像@aSkywalker 建议的那样因为“简单”而去规范化。修复和清理损坏的数据绝非易事。
您本身并没有标准化“正确性”。事情是这样的:
非规范化表具有提高性能的好处,但需要冗余和更多的开发人员脑力。
规范化表具有减少冗余和增加开发便利性的好处,但需要性能。
这几乎就像一个经典的平衡方程。因此,根据您的需求(例如有多少正在敲击您的数据库服务器),除非确实需要,否则您应该坚持使用规范化表。然而,从规范化到非规范化的开发比反规范化更容易且成本更低。
没门。请记住,您应该规范化的是您的关系(逻辑级别),而不是您的表格(物理级别)。
非规范化数据更常见于未进行足够规范化的地方。
我的口头禅是“为了正确而规范化,为了表现而消除”。RDBM 是非常灵活的工具,但针对 OLTP 情况进行了优化。用更简单的东西(例如内存中的对象和事务日志)替换 RDBMS 会很有帮助。
我不同意这里人们的断言,即规范化数据库总是与更简单、更清洁、更健壮的代码相关联。当然,在很多情况下,完全规范化的代码比部分非规范化的代码更简单,但充其量这是一个指导方针,而不是物理定律。
有人曾经将一个词定义为一个活生生的想法的皮肤。在 CS 中,您可以说对象或表格是根据问题的需求和现有基础设施来定义的,而不是理想对象的柏拉图式反映。从理论上讲,理论和实践之间没有区别,但在实践中,你确实会发现理论的差异。这句话对 CS 来说特别有趣,因为该领域的重点之一是发现这些差异并以尽可能最好的方式处理它们。
从数据库方面休息一下,看看事情的编码方面,面向对象编程通过将许多密切相关的代码组合在一个对象类名称下,使我们摆脱了意大利面条编码的许多弊端它具有易于记忆的英文含义,并且以某种方式适合与其相关联的所有代码。如果太多信息聚集在一起,那么每个对象最终都会变得非常复杂,这让人想起意大利面条代码。如果你把集群做得很小,那么你就不能在不搜索大量对象的情况下遵循逻辑线程,每个对象中的信息很少,这被称为“通心粉代码”。
如果您考虑在事物的编程方面的理想对象大小与标准化数据库所产生的对象大小之间的权衡,我会向那些说通常基于数据库选择更好的人表示赞同然后在代码中解决该选择。特别是因为在某些情况下,您有能力通过使用 hibernate 和类似技术的连接创建对象。但是,我不会说这是绝对规则。任何 OR-Mapping 层的编写都是为了使最复杂的情况更简单,可能以增加最简单情况的复杂性为代价。请记住,复杂性不是以大小为单位来衡量的,而是以复杂性为单位来衡量的。那里有各种不同的系统。有些预计会增长到几千行代码并永远留在那里。其他人旨在成为公司数据的中央门户,理论上可以不受限制地向任何方向增长。一些应用程序管理每次更新都会读取数百万次的数据。其他人管理仅为审计和临时目的而读取的数据。一般来说,规则是:
规范化在中型或更大的应用程序中几乎总是一个好主意,当拆分两侧的数据都可以修改并且潜在的修改彼此独立时。
从单个表中更新或选择通常比使用多个表更简单,但是通过编写良好的 OR,可以将大部分数据模型空间的这种差异最小化。使用直接 SQL,这对于单个用例来说几乎是微不足道的,尽管它是以非面向对象的方式。
为了便于管理,代码需要保持相对较小,一种有效的方法是划分数据模型并围绕数据模型的各个部分构建面向服务的架构。应在整体复杂性管理策略的范式中考虑数据(去)规范化的最佳状态的目标。
在复杂的对象层次结构中,有一些您在数据库端看不到的复杂性,例如更新的级联。如果您使用对象所有权关系对关系外键和交叉链接进行建模,那么在更新对象时,您必须决定是否级联更新。这可能比在 sql 中更复杂,因为做某事一次和总是正确地做某事之间的区别,有点像加载数据文件和为该类型的文件编写解析器之间的区别。在 C++、java 或任何需要为各种不同场景正确做出决定的代码中级联更新或删除的代码,并且此逻辑中的错误后果可能非常严重。
还有一点值得用规范化规则之一来描述。数据库规范化的一个核心论点是数据重复总是不好的想法。这通常是正确的,但不能盲目地遵循,尤其是当解决方案的不同部分有不同的所有者时。我曾经看到一种情况,一组开发人员管理某种类型的事务,另一组开发人员支持这些事务的可审计性,因此第二组开发人员编写了一个服务,该服务在事务发生时刮掉几个表并创建一个非规范化的快照记录实际上说明了交易时系统的状态。这个场景是一个有趣的用例(至少对于问题的数据重复部分),但它实际上是更大类别问题的一部分。数据一致性要求通常会对数据库中的数据结构施加某些限制,通过使某些不正确的情况变得不可能,从而使错误处理和故障排除变得更简单。但是,这也会产生“冻结”部分数据的影响,因为更改该数据子集会导致过去的事务在一致性规则下变得无效。显然需要某种版本控制系统来解决这个问题,所以显而易见的问题是是使用规范化版本控制系统(有效和到期时间)还是基于快照的方法(交易时间的价值)。标准化版本有几个内部结构问题,您不必担心快照方法,例如:
- 即使对于大型表,日期范围查询也能有效完成吗?
- 是否可以保证日期范围不重叠?
- 是否可以将状态事件追溯到操作员、事务或更改原因?(可能是的,但这是额外的开销)
- 通过创建更复杂的版本控制系统,您是否让正确的所有者负责正确的数据?
我认为这里的最佳目标是不仅要了解理论上什么是正确的,还要了解为什么是正确的,以及违规的后果是什么,然后当您在现实世界中时,您可以决定哪些后果值得承担还有哪些其他好处。这才是设计的真正挑战。
报告系统和交易系统有不同的要求。
我会推荐交易系统,总是使用规范化来保证数据的正确性。
对于报告系统,使用规范化,除非出于某种原因需要非规范化,例如便于即席查询、性能等。
简单?不确定 Steven 是否会用他的报纸打我,但是在我挂的地方,有时非规范化的表可以帮助报告/只读人员完成他们的工作,而不会一直窃听数据库/开发人员......