分享

介绍

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

*数据库模式* 作为数据库中数据形状和格式的蓝图。对于关系型数据库,这包括描述数据类别及其通过表、主键、数据类型、索引和其他对象连接的方式。对于 NoSQL 模式,这通常涉及根据最有可能的预期查询模式组织数据。

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

为什么数据库模式很重要?

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

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

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

良好的模式往往会减少隐式信息,而有利于将其显式化给系统及其用户。关系型数据库中的模式可以减少信息冗余,确保数据一致性,并提供访问和连接相关数据的必要支架和结构。在非关系型环境中,良好的模式通过将存储格式与应用程序必不可少的访问模式对齐,从而实现高性能和可扩展性。

定义物理模式与逻辑模式

在我们继续之前,我们应该介绍几个定义。两个可能令人困惑的术语是 *物理模式* 和 *逻辑模式*。这两个术语的含义可能因它们使用的 *上下文* 而异。

出于本文的目的,我们主要在 *设计数据库模式* 时谈论逻辑模式和物理模式。

设计数据库模式时

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

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

在这种情况下,逻辑模式和物理模式是设计过程的不同阶段。该过程的目标是通过首先列出数据的抽象特性,然后将该组织映射到您要使用的数据库系统的工具集和语言,从而迭代地开发一个实现计划。

讨论数据库架构时

在讨论数据库架构时,物理模式和逻辑模式有时在实际数据库软件的物理和虚拟架构中被提及。

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

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

静态模式与动态模式

另一个可以帮助澄清关系型数据库和非关系型数据库之间模式差异的重要分类是静态模式和动态模式之间的差异。

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

相比之下,**动态模式**在非关系型环境中更为普遍。动态模式没有那么严格,甚至可能完全缺乏预先设定的组织结构。相反,动态模式会根据输入系统的数据特性出现。尽管许多非关系型数据库可以存储具有任意内部结构的信息,但大多数现实世界的用例都会出现规律的模式。

由于动态模式是出现结构,因此数据库系统无法将其用作一致性工具。但是,作为用户,它们仍然非常重要,需要理解并围绕它们进行开发。了解数据在一般情况下的外观以及应用程序与数据交互的方式将有助于您选择满足需求、性能良好且避免不必要的差异性的结构。

设计数据库模式

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

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

一般提示

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

了解您的数据

设计模式的第一步应该始终是了解您的数据和领域。如果不了解要管理的信息及其使用环境,就无法开发出良好的数据库设计。

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

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

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

了解使用模式

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

您应该问问自己以下问题

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

开发命名约定

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

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

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

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

为关系型数据库设计模式

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

开发逻辑模式

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

您将要管理的数据项、它们之间的关系以及任何需要考虑的属性映射出来,而不考虑实现细节或性能标准。这一步很重要,因为它将所有数据项收集在一个地方,并允许您在抽象级别上对它们之间的关系进行分类。

您可以开始勾勒出代表特定数据项及其属性的表。这种映射过程通常最好由 实体-关系 (或 ER) 模型 表示。ER 模型是图表,通过定义项目类型及其属性来直观地表示数据对象,然后将它们连接起来以映射出关系和依赖关系。

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

开发物理模式

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

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

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

规范化数据结构

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

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

设计非关系型和 NoSQL 数据库的架构

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

确定您的主要查询

非关系型数据库架构通常与将使用它们的应用程序一起设计。架构反映了应用程序的特定需求,从某种意义上说,它是一个定制结构,旨在适合应用程序开发的模具。

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

确定您的应用程序需要执行哪些查询后,您需要选择最重要的查询来重点关注。这些是您的应用程序经常执行的查询,而且不能等待它们完成。

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

围绕您的主要查询设计您的初始架构

现在您已经了解了最基本的访问模式,您可以开始开发与这些查询匹配的架构。

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

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

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

确定了所需的属性并规划了将与每个查询相关的所有项目存储在一个实体中的样子之后,检查是否有重叠。这样做的目的是尽可能地合并数据实体,以减少系统将维护的单独项目的数量。维护的独立实体类型越多,出现不一致和更新性能问题的可能性就越大。

其中一些重叠相当明显。一个查询返回另一个查询的属性子集的情况可以安全地合并到一个实体中。

其他时候,可能更难确定如何映射查询的信息。非关系型数据库通常不擅长从多个实体中合并单个查询中的数据,而这正是关系型数据库通过连接所擅长的。因此,当某些属性或实体存在于多个查询中时,您可能需要在如何最好地表示这些数据方面做出选择。

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

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

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

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

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

确定适当的划分键

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

找到合适的划分键高度依赖于您的数据和工作负载。但是,一些一般规则可以帮助您进行指导。

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

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

另一方面,如果您有写密集型工作负载,通常最好将写入分散到尽可能多的服务器上。如果每个请求最终将数据写入同一台服务器,您将无法在写密集型操作中获得多少性能提升。

总结

设计有效的数据库架构需要耐心、练习,并且通常需要大量的反复尝试。

首先,您必须尝试了解您的数据将是什么样子,您的应用程序将如何使用它,以及需要哪些可用性和数据完整性要求。之后,您的目标是开发一个反映数据特定特征并促进您预期的用例类型的架构。

架构设计,就像任何其他类型的设计一样,是一个反复迭代的过程。随着您对问题空间的理解加深以及真实世界性能数据的可用性,预计您的设计会发生变化。虽然您可能需要随着时间的推移来演变您的架构,但从一个坚实的基础开始将在这方面为您提供帮助,并降低将来发生重大、破坏性架构变更的可能性。

关于作者
Justin Ellingwood

Justin Ellingwood

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