简介
数据建模的某些方面会变得主观。实际上,可以公平地说,尽管数据建模具有非常数学的基础,但大多数数据建模方面都是主观的。任何关于如何表示信息的问题都有很多答案,而且并不总是清楚哪一个在上下文中适用。在深入实施细节之前,你应该尽可能全面地了解该上下文和需求。这需要时间,以及其他人的时间,以及大量的提问。研究和访谈对于数据库设计与用户体验设计一样重要:不完整、过于详细、侵入性或结构不良的数据可能会让用户和主题至少与系统的维护者一样痛苦!即使你被提供了需求并被告知去执行,你很可能是第一个完整思考问题及其可能后果的人。
数据建模问题的核心是定义有用的实体,并确定它们是否以及如何在一个连接网络或图中相互关联。这些实体代表物理对象或无形概念的类别——以及介于两者之间的很多东西,例如位置、货运或分配。但是,占据这个符号空间的表示不受物理定律的约束,并且不一定存在符号和所指之间严格的一对一对应关系。单个实体可能代表你正在处理的许多概念共享的一个方面,甚至代表整个子系统;或者,看起来像是单个概念的东西可能被分解成多个概念更有用或更易于管理。
任何实体本身都只能意味着那么多。祖鲁谚语"一个人通过其他人而成为一个人"对于抽象表示也不假。数据模型很少表示孤立事实的单一类别;更常见的是,它们代表系统,即使是少数组件的关系和交互对于系统准确和有用的表示也与组件本身一样重要。
主观性使得无法抽象地回答这些问题。这是一个需要认真思考并通过询问对您收集的数据有利益关系的人来积极调查的问题。即使那样,你最初提出的答案充其量也是暂时的;很少有数据模型能够在发布之前保持不变。
什么是重要的东西,足以被归类为实体?
"我们真正关心的是哪些类别?"这个问题是如此显而易见,以至于即使当你即将把你的假设刻在石头上时,也会忽略它。定义系统的主题和对象是你在使用任何数据库时首先要考虑的事情。这也与数据存储的选择有关:例如,如果你关心的是大量仪器读数或其他简单的事实,你会比更新更频繁地读取这些数据,那么这应该将你指向 Cassandra 或 HBase 等列式数据库。
关系型数据库很复杂。在谈到系统时,必须区分复杂与复杂。复杂系统包括神经网络、经济、语言、生命本身:每个组件基于有限的上下文信息与许多其他组件交互,整个系统与其环境交互,历史和行为出现。
同时,复杂的系统只是由许多单独的部件组成,每个部件都以时钟或发动机的节奏行动和被行动。有可能在数据库中编码历史并定义行为,但这样做是一个严格的自上而下的练习;数据模型的任何看似出现的特征永远不会比确定性的错误更多。数据模型的元素与其他元素交互,但总是以相同的方式与相同的其他元素交互。有些元素对于数据库中的表示就像屏幕门对于潜艇一样,充其量只是增加了一个额外的机械故障点。
实体之间的哪些连接很重要?
在将多个用户群及其数据存储在同一张表中的数据模型中,有两种设计方法。我们将在后面回到多租户的更广泛问题,但有一个共享模式的特定选择,它体现了建模中的一些主要问题:每条记录是否应该包含一个 tenant_id
值,还是只包含那些与租户有直接、有意义的连接的记录?
从概念上讲,只要外键约束被强制执行,识别哪一行属于哪个租户并不困难。一个从表到表再到 tenants
的 JOIN
,然后所有包含你要查找的 tenant_id
值的联接结果都属于该租户。但是,随着模式的进一步发展,管理 SQL 会变得非常糟糕,并且如果系统中的数据足够多,性能可能会成为一个问题。这并不意味着在更远的表中管理额外的 tenant_id
列一定有价值,但也不需要花费很多就能使这个决定合理。
实体和关系构成一个节点和边的有向图。"有向"在这里意味着边是一条单向连接。带有 tenant_id
值的记录意味着 tenants
表的存在,以及其中特定记录的存在(如果设置了正确的外键约束,它保证而不是暗示)!然而,来自 tenants
本身的记录本身并没有暗示哪些其他数据可能属于它。
每个额外的节点或边都会使图变得更加复杂,这反过来意味着维护和改进数据模型将需要更多努力。然而,这种复杂性是局部和全局的问题。实体集可以连接在重要的关系缠结中,这对于管理和使用来说都是具有挑战性的。这些子系统通常可以通过视图或表继承机制(如 PostgreSQL 中的机制)在一定程度上得到屏蔽,其净效应类似于面向对象软件开发中的Facade 模式。
组织单元如何交互?
模型中的任何元素 - 无论是实体、关系,还是包含多个实体和关系的子图 - 都可能代表您正在为其设计的组织的一部分或这些单元之间交互的场所。库存将制造与物流连接起来,或者将物流与销售连接起来。费用记录将财务和人力资源联系起来,以及人力资源与整个公司的部门联系起来。一些参与者可能主要通过边缘的一小部分实体与模型交互;如果他们对系统的更深层运作没有多少了解,或者通过给他们更多可见性和与其他部门的联系来改善组织的整体运作,这可能会带来挑战。
很少有观察结果像Conway 定律一样经久不衰:组织注定要在其系统设计中复制其通信结构,数据库也不例外。一些数据建模问题,全部或部分,实际上是数据库可能无济于事的通信问题。
需要多少细节?
将前两个问题反过来也很有用:如果我们完全忽略这个可能的实体或那个关系,数据模型的用户会错过什么?当然,您最初认定为重要的元素大多数都很重要。但是,有些可能不像最初看起来那么关键,忽略或简化它们可以改善模型的整体运作和可维护性。
重要性的问题有不止“是”和“否”两种答案。您可能不需要将问题空间的某些方面表示为独立的实体,但仍然关心通用且易于管理的事实,如存在或数量。在制造业中,零件号是有关校准、过程历史、子组件、容量、公差等的丰富信息的关键,不同的零件类型有不同的特征。在仓储中,零件号变为 SKU(来自“库存单位”),关键细节要简单得多:有多少,什么颜色,重量多少,以及它们在哪里?
某些信息也很有用,但已经具有自身的内部结构,将它们转换为实体和关系不太方便。层次结构、超文本文档、物料清单,甚至数据库中其他地方实体关系子图的瞬态“工作副本”;这些或它们包含的信息可以以关系方式表示,但除非存在跨越外部和内部结构边界的重要的关系,否则分解它们的努力可能不值得。如果这种信息是您主要关注的,则可能适合使用专用数据库,例如(对于分层文档)CouchDB 或 MongoDB。如果它们是其他关系模型的例外,则 JSON 和 XML 等数据类型可以帮助避免将模型拆分到两个或多个数据库中。每个额外的数据库不仅需要更多维护和协调工作,而且参照完整性(保证关系所依赖的信息不会消失)只能在单个数据库中保持。
哪些子图、聚合和统计信息有用?
查询关系数据库需要时间。具体需要多少时间取决于多种因素:主机计算机的物理属性,如磁盘速度和 CPU 能力,所讨论的 RDBMS 的操作能力,如缓存和优化,最后是查询本身的构成。您的数据模型必须适应前两类设定的限制,但其成功的关键决定因素之一是它们在多大程度上预测第三类问题。
跨越的关系数量是查询组成在性能和可维护性方面最重要的方面之一。高 JOIN
计数并不一定意味着不可接受的性能,尤其是在连接的实体较小或索引良好的情况下,但即使在最佳情况下,每个连接都会增加查询作者的认知负担。对于您、后端开发人员和其他数据库用户来说,组装常用的结果集应该尽可能简单。
数据库提供了一些工具来帮助完成此任务。视图封装了常用的子图及其计算,这有助于将复杂性从用户和应用程序中屏蔽。将您的二级表示集中在数据库中还可以确保存在对,例如,带有所有相关信息的采购订单或仓库的常用统计信息的单一规范表示。普通视图对性能没有任何帮助,因为它们是存储的查询,它们会集成到正在运行的语句中。然而,大多数关系数据库支持“物化”视图(MySQL 和 MariaDB 是值得注意的例外)。这些视图将数据持久化到磁盘,就像表格一样,使检索速度快得多,但是数据会过时,需要刷新。
这里的最后手段是反规范化:将实体的数据或有用的聚合(如计数或总数)作为其他实体的属性内联存储。我们将在后面的章节中更深入地探讨范式,但可以肯定地说,这并非轻易采取的措施。尽管如此,如果组装您需要的数据耗时过长,放弃更高层次的规范化可能值得额外的管理工作。如果您需要在这里走极端,那么该考虑再次使用列式数据库了。
不应该存储什么?
您不负责您没有的信息。整个行业都围绕着保护数据而构建,但是没有任何安全措施是万无一失的。避免一类非常棘手问题的最简单方法是不收集可能导致您出现此类问题的數據。请注意,这超出了数据库的范围;敏感信息可能会出现在日志、报告、源代码管理和其他系统中。
最敏感的一类信息,其影响最大,是与人有关的信息。关于人的信息必然是政治性的,在人类参与的系统中建立模型既反映了又强化了建模者的政治立场。谁进入或被输入系统,以什么身份,以哪些特征被认为重要或不重要,对谁可见:政治,每一个细节。我们才刚刚开始看到随意或侵犯性地处理个人数据的后果。越来越多的个人信息不断被输送到数据模型中,而这些模型往往没有得到相应的重视。在很多情况下,它们的设计初衷就是为了利用这些信息。
个人身份信息或 PII 指示了其他数据与谁相关。如果没有 PII,随机的医疗记录就是在真空中的历史。某个地方的某个人,只知道一个号码,有这些童年疫苗接种记录,在这里出现肺炎,在那里出现轻微的皮肤癌,曾经出现过物质依赖,还出现过几次流感,以及躁郁症。将该医疗记录号与姓名和地址联系起来,突然间您 - 或任何可以查看它的人 - 了解了关于某个特定人的生活比他们可能打算或希望其他人知道的更多。对于那些数据属于他们的人来说,收集我们无法成功保护的更多数据的后果从简短的不愉快谈话一直到失业、身份盗窃、勒索和各种仇恨犯罪。
在医疗记录方面,许多国家制定了严格的法律来管理访问和披露,这是有充分理由的。大多数其他语境下的信息不受如此严格的法律控制(如果有的话),这意味着您有责任弄清楚您需要多认真地保护这些姓名和地址、出生日期、生物识别信息、母亲的娘家姓以及政府分配的身份值,如护照或纳税人识别号码。PII 也会出现在您意想不到的地方,就像 2004 年 AOL 将他们认为是匿名化的搜索日志发布给公众时发生的那样。更糟糕的是,即使数量足够多的非识别性事实也会变成识别性事实:还有多少其他人与您居住在同一个城市,拥有相同的性别、职位、手机型号和最喜欢的餐厅?还有什么其他事情可能与这种事实组合相关联?
另一类敏感信息允许您代表其他人采取行动。社交媒体或其他网络凭据、银行路由和账户信息、信用卡号码都是您信任给您的。在某些情况下,尤其是处理付款时,存在第三方服务,它们将这种信任负担从您身上解除。除非您有充分的理由自己承担,否则请使用它们。
接下来会发生什么?
数据模型并非一成不变。需求会演变,业务会转型,外部信息来源也会出现、改变和消失。这些变化不像应用程序开发那样快节奏,但变化是确实存在的,从大改到小修,不一而足。精心设计有助于将变化控制在更慢、更小的范围内,但无法完全避免对数据模型进行大修的情况,甚至一些看似微不足道的改动也可能在部署时带来意想不到的困难。例如,在 PostgreSQL 11 之前,添加一个具有非空默认值的新列需要重写表中的所有记录,对于数百万行数据来说可不是一件轻松的事!
当下比未来更重要。如果您的模型现在没有用,您可能就没有机会看到它在将来变得有用。然而,自然或人为选择的规律始终存在,无法适应这些压力的数据库将会消亡。我们将在后面详细介绍如何规划未来的可能性,但即使了解可能会把自己逼到角落,也能帮助您避免这种命运。