元表 VS 具有许多字段的表,规模很大。性能方面
Posted
技术标签:
【中文标题】元表 VS 具有许多字段的表,规模很大。性能方面【英文标题】:Meta table VS table with many fields, on a large scale. Performance-wise 【发布时间】:2012-06-10 18:43:47 【问题描述】:这是一个具体的例子:
Wordpress 将用户信息(元)存储在一个名为 wp_usermeta 的表中,您可以在其中获取元键字段(例如:名字)和元值(约翰)
但是,仅在 50 个左右的用户之后,该表已经打包了大约 1219 条记录。
所以,我的问题是:从性能角度来看,将所有元数据作为一个字段的表,还是像 WordPress 那样将所有元数据作为一行的表更好?
在这两种情况下都正确设置了索引。几乎不需要添加新的元数据。请记住,像 wp_usermeta 这样的表必须使用文本/长文本字段类型(大占用空间)才能容纳可以输入的任何类型的数据。
我的假设是,只有当您不知道用户可能需要什么时,WordPress 方法才有效。否则:
检索所有元数据需要更多 I/O,因为字段未存储在单行中。该字段未优化。 如果没有重大缺陷(索引长文本?除非它是部分索引...但是那么,要多长时间?),您不能真正在 meta_value 字段上建立索引。 很快,您的数据库就会变得杂乱无章,即使是最精确的元数据也会诅咒您的研究 不存在对开发人员友好的情况。您无法真正发出加入请求来获得所需的一切并正确显示。不过,我可能遗漏了一点。我不是数据库工程师,我只知道 SQL 的基础知识。
【问题讨论】:
【参考方案1】:你说的是实体-属性-值。
- Entity = User, in your Wordpress Example
- Attribute = 'First Name', 'Last Name', etc
- Value = 'John', 'Smith', etc
这样的模式非常擅长为任何给定的实体提供动态数量的属性。您无需更改架构即可添加属性。根据查询的不同,通常可以在不更改任何 SQL 的情况下使用新属性。
只要您知道要查找的实体和属性,检索这些属性值的速度也非常快。这只是一种花哨的 Key-Value-Pair 类型的设置。
但是,如果您需要根据 Value 内容搜索记录,则不是很好。如,get me all users called 'John Smith'
。用英语问很简单。对“正常”表进行编码很简单; first_name = 'John' AND last_name = 'Smith'
。但是用 SQL 编写对抗 EAV 并不简单,而且相对性能很差; (获取所有的约翰,然后是所有的史密斯,然后将它们相交以获得匹配两者的实体。)
网上关于EAV的说法很多,这里就不赘述了。但一般的经验法则是:如果你能避免它,你可能应该这样做。
【讨论】:
【参考方案2】:取决于平均打包到 wp_usermeta 中的名称数量。
文本字段搜索速度非常慢。索引通常更快。
但是一些数据仓库索引每个字段的废话,而 Wordpress 可能也在做同样的事情。
我会投票支持 meta 作为一个字段而不是一行。
好的 SQL,晚安。 迈克
【讨论】:
【参考方案3】:来自 GPL 领域的两个主要软件的示例将说明两种设计之间的差异有多大:
Wordpress 和电子商务
两者都有自己的缺点和优势,并且都在各自的领域占据着巨大的主导地位,并且已经完成了很多事情。但是它们之间的根本和最大的区别之一是它们的数据库表设计方法。当然,在比较它们时,它们的代码架构也会影响它们的搜索速度,但两者都受到自身缺点的阻碍和自身优势的提升,因此对于生产环境的比较或多或少是准确的。
Wordpress 使用 EAV。一般数据(称为具有不同帖子类型的帖子)作为主要实体存储,其他所有数据都存储在帖子元表中。一些基本数据存储在主表中,如修订、帖子类型等,但几乎所有其余数据都存储在元数据中。
非常适合添加、修改数据和功能。
但是尝试使用复杂的 SQL 连接进行搜索,该连接需要从元表中提取 3-4 个不同的值并获得结果集。它是一条铁狗。根据您要搜索的数据,搜索结果非常慢。
这就是为什么您看不到许多需要托管复杂数据的 wordpress 插件的原因之一,而实际需要托管的插件往往会创建自己的表。
另一方面,oScommerce 将几乎所有与产品相关的数据都保存在产品表中。大多数 oScommerce 模组都会修改此表并添加其字段。有 products_attribute 表,但是这也相当扁平,并且没有任何元设计。它只是通过产品 ID 链接到产品。
因此,尽管 oScommerce 是很久以前的老式意大利面条代码,但即使在您搜索复杂且组合的产品标准时,它也会提供惊人的快速搜索结果。实际上,大部分 oScommerce 的正常显示代码(就像它在产品详细信息页面中显示的那样)来自相当复杂的 SQL,它们通过复杂的连接语句从大约 2-3 个表中提取数据。即使只有一个连接,相对简单得多的 sql 也可以使 wordpress 与数据库一决高下。
因此得出的结论相当简单:EAV 非常适合轻松扩展和修改数据以实现扩展功能(例如在 wordpress 中)。扁平的大单片表更适合用于表示复杂记录的数据库,并且会在其上运行具有多个条件的复杂搜索。
这是一个应用问题。
【讨论】:
【参考方案4】:据我所知,EAV 模型不会影响性能。除非您需要空值。在这种情况下,您应该与包含所有 type_meta 的表进行连接。
我不同意 Dems 的回答。
如果您想创建用户的全名,您不必询问与该名称匹配的每个名称。
为此,您应该使用第 5 或第 6 个 NF。
或者您甚至可能拥有一个用户实体表:
身份证 用户名 密码 盐然后就可以了。这是基础,对于所有用户“额外”数据,您应该有一个 user_meta 和 user_type_meta 实体。然后是用户。
【讨论】:
哇,在这里复活 :) 您能否在回答中说明如何找到所有符合 both 条件的实体?属性first_name
是'John'
,属性last_name
是'smith'
。不使用我描述的过程?
我不明白在代码中查找每个 John Smith 的目的,但您应该先搜索所有 John,然后搜索所有 Smith,然后再将它们相交。更像是找到所有的约翰然后弹出谁不是史密斯。
喜欢:$users = $repoUser->findBy(array('firstName' => 'John')); foreach($users as $user) if($user->getLastName() == 'Smith') $johnSmiths[] = $user;
1) 这不是 SQL。 2)交集描述是我在回答中描述的方法。 3) 在 EAV 中实现交集的性能显着低于具有两个字段和这两个字段上的复合索引的单个表。
再一次,我不同意您调用同一张表 2 次的方法是解决方案。这就像只调用一次该表,并使用您已经拥有的数据处理那些没有姓氏的数据。当然,将 2 个属性放在同一行中会更快。如果您对它们进行索引,它甚至会更快,使用 NoSQL 甚至会更快。但是,回到我的观点,不要分成 2 个字段,只需使用一个。称为用户名。并且不要把它放在 metas 表中,把它放在密码所在的 user 表中。以上是关于元表 VS 具有许多字段的表,规模很大。性能方面的主要内容,如果未能解决你的问题,请参考以下文章
Snowflake 多集群仓库性能 vs 大仓库规模的单仓库