导言
数据建模的某些方面变得主观。实际上,公平地说,尽管数据建模具有非常数学的基础,但其大多数方面都是主观的。关于如何表示信息的任何问题都有许多答案,而且并不总是清楚哪个答案最适合具体情况。在深入研究实施细节之前,您应该尽可能地了解上下文和需求。这需要时间,以及其他人的时间,以及大量的问题。研究和访谈对于数据库设计与用户体验设计同等重要:不完整、过于详细、侵入性或结构不良的数据可能会给用户和主题以及系统的维护者带来至少同样多的痛苦!即使您正在接受需求并被告知要执行,您也最有可能成为第一个通盘考虑问题及其可能后果的人。
数据建模问题的关键在于定义有用的实体,并识别它们是否以及如何在连接的网络或图中相互关联。这些实体代表物理对象或无形概念的类别——以及介于两者之间的许多事物,例如位置、货物或任务。但是,占据这个符号空间的表示不受物理定律的约束,并且在能指和所指之间不一定存在严格的一对一对应关系。单个实体可能代表您正在处理的许多概念共享的方面,甚至代表整个子系统;或者,看起来像是奇异概念的东西可能更有用或更易于管理地分解为多个部分。
任何实体本身都只能表达这么多含义。祖鲁谚语“人通过他人成为人”对于抽象表示也并非不正确。数据模型很少表示孤立事实的单一类别;更常见的是,它们表示系统,即使只有少数组件,它们的关系和交互作用对于系统准确且有用的表示也与组件本身同等重要。
主观性使得无法在抽象中回答这些问题。这是一项需要思考的练习,并通过询问与您收集的数据有利害关系的人员来积极调查。即便如此,您最初制定的答案充其量也只是暂定的;很少有数据模型能够保持不变直到发布。
什么足够重要以符合实体的资格?
“我们真正关心哪些类别?” 这是一个显而易见的问题,以至于可能会被忽略,即使您即将将您的假设写在石头上。无论您使用什么数据库,定义系统的对象和主题都是首先要考虑的事情。这也与数据存储的选择有关:例如,如果您关心大量仪器读数或其他直接的事实,而您读取的频率远高于更新的频率,那么这应该会将您指向像 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 之前,添加具有非空默认值的新列需要重写表中的每个记录——对于数百万行来说,这不是一件有趣的事情!
现在比未来更重要。如果您的模型现在没有用,您可能就没有机会看到它以后变得有用。自然的或非自然的选择力量始终在起作用,无法适应这些压力的数据库就会消亡。我们将在稍后更详细地讨论为未来可能性进行规划,但即使知道存在您可以将自己困住的角落,也可以帮助您避免那种命运。