实体属性值数据库与严格的关系模型电子商务


136

可以肯定地说,EAV / CR数据库模型是错误的。那就是

问题:应该使用哪种数据库模型,技术或模式来处理描述可以在运行时更改的电子商务产品的属性“类”?

在一个良好的电子商务数据库中,您将存储选项的类别(例如电视分辨率,然后为每个电视都具有一个分辨率,但是下一个产品可能不是电视,并且没有“电视分辨率”)。您如何存储它们,有效搜索以及允许用户使用描述其产品的可变字段来设置产品类型?如果搜索引擎发现客户通常根据控制台深度搜索电视,则可以将控制台深度添加到字段中,然后在运行时为每种电视产品类型添加一个深度。

优秀的电子商务应用程序中有一个很好的通用功能,它们可以显示一组产品,然后具有“向下钻取”侧边菜单,您可以在其中看到“电视分辨率”作为标题,以及最常见的前五种电视分辨率。找到集。您单击一个,它仅显示该分辨率的电视,从而允许您通过在侧面菜单上选择其他类别来进一步向下钻取。这些选项将是在运行时添加的动态产品属性。

进一步讨论:

长话短说,互联网上是否有任何链接或模型描述可以“学术地”修复以下设置? 我感谢诺埃尔·肯尼迪(Noel Kennedy)提出了类别表,但需求可能更大。我在下面以另一种方式描述它,以强调其重要性。我可能需要进行视点校正以解决该问题,或者我可能需要更深入地研究EAV / CR。

喜欢对EAV / CR模型的积极回应。我的所有开发人员都说以下是Jeffrey Kemp谈到的内容:“新实体必须由专业人员建模和设计”(出于上下文考虑,请在下面阅读他的回答)。问题是:

  • 实体每周添加和删除属性
    (搜索关键字决定将来的属性)
  • 新实体每周到达
    (产品由零件组装)
  • 旧实体每周消失一次
    (存档,不受欢迎,季节性)

客户要为产品添加属性有两个原因:

  • 部门/关键字搜索/同类产品之间的比较表
  • 结帐前的消费类产品配置

这些属性必须具有重要性,而不仅仅是关键字搜索。如果他们想比较所有具有“奶油糖霜”的蛋糕,则可以单击蛋糕,单击生日主题,单击“奶油糖霜”,然后检查所有有趣的蛋糕(知道它们都带有奶油糖霜)。这不仅仅针对蛋糕,仅是示例。


您为什么不能只拥有一个带有外键引用其自身的“类别”表?
诺埃尔·肯尼迪

29
说EAV数据库模型不好是不安全也不精确的,因为它非常适合某些应用程序。
spencer7593

如果您使用Entity Framework 4中的父对象来装饰具有各种属性的各种对象,该怎么办?它如何持久化那些对象?
扎卡里·斯科特

1
刚才提到这篇关于一位顾问在基于EAV 极端版本的系统上的经验的出色文章。阅读!simple-talk.com/opinion/opinion-pieces/bad-carma
Jeffrey Kemp

1
EAV是一个非常可行的数据库模型。我正在像您一样解决类似的问题,而解决方案是EAV。我会推荐以下文章:sqlblog.com/blogs/aaron_bertrand/archive/2009/11/19/…–
Sandor

Answers:


75

我可以想到一些一般的利弊,在某些情况下,一个要比另一个好:

选项1,EAV型号:

  • 专业版:减少设计和开发简单应用程序的时间
  • 优点:易于添加的新实体(甚至可以由用户添加吗?)
  • Pro:“通用”界面组件
  • 缺点:验证简单数据类型所需的复杂代码
  • 缺点:简单报告的复杂SQL
  • 缺点:复杂的报告几乎变得不可能
  • 缺点:大型数据集的性能较差

选项2,分别为每个实体建模:

  • 缺点:收集需求和设计需要更多时间
  • 缺点:新实体必须由专业人员建模和设计
  • 缺点:每个实体的自定义界面组件
  • Pro:数据类型约束和验证易于实现
  • 优点:SQL易于编写,易于理解和调试
  • 优点:即使是最复杂的报告也相对简单
  • 专业版:大型数据集的最佳性能

选项3,组合(“适当”建模实体,但为某些/所有实体的自定义属性添加“扩展名”)

  • Pro / Con:收集需求和设计所需的时间比选项1多,但可能不及选项2多*
  • 缺点:新实体必须由专业人员建模和设计
  • 优点:以后可以轻松添加新属性
  • 缺点:验证简单数据类型(用于自定义属性)所需的复杂代码
  • 缺点:仍需要自定义接口组件,但自定义属性可能有通用接口组件
  • 缺点:一旦报表中包含任何自定义属性,SQL就会变得复杂
  • 缺点:通常性能良好,除非您开始需要根据自定义属性进行搜索或报告

* 我不确定方案3是否一定会在设计阶段节省任何时间。

就我个人而言,我倾向于方法2,并尽可能避免使用EAV。但是,在某些情况下,用户需要EAV随附的灵活性。但这要付出巨大的代价。


如果您有一个带有索引的文本值1-n的表,然后在C#中(在ram中)将所需内容映射到所需内容该怎么办。它仍将像EAV一样工作,但“匹配项”将是域模型。有点像序列化,但是您可以在索引文本字段上使用SQL选择。每个记录没有多个选择。所有的“成本”都发生在RAM中。
Zachary Scott

1
@Zim,听起来很像选项3。每行都有1-n个额外的“通用”列,并且存储在其中的数据在应用程序级别进行解释。将一条记录的所有数据都放在一个地方,可以提高性能。但是,与这些列有关的元数据需要存储在某个地方,这是成本增加的地方。当然,我们可以将元数据缓存在ram中,但是与直接在应用程序代码中对域进行建模相比,它的成本仍然更高。当然比完全成熟的EAV模型更好!
杰弗里·肯普

1
+10000好答案。如今,人们无视数据库设计和需求收集。他们宁愿编写一百倍的代码行,而这需要花时间来进行良好的设计。
图兰斯·科尔多瓦2014年

如果仅提供选项1的结构,则不需要为EAV选项(1)设计更多的关系选项(2),并且关系接口是从描述该结构的元数据通用的。这将删除所有选项2缺点。但是,您忘记了唯一的实际缺点:DDL管理表太慢。
philipxy 2015年

嗨@philipxy,我没有说“更多设计”。EAV的存在理由是(大概)系统设计师可以在设计模型上花费更少的时间,而将设计工作留给以后的“用户”使用(由于缺乏专业设计,导致选项1列出了缺点) 。如果EAV不能为设计者节省成本,那只会增加更多的燃料,从而无法控制EAV。另外,我也不同意DDL太慢-因为仅应很少使用DDL(例如,修复模型中的错误或实现新功能),因此DDL的性能应相对不重要。
杰弗里·肯普

63

可以肯定地说,EAV / CR数据库模型是错误的。

不,这不对。只是它们对关系数据库的使用效率低下。纯粹的键/值存储适用于此模型。

现在,您真正的问题是:如何存储各种属性并使它们可搜索?

只需使用EAV。在您的情况下,它将是一个额外的表。在属性名称和值上对其进行索引,大多数RDBM都会在属性名称重复项上使用前缀压缩来实现,从而使其真正快速且紧凑。

当您使用EAV / CR替换“真实”字段时,它会变得难看。与每种工具一样,过度使用它是“不好的”,并给它留下不好的印象。


所以问题是我为我的类别之一增加了15个字段,在eav模型中,它需要16个join +主表,因此要进行左左16个join以在3-4百万条记录中搜索产品(如果有客户的话还有16个)(一个人们出售二手产品的网站)那么它的性能较低吗?
babak faghihian

2
如果已经定义了这些“附加字段”,则绝对最好将其作为“真实字段”。当然,在大型查询中进行无数次的联接将造成沉重的负担(但仍然可以!)。在繁重的元数据项目中,我所做的是每个“主要项目”允许任意数量的“标签”(作为EAV记录),但是“大型查询”仅选择一些预定义的标签名,从而限制了连接的总数(目前典型的就是4个标签和大约5其他连接),并且当用户选择一个特定的项目,然后它去抓取相关的一切,但对于单个项目。
哈维尔

但是,当然,该特定系统当前正在移植到某个hstore字段(这正是我们使用PostgreSQL的原因之一)
Javier 2015年

15
//至此,我想花点时间与您谈谈Magento / Adobe PSD格式。
// Magento / PSD不是一个好的电子商务平台/ 格式。Magento / PSD甚至不是一个糟糕的电子商务平台/ 格式。称它为
//侮辱其他不良的电子商务平台/ 格式,例如Zencart或OsCommerce。不,Magento / PSD是一种糟糕的电子商务平台/ 格式。有
//现在已经为这个代码工作了几周了,我对Magento / PSD的厌恶情绪越来越大
//以一百万个太阳的强烈激情燃烧。

http://code.google.com/p/xee/source/browse/trunk/XeePhotoshopLoader.m?spec=svn28&r=11#107

内部模型充其量是古怪的,就像有人将模式放入一个令人讨厌的游戏中,将其密封并放置在一个油漆黑客中一样。

现实世界:我正在开发一个中间件实现应用程序,这是获取地址信息的查询之一。

CREATE OR REPLACE VIEW sales_flat_addresses AS
SELECT sales_order_entity.parent_id AS order_id, 
       sales_order_entity.entity_id, 
       CONCAT(CONCAT(UCASE(MID(sales_order_entity_varchar.value,1,1)),MID(sales_order_entity_varchar.value,2)), "Address") as type, 
       GROUP_CONCAT( 
         CONCAT( eav_attribute.attribute_code," ::::: ", sales_order_entity_varchar.value )
         ORDER BY sales_order_entity_varchar.value DESC
         SEPARATOR '!!!!!' 
       ) as data
  FROM sales_order_entity
       INNER JOIN sales_order_entity_varchar ON sales_order_entity_varchar.entity_id = sales_order_entity.entity_id
       INNER JOIN eav_attribute ON eav_attribute.attribute_id = sales_order_entity_varchar.attribute_id
   AND sales_order_entity.entity_type_id =12
 GROUP BY sales_order_entity.entity_id
 ORDER BY eav_attribute.attribute_code = 'address_type'

懒惰地确切显示订单的地址信息

-

摘要:仅在以下情况下使用Magento:

  1. 你被拿到一大笔钱
  2. 你必须
  3. 享受痛苦

这是一篇较旧的文章,但我希望我在3个月前为一个客户启动Magento项目时就已经发现了。+1令人难以置信/摇摇欲坠的比喻!
trevorc 2011年

1
有趣的是,magento似乎是电子商务系统中的王者。也许只是市场营销非常好
Herr

1
Magento并不是因为维护级别而流行,而是具有自定义功能,允许任何人无需更改架构或进行任何修改即可实现新功能。此功能需要付费。
Diego Mendes

如果您想避免FE和BE
遭受

15

我很惊讶没有人提到NoSQL数据库。

我从未在生产环境中实践过NoSQL(刚刚经过测试,对MongoDB印象深刻),但是NoSQL的全部目的是能够将具有不同属性的项目保存在同一“文档”中。


考虑到对MongoDB的写入需要数据库级别的锁定,这对于并发生产流量意味着什么。
Bill Karwin

考虑锁定持续时间约为微秒。
你好世界

12

在性能不是主要要求的地方(例如在ETL类型的应用程序中),EAV的另一个明显优势是:节省差异。

我已经实现了许多应用程序,其中最重要的要求是能够查看域对象从其第一个“版本”到其当前状态的历史记录。如果该域对象具有大量属性,则意味着每次更改都需要在其对应的表中插入新行(不是更新,因为会丢失历史记录,而是插入)。假设这个领域对象是一个Person,我有500k Persons进行跟踪,平均在Persons生命周期中,对各种属性的更改超过100次。再加上很少有只有一个主要域对象的应用程序这一事实,您很快就会发现数据库的大小将很快失去控制。

一个简单的解决方案是仅将差异更改保存到主要域对象,而不是重复保存冗余信息。

所有模型都会随着时间而变化,以反映新的业务需求。期。使用EAV只是我们工具箱中要使用的工具之一;但永远不要将其自动分类为“不良”。


2
+1表示“使用EAV只是我们工具箱中要使用的工具之一;但永远不要将其自动归类为“不良”。
Catchops

顺便说一句,这称为SCD(尺寸变化缓慢)。此外,对于具有此属性的属性,比特时间要求(类型4 SCD的特定情况)也要求EAV模式。请记住,99%的NoSQL没有本机联接,因此如果您需要使用这种类型的数据进行“实时”联接,则只有EAV可行。
Cowbert

3

我在同一个问题上挣扎。您可能对以下关于两个现有电子商务解决方案的讨论感兴趣:Magento(EAV)和Joomla(常规关系结构):https : //forum.virtuemart.net/index.php?topic=58686.0

看来,Magento的EAV性能是真正的突破。

这就是为什么我倾向于标准化的结构。为了克服缺乏灵活性的问题,我考虑在将来添加一些可以编辑的单独的数据字典(XML或单独的数据库表),并在此基础上,使用显示和比较具有新属性集的产品类别的应用程序代码与SQL脚本一起生成。

在这种情况下,这种架构似乎是最有效的选择-同时具有灵活性和高性能。

问题可能是在实时环境中频繁使用ALTER TABLE。我正在使用Postgres,因此它的MVCC和事务性DDL有望减轻痛苦。


2

我仍然投票支持在EAV的最低意义的原子级别上进行建模。让适合特定用户群体的标准,技术和应用程序来决定内容模型,属性,粒度的重复需求等。


2

如果只涉及产品目录属性,因此对这些属性的验证要求非常有限,则EAV的唯一真正缺点就是查询性能,即使当您的查询处理带有属性的多个“事物”(产品)时,这也是一个问题,查询“给我ID为234的产品的所有属性”的性能虽然不是最佳的,但仍然非常快。

一种解决方案是仅将SQL数据库/ EAV模型用于产品目录的admin / edit一侧,并进行一些处理以将产品反规范化为可搜索的内容。由于您已经具有属性,因此很有可能要进行刻面处理,因此可以使用Solr或ElasticSearch。这种方法基本上避免了EAV模型的所有缺点,并且增加的复杂性仅限于在更新时将完整的产品序列化为JSON。


2

EAV有很多缺点:

  1. 随着时间的推移性能下降一旦应用程序中的数据量增长到超过一定大小,该数据的检索和处理就可能变得越来越低效。
  2. SQL查询非常复杂且难以编写。
  3. 数据完整性问题。您不能为所有需要的字段定义外键。
  4. 您必须定义和维护自己的元数据。

1.大多数关系数据库也是如此。这就是为什么创建分片的原因。2.数据建模可能很复杂且难以实现。我花了数月至数月的时间来等待OLAP多维数据集架构更改。3.现在基本上已经在软件中完成了。4.无论如何,在对关系模式进行建模时,都必须在“ ERwin,Excel和Visio中”执行此操作。
Cowbert

1

我有一个略有不同的问题:我希望存储的内容更像电子表格,而不是稀疏值的许多属性(这可能是使用EAV的一个很好的理由)。工作表中的列可以更改,但是工作表中的所有单元格都将包含数据(不是稀疏的)。

我进行了少量测试以对两种设计进行基准测试:一种使用EAV,另一种使用Postgres ARRAY存储单元格数据。

电动汽车 在此处输入图片说明

数组 在此处输入图片说明

这两种模式在适当的列上都有索引,并且计划者使用这些索引。

事实证明,对于插入和查询而言,基于数组的架构要快一个数量级。从快速测试来看,似乎两者均呈线性比例。测试不是很彻底。欢迎提出建议和建议-它们已获得MIT许可。


您如何在数组模型的工作表列(即vlookup)上进行联接?您不必编写自己的数组合并排序功能吗?如果您使用单元格的sheet_id + x坐标+ y坐标作为单元格值的键,则高度怀疑它与预编译的合并排序一样好。(要模拟excel,请为x坐标预生成一个查询表,其中0-18278是A-ZZZ列(excel最大值为16384)),然后可以选择sheet_id = uuid和x-coord = 0且y-coord的值<1001以获取col A的前1000行
。– cowbert

@cowbert你是对的;实际上,我只是加载我感兴趣的列并在Python中进行联接。松弛!
z0r
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.