[译] Django EAV 2 - 现代 Django 的 EAV 存储

一直以来,EAV 模型的设计及使用存在争议性。这是 Django EAV 2 (https://github.com/jazzband/django-eav2)的自述文件(README.md),是的,您没看错,是该 repo 的自述文件。这篇文章对 EAV 及应用场景的介绍得很到位,可以说是一篇 EAV 最佳实践指南。

Django EAV 2 - Django 的实体-属性-值存储( Entity-Attribute-Value storage)

Django EAV 2 是 django-eav 的一个分叉(django-eav 本身是由 eav-django 衍生而来)。你可以在 这里 找到文档。

EAV 到底是什么?

实体-属性-值模型(EAV)是一种数据模型,用于以节省空间的方式对实体进行编码,其中可用于描述实体的属性(属性、参数)数量可能非常多,但实际适用于特定实体的数量却相对较少。这样的实体对应于稀疏矩阵的数学概念。(Wikipedia)

EAV 中的数据被存储为一个3元组(通常对应于三个不同的表)。

  • 实体:被描述的项目,例如:Person(name='Mike')。
  • 属性:通常是一个属性表的外键,例如Attribute(slug='height', datatype=FLOAT)。
  • 属性的值,包含属性和实体的链接,例如:Value(value_float=15.5, person=mike, attr=height)。

django-eav2 中的实体是你典型的 Django 模型实例。属性(名称和类型)存储在他们自己的表中,这使得在系统中操作可用的属性列表变得容易。值是属性和实体之间的一个中间表,每个实例持有一个值。这种实现也使得在Django Admin和表单实例中编辑属性变得容易。

Django -eav2 中的实体是典型的 Django 模型实例。属性(名称和类型)存储在它们自己的表中,这使得在系统中操作可用属性的列表变得很容易。值是属性和实体之间的一个中间表,每个实例包含一个值。这个实现也使得在Django Admin和表单实例中编辑属性变得很容易。

你可以在这里找到关于EAV的详细描述:

EAV - 好的、坏的还是丑的?

EAV 是一种灵活性和复杂性之间的权衡。因此,它不应该被认为是一种改善的模式,也不应该被认为是一种反模式。它更像是一种灰色模式--它存在于某些环境中,用来解决某些问题。如果使用得当,它可以引入巨大的灵活性,缩短原型设计时间或降低复杂性。然而,如果不小心使用,它可能会使数据库模式复杂化,降低性能并使维护变得困难。 就像每一个工具一样,它不应该被过度使用。 在下面的段落中,我们简要地讨论了 EAV 的优点、缺点和使用时需要注意的要点。

什么时候使用 EAV?

最初,引入 EAV 是为了解决在关系模型中不易解决的问题。为了实现这一点,EAV 绕过了正常的模式限制。有些人认为这是内部平台效应的一个例子。自然,在这种情况下,RDMS 资源不能被有效地使用。

EAV 模型的典型应用集解决了稀疏数据的问题,这些数据有大量的适用属性,但只有一小部分适用于一个特定的实体,而这个实体可能事先并不知道。考虑一个经典的例子:

在生物医学领域,数据建模者经常遇到的一个问题是组织和存储高度多样化和异质性的数据。例如,一个病人可能有数以千计的适用描述性参数,所有这些参数都需要在电子病历系统中容易获取。这些要求带来了重大的建模和实施挑战。[1]

还有:

[...]当你有客户要求实时、按需添加他们想存储的属性时,你该怎么办?在我管理的一个系统中,我们的客户正是想这样做。由于我们运行的是 SaaS(软件即服务)应用程序,我们有许多跨越几个不同行业的客户,他们又想用我们的系统来存储关于他们客户的不同类型的信息。一家连锁美容院可能想记录诸如 "头发颜色"、"头发类型" 和 "理发频率" 等事实;而一家投资公司可能想记录诸如 "投资组合名称"、"上次投资组合调整日期" 和 "当前投资组合余额" 等事实。[2]

在这两个问题中,我们必须处理稀疏和异质的属性,这些属性只适用于特定实体的潜在不同子集。将 EAV 应用于数据库的子模式中,可以对所需的行为进行建模。传统的解决方案是用许多列的宽表来存储不适用于某个实体的属性的 NULL 值。

EAV 非常常见的用例是电子商务实现中的自定义产品属性,例如 Magento。[3]

作为一个经验法则,EAV 可以在以下情况下使用:

  • 模型属性由终端用户添加和删除(或以某种不同的方式不可知)。EAV 支持这些特性,不需要 ALTER TABLE 语句,并允许属性具有强类型和易于搜索。
  • 会有很多属性,而且值是稀疏的,这与拥有大部分为空列的表形成对比。
  • 数据是高度动态的/易变的/易受更改的。这个问题出现在上面给出的第二个例子中。另一个例子是快速发展的系统,比如需求不断变化的原型。
  • 我们想要存储元数据或支持信息,例如,定制系统的行为。
  • 需要表示大量的数据类,每个类有有限数量的属性,但每个类的实例数量非常少。
  • 我们希望在更改数据模型时尽量减少程序员的投入。

更多关于合适的用例的讨论,请参见:

  1. Wikipedia - 适合 EAV 建模的场景
  2. StackOverflow - 实体属性值数据库 vs 严格关系模型电子商务
  3. WikiWikiWeb - 通用数据模型

什么时候应该避免?

正如我们在开头部分所概述的,EAV 是一种权衡。它不应在以下情况下使用:

1. 系统的性能是关键
当数据以 EAV 形式存储时,以属性为中心的查询在本质上比以传统形式存储时更困难。[4]

一般来说,你的数据模型越是结构化,你就越能有效地处理它。因此,像 EAV 这样的松散数据存储在性能上有明显的折衷。具体来说,EAV 模型的应用使得在表中执行 JOIN 更加复杂。

2. 低复杂性/低维护成本是优先考虑的

EAV 通过将信息分割到不同的表中,使数据模型变得复杂。这增加了概念上的复杂性,以及查询数据所需的 SQL 语句。因此,某个领域的优化也会使系统更难理解和维护。

但是,必须指出:

EAV 设计应该只用于数据库中需要建模稀疏属性的子模式:即使在这里,它们也需要第三范式元数据表的支持。遇到稀疏属性的数据库设计问题相对较少:这就是为什么 EAV 设计适用的情况比较少。[1]

替代方案

在某些情况下,JSONB(二进制 JSON 数据)数据类型(Postgres 9.4+ 和其他 RDMS 中的类似数据)可以作为 EAV 的替代品。JSONB 支持索引,这就摊薄了性能折衷。重要的是要记住,JSONB 不是 RDMS 的解决方案,它有自己的问题,比如说类型化。

参考文献

[1] 探讨使用实体-属性-价值表示法组织的临床数据库的性能问题,https://doi.org/10.1136/jamia.2000.0070475

[2] EAV到底有什么不好?https://sqlblog.org/2009/11/19/what-is-so-bad-abou...

[3] Magento 开发者:第7部分:高级 ORM:实体属性值,https://devdocs.magento.com/guides/m1x/magefordev/...

[4] 实体-属性-值数据库的数据提取和即席查询,https://www.ncbi.nlm.nih.gov/pmc/articles/PMC61332...

编者注:

  • 属性的稀疏性:在数学和计算机科学中,如果一个对象只具有潜在的大量属性中的几个属性,我们称之为 "稀疏的矩阵"。在 EAV 模型时,使用术语 "稀疏" 来描述大多数没有值的属性。


喜歡:
0
去到頂部