Logo
Section 1 / 5

概要:实体-属性-值

让我们来看看数据库中实体-属性-值的简单性。

导入

概要:实体-属性-值

让我们来看看数据库中实体-属性-值的简单性。

“我们如何按日期统计行数?”这是数据库程序员的一个简单任务,涉及基本的SQL语法:

1SELECT date_reported, COUNT(*) 2 FROM Bugs 3 GROUP BY date_reported;

然而,这个简单的解决方案假设了两个前提:

  • 数据存储在同一个列中,例如 Bugs.date_reported。

  • 数据值可以相互比较,使得 GROUP BY 能够准确地将具有相同值的日期分组在一起。

如果无法依赖这些假设呢?如果日期可能存储在 date_reported、report_date 或其他每行不同的列中?如果日期格式多种多样,计算机难以直接比较两个日期?

当我们使用一种被称为 实体-属性-值(EAV) 的反模式时,可能会遇到这些问题以及其他类似问题。

目标:支持可变属性

可扩展性通常是软件项目的目标之一。我们希望设计出能够灵活适应未来使用需求的软件,且几乎不需要额外编程。

这并非新问题;自1970年关系模型首次提出以来,关于关系数据库元数据僵化性的批评就一直存在。1970年,埃德加·F·科德(Edgar F. Codd)在《大型共享数据银行的关系数据模型》一文中首次提出关系模型。该文发表于《ACM通讯》,第13卷第6期,页码377–387,1970年6月。

传统表结构中,每一行都代表同一类对象的实例,因此所有行都拥有相同的属性列。不同类型的对象应有各自独立的属性集合,因此应存放在不同的表中。

然而,在现代面向对象编程模型中,不同对象类型可以通过继承同一基类型进行关联。在面向对象设计中,这些对象既是基类型的实例,也是各自子类型的实例。我们希望将这些对象作为行存储在单一数据库表中,以简化对多个对象的比较和计算。但同时,我们也需要允许每个子类型存储其特有的属性列,而这些属性可能并不适用于基类型或其他子类型。

让我们以我们的缺陷数据库为例。在“缺陷类型面向对象类图”中可以看到,Bug 和 Feature Request 共享一些共同属性,如 Issue 基类所定义的内容。每个问题都与报告人相关联,也与某个产品相关,并具有完成优先级。然而,一个缺陷还具有一些独特属性:缺陷发生的版本以及缺陷的严重程度或影响范围。同样,FeatureRequest 也可能有自己的专属属性。在此示例中,假设某个功能与一位赞助商相关联,其预算用于支持该功能的开发。

...
缺陷类型面向对象类图

反模式的合理用途

在关系型数据库中使用EAV反模式很难被合理证明。我们必须牺牲关系模型的许多重要特性。尽管如此,在某些应用场景中,确实存在合法需求来支持动态属性。

大多数需要无模式数据的应用,通常只需要在少数几张表甚至仅一张表中实现。其余的数据需求仍符合标准表结构设计。如果你在项目计划中考虑到了EAV带来的额外工作量和风险,那么适度使用它可能是更优的选择。但必须牢记,经验丰富的数据库顾问指出,使用EAV的系统往往在一年内就会变得难以维护。

如果我们有非关系型的数据管理需求,最佳方案是采用非关系型技术。本课程不深入探讨替代技术的细节,但以下是一些常见技术的简要列举:

  • Berkeley DB 是一种流行的键值存储,易于嵌入各类应用。

  • Cassandra 是由Facebook开发的分布式列式数据库,已贡献给Apache项目。

  • CouchDB 是一种文档型数据库——一种分布式的键值存储,使用JSON编码数据。

  • Hadoop 和 HBase 构成一个开源数据库管理系统,灵感来自谷歌的MapReduce算法,适用于大规模半结构化数据的分布式查询。

  • MongoDB 是一种类似CouchDB的文档型数据库。

  • Redis 是一种基于内存的键值存储。

  • Tokyo Cabinet 是一种键值存储,设计风格类似于POSIX DBM、GNU GDBM或Berkeley DB。

还有许多其他非关系型项目正在涌现。然而,EAV相对于关系型数据库的弱点,同样存在于这些替代方案中。当元数据频繁变动时,编写简单查询变得更加困难。应用程序需要耗费大量精力去发现数据结构并适应变化。

逻辑反模式 - 乱穿马路

反模式:使用通用属性表