分享到

简介

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

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

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

为什么数据库 Schema 很重要?

数据库 Schema 之所以重要,原因有很多。

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

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

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

定义物理 Schema 与逻辑 Schema

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

就本文而言,我们主要在*设计数据库 Schema*时讨论逻辑 Schema 和物理 Schema。

设计数据库 Schema 时

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

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

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

讨论数据库架构时

物理 Schema 和逻辑 Schema 有时在数据库的物理和虚拟架构方面被提及,这是另一个上下文。

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

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

静态 Schema 与动态 Schema

另一个重要的分类有助于阐明关系型和非关系型数据库 Schema 之间的区别,即静态 Schema 和动态 Schema 之间的区别。

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

相比之下,**动态 Schema** 在非关系型上下文中更为普遍。动态 Schema 缺乏刚性,甚至可能没有任何预设的组织结构。相反,动态 Schema 是根据输入到系统中的数据质量*涌现*的。尽管许多非关系型数据库可以存储具有任意内部结构的信息,但大多数实际用例中往往会出现规则模式。

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

设计数据库 Schema

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

设计过程因您要为其设计 Schema 的数据库类型而异。具体来说,静态 Schema 的设计过程与动态 Schema 的设计过程不同。实际上,这些差异最终会与关系型数据库(静态)和非关系型数据库(动态)的设计差异对齐。

一般提示

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

了解你的数据

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

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

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

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

理解使用模式

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

您应该问自己以下问题:

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

制定命名约定

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

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

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

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

为关系型数据库设计 Schema

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

开发逻辑 Schema

关系型 Schema 设计通常从逻辑 Schema 开始,正如上一节讨论的那样。

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

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

ER 模型经常用于早期 Schema 设计,因为它们非常擅长帮助您找出您拥有的不同实体、必须管理的属性、哪些实体相互关联以及它们关系的特定性质。使用 ER 模型图来表示您的逻辑 Schema 为您提供了关于*您想要您的数据库设计是什么*的可靠计划,而无需评论实现特定的细节。

开发物理 Schema

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

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

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

规范化数据结构

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

**数据库规范化**是一个过程,它确保您的数据库每次只表示一块数据,并且不允许导致不一致的更新。规范化是一个巨大的主题,在大多数情况下超出了本指南的范围,但物理 Schema 设计过程的一部分涉及确定要寻求的规范化级别,并根据需要转换数据实体以实现该目标。

为非关系型和 NoSQL 数据库设计 Schema

非关系型数据库的设计过程通常大相径庭。这种差异很大程度上源于非关系型数据库通常是为了在有限数量的预定义查询上实现高性能而选择的。

确定您的主要查询

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

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

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

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

围绕主要查询设计初始 Schema

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

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

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

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

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

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

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

确定应用程序可以在何处填补空白

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

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

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

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

确定合适的切分键

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

找到正确的分区键高度依赖于您的数据和工作负载。然而,一些一般规则可以指导您。

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

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

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

总结

设计有效的数据库 Schema 需要耐心、实践,通常还需要大量的试错。

首先,您必须努力对您的数据将是什么样子、您的应用程序将如何使用它以及需要哪些可用性和数据完整性要求有一个很好的概念。之后,您的目标是开发一个 Schema,该 Schema 反映您的数据的特定功能并促进您预期的用例。

Schema 设计,就像任何其他类型的设计一样,是一个迭代过程。随着您对问题领域的理解加深以及实际性能数据可用,预计您的设计会发生变化。虽然您可能需要随着时间的推移演变您的 Schema,但从坚实的基础开始将有助于您完成此过程,并减少未来发生剧烈、破坏性 Schema 更改的可能性。

作者简介
Justin Ellingwood

Justin Ellingwood

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