分享到

简介

数据库相对于其他更简单的数据存储选项的主要优势之一是它们能够以有序、易于查询的结构存储信息。这些特性源于数据库实现模式来描述它们存储的数据。

数据库模式充当数据库内数据形状和格式的蓝图。对于关系数据库,这包括描述数据类别及其通过表、主键、数据类型、索引和其他对象的连接。对于 NoSQL 模式,这通常涉及根据最重要的预期查询模式组织数据。

无论在哪种情况下,理解数据库模式的价值以及如何最好地设计和优化它以满足您的需求都至关重要。本指南将重点介绍什么是数据库模式、您可能遇到的不同模式类型、它们为什么重要,以及在设计自己的模式时需要注意什么。

为什么数据库模式如此重要?

数据库模式非常重要,原因有很多。

无论数据来源或应用如何,您的数据几乎总是包含一定的规律性。有些数据是高度规则的,这意味着所有数据都可以用相同的模式来描述。有些数据则更加不规则,但即便如此,它的元数据,即关于数据本身的上下文数据,通常仍然是规则的。

数据库模式告诉数据库您的数据是什么以及如何处理它。数据库模式帮助数据库引擎理解这些模式,从而使其能够对数据强制执行约束,在查询时响应正确的信息,并以用户请求的方式对其进行操作。

良好的模式倾向于减少隐式信息,使其对系统及其用户可见。关系数据库中的模式可以减少信息冗余,确保数据一致性,并提供访问和连接相关数据所需的支架和结构。在非关系上下文中,良好的模式通过将存储格式与应用程序必不可少的访问模式对齐,从而实现高性能和可扩展性。

定义物理模式与逻辑模式

在我们进一步深入之前,我们应该介绍一些定义。两个可能令人困惑的术语是物理模式逻辑模式。这两个术语在使用的上下文中可能传达不同的含义。

就本文而言,我们主要讨论的是设计数据库模式时的逻辑模式和物理模式。

设计数据库模式时

在讨论设计数据库模式时,逻辑模式是将数据组织成不同类别、定义数据属性以及确定数据库项最佳结构的一般设计。此通用文档没有实现细节,因此与平台无关。它可以被视为蓝图并在各种数据库系统中实现。

在相同的上下文中,物理模式被认为是设计过程的下一步,其中制定了特定于实现的细节。不同实体、约束、键、索引和其他项的名称被识别并映射到逻辑模式上。这为使用给定数据库平台进行实现提供了具体计划。

在这种上下文中,逻辑模式和物理模式是设计过程的不同阶段。该过程的目标是通过首先布局数据的抽象质量,然后在稍后将该组织映射到您想要使用的数据库系统的工具集和语言,从而从一组需求迭代地开发实现计划。

讨论数据库架构时

物理模式和逻辑模式有时在数据库中看到的另一个上下文是在实际数据库软件的物理和虚拟架构中。

在这种上下文中,逻辑模式指的是用户与之交互的可见数据库实体。这意味着像表、键、视图和索引这样的对象是用户使用数据库软件创建和操作的抽象。这些项在系统中的布局是数据库呈现的逻辑模式的一部分。

在相同的上下文中,物理模式指的是数据库软件在与文件系统交互时处理数据、文件和存储的方式。例如,数据库架构的物理模式可以确定系统是否为每个数据库或每个表存储单独的文件,并确定如何跨多个服务器对这些文件进行分区。

静态模式 vs 动态模式

另一种重要的分类可以帮助阐明关系数据库和非关系数据库中模式之间的差异,即静态模式和动态模式之间的差异。

静态模式是通常与关系数据库关联的模式类型。它们是预先定义的,作为数据必须遵循的形状的定义,才能被系统接受。数据库系统在使用静态模式时能够强制执行这些模式,因为静态模式是对所需状态的断言,数据库系统可以根据该状态验证输入。

相比之下,动态模式在非关系上下文中更为普遍。动态模式不太严格,可能缺乏任何先入为主的组织结构。相反,动态模式出现是基于输入到系统中的数据的质量。虽然许多非关系数据库可以存储具有任意内部结构的信息,但在大多数实际用例中,规则模式往往会浮现出来。

由于动态模式是涌现的结构,因此数据库系统不能将其用作一致性工具。但是,作为用户,理解和围绕它们进行开发仍然非常重要。从总体上了解您的数据将是什么样子,以及您的应用程序将需要如何与之交互,将有助于您选择满足您需求、性能良好并避免不必要不一致的结构。

设计数据库模式

既然您了解了一些不同类型的数据库模式,那么如何为您的项目设计一个模式呢?设计有效的模式需要思考和实践,以及对问题领域和将使用数据的系统有透彻的理解。

设计过程因您要为其设计模式的数据库类型而异。具体来说,静态模式的设计过程与动态模式的设计过程不同。实际上,这些最终与为关系数据库(静态)和非关系数据库(动态)设计之间的差异保持一致。

一般技巧

尽管关系数据库和非关系数据库的模式设计之间存在差异,但仍有一些通用技巧适用于任何模式开发。由于其中许多技巧对于设计过程的开始很重要,因此首先讨论这些技巧是有意义的。

了解您的数据

设计模式的首要步骤之一始终是了解您的数据和领域。如果不了解数据库将管理的信息及其将使用的上下文,就不可能开发出良好的数据库设计。

虽然您可能在一开始并不了解数据的全部特征,但尽可能多地了解您的系统预期管理的数据对于设计至关重要。

您应该尝试回答的一些问题包括

  • 广义上讲,数据将是什么?
  • 哪些属性对记录很重要?
  • 您的总数据集将有多大?
  • 系统积累新数据的速度有多快?
  • 您的数据是否高度规则?

了解使用模式

同样,在不了解用户需求的情况下设计数据库模式,就像其他软件设计一样成问题。如果您不是数据将要使用的领域的专家,则需要咨询该领域的专家来指导您了解需求。

您应该问自己如下问题

  • 最常见的查询是否可预测?
  • 将有多少并发用户或客户端?
  • 典型操作和查询将触及多少数据?
  • 大多数请求将是读取查询还是写入查询?
  • 哪些数据将定期一起查询?
  • 大多数操作是针对单个记录还是聚合多个记录?

制定命名约定

虽然它可能看起来并不重要,但设计命名约定并严格遵循它将有助于开发和常规使用。

命名和样式约定有助于最大限度地减少在命名新实体时需要执行的脑力劳动量。同样,约定允许用户在访问模式中的不同项时安全地假设模式。一些数据库系统或数据库类型已经有流行的命名约定,您可以遵循这些约定以避免意外,并避免开发自己的标准的需要。

您可能要考虑的一些样式和命名约定

  • 对于区分大小写的系统,您应该如何使用大写和小写字母?
  • 项目何时应使用单词的复数形式,何时应使用单数形式?
  • 多词名称是否应该用下划线、破折号或其他分隔符分隔单词?
  • 是否应始终使用全名,还是在某些情况下允许使用缩写?

为关系数据库设计模式

关系数据库通常被认为是灵活的、通用的解决方案。它们处理即席查询的能力允许同一个数据库为不同的应用程序和用例服务。因此,在为关系数据库设计模式时,您的最终目标通常是以一种促进灵活性,同时最大限度地减少数据不一致性进入系统的机会的方式来表示您的数据。

开发逻辑模式

关系模式设计通常从逻辑模式开始,如上一节所述。

您绘制出要管理的数据项、它们的关系以及需要考虑的任何属性,而无需考虑实现细节或性能标准。此步骤很重要,因为它将所有数据项收集在一个位置,并允许您在抽象级别上梳理它们彼此关联的方式。

您可以开始草拟表示特定数据项及其属性的表。此映射过程通常最好用 实体关系(或 ER)模型 表示。ER 模型是图表,通过定义项目类型及其属性,然后连接这些项目以绘制关系和依赖项,从而直观地表示数据对象。

ER 模型经常在早期模式设计中使用,因为它们非常擅长帮助您弄清楚您拥有哪些不同的实体、必须管理哪些属性、哪些实体彼此相关以及它们关系的具体性质。使用 ER 模型图来表示您的逻辑模式,为您提供了一个关于希望数据库设计成为什么的可靠计划,而无需评论特定于实现的细节。

开发物理模式

一旦您有了逻辑模式,下一步就是通过创建物理模式(如上一节所述)来弄清楚具体的实现细节。物理模式将准确确定您希望如何使用可用的数据库结构和功能来提交您的计划。

第一步通常是遍历每个数据库实体并确定您的主键字段。主键用于唯一标识表中的每个记录,以及将来自不同表的记录绑定在一起。当逻辑模式中的两个实体之间存在关系时,您将必须通过在一个表中引用主键作为另一个表中的外键来连接物理模式中的两个表。这种关系的方向将影响您在使用数据库时连接不同实体的性能和易用性。

您在此阶段要考虑的另一个因素是预测的查询模式。某些表和这些表中的字段将被访问的频率远高于其他表和字段。这些“热点”是数据库索引的良好候选者。数据库索引显着加快了常用项的检索速度,但代价是在数据更新期间性能较差。确定最初要索引哪些列将帮助您平衡这些考虑因素,并定义系统中索引最关键的位置。

规范化您的数据结构

在此过程中,您可能会发现将某些元素从逻辑实体提取到它们自己的独立表中更容易。例如,您可能希望从客户那里提取送货地址,以便可以将多个送货地址与单个客户关联,并使产品订单可以引用特定地址。这些更改可以被认为是称为规范化的过程的一部分。

数据库规范化是一个确保您的数据库仅表示每条数据一次,并且不允许导致不一致的更新的过程。规范化是一个巨大的主题,在很大程度上超出了本指南的范围,但物理模式设计过程的一部分包括弄清楚要寻求的规范化级别,并根据需要转换数据实体以实现该目标。

为非关系数据库和 NoSQL 数据库设计模式

非关系数据库的设计过程通常看起来大相径庭。这种差异的很大一部分源于这样一个事实,即通常选择非关系数据库是为了在有限数量的预定义查询上实现高性能。

确定您的主要查询

非关系数据库模式通常与将要使用它们的应用程序协同设计。模式反映了应用程序的特定需求,并且在某种意义上,是一种定制结构,旨在适应应用程序开发的模型。

由于这种密切的关系,确定必须优化数据库以响应哪些查询非常重要。第一步是弄清楚您的数据库需要运行哪些查询。由于您还没有数据结构,这些将是伪查询,但了解您的应用程序将需要哪些数据来执行某些操作是您的首要目标。

一旦您对应用程序需要执行哪些查询有了很好的了解,就需要选择最重要的查询来关注。这些是您的应用程序经常执行且无法承受等待的查询。

定义哪些查询最重要会告诉您数据结构需要围绕哪些确切的访问模式进行优化。数据库系统存储和表示数据的方式将对数据库系统快速检索和操作数据项的能力产生巨大影响。

围绕您的主要查询设计您的初始模式

现在您知道了最重要的访问模式,就可以开始开发与这些查询匹配的模式。

此过程的第一步是确定每个查询需要返回的确切信息。然后,绘制出将所有信息存储在一个实体中以响应查询的样子。

例如,如果您的应用程序将查询您的数据库以检索用户个人资料信息,那么您的起点可能应该是假设所有用户个人资料信息都可以存储在一个位置。

尽可能合并和去重数据实体

在您确定了所需的属性并绘制出将与每个查询相关的所有项目存储在单个实体中的样子之后,请检查是否存在重叠。这个想法是在可能的情况下合并数据实体,以减少系统将维护的单独项目的数量。您维护的不同实体类型的数量越多,出现不一致和更新性能问题的机会就越大。

其中一些重叠将非常明显。一个查询返回另一个查询的属性子集的情况可以安全地折叠为一个实体。

其他时候,可能更难以确定如何映射查询的信息。非关系数据库通常不擅长在单个查询中合并来自多个实体的数据,而关系数据库通过连接在这方面表现出色。因此,当某些属性或实体出现在多个查询中时,您可能必须选择最佳的数据表示方式。

确定您的应用程序可以在哪里填补空白

对于某些查询,您的应用程序可能需要执行部分数据组装工作,而不是依赖数据库在单个查询中返回所有相关信息。例如,如果您需要处理客户信息及其关联的订单,则将订单存储在不同的类别中,并在您的客户对象中通过 ID 引用它们可能更有意义。

某些数据库系统无法通过跟踪对象之间的引用来轻松连接此信息。相反,您的应用程序可能需要先查询客户,然后使用您发现的订单 ID 为每个相关订单发出额外的查询。

在您的应用程序代码中执行这些操作可以帮助您绕过某些非关系型数据库的限制。这通常比尝试在单个条目中维护大量信息或尝试为许多不同类型的数据库对象多次复制数据更好。这些选项可能会导致非常差的性能和数据一致性。

话虽如此,重要的是在您的应用程序代码和数据库模式上线后对其进行测试和调整,以确保您没有为了快速的数据库操作而牺牲良好的应用程序性能。一个好的经验法则是尽可能使用数据库的功能,因为它们针对信息检索和操作进行了高度优化,并根据需要在您的应用程序中进行补充。

确定合适的分区键

对于高度可扩展的非关系型数据库,用户通常必须确定分区键或分片键。这些键将用于在各种服务器之间拆分数据集,以提高性能和响应速度。

找到合适的分区键高度依赖于您的数据和工作负载。然而,一些通用规则可以帮助您进行指导。

最好尝试选择一个键分布相当均匀的分区键。例如,如果您需要分发客户数据,他们的出生月份通常会带来不错的分布。相反,如果您销售冬季服装,注册月份将不是一个好的分区键,因为您产品的季节性可能会影响键的分布。对您的候选数据应用哈希算法有时也有助于更均匀地分布您的键空间。

另一个考虑因素是您的工作负载是读密集型还是写密集型。如果您有一个读密集型应用程序,您可能希望选择一个分区键,该分区键允许您将尽可能多的相关数据写入单个服务器。这将帮助您避免在每次需要检索相关数据时都必须从多个服务器读取。

另一方面,如果您有写密集型工作负载,通常最好将写入操作分散到尽可能多的服务器上。如果每个请求最终都将数据写入同一服务器,那么对于写密集型操作,您将不会获得太多性能提升。

总结

设计有效的数据库模式需要耐心、实践,并且通常需要大量的尝试和错误。

首先,您必须尝试对您的数据外观、应用程序将如何使用它以及需要哪些可用性和数据完整性要求有一个好的想法。之后,您的目标是开发一个反映您数据的特定特征并促进您预期的用例的模式。

模式设计,就像任何其他类型的设计一样,是一个迭代过程。当您对问题空间的理解加深并且实际性能数据可用时,预计会更改您的设计。虽然您可能必须随着时间的推移发展您的模式,但从坚实的基础开始将有助于您完成此过程,并降低未来发生剧烈的、破坏性的模式更改的可能性。

关于作者
Justin Ellingwood

Justin Ellingwood

自 2013 年以来,Justin 一直在撰写关于数据库、Linux、基础设施和开发者工具的文章。他目前与妻子和两只兔子住在柏林。他通常不必以第三人称写作,这对所有相关方来说都是一种解脱。