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