道歉,如果过去已经彻底涵盖了这一点 - 我已经看过一些相关的帖子,但没有找到任何令我满意的特定情况。
我最近一直在寻找一个相对简单的游戏,大约有1万名玩家。在游戏中,你可以捕捉和繁殖具有某些属性的宠物(即翅膀,角,鬃毛)。目前数据库中有一个表格如下所示:
-------------------------------------------------------------------------------
| pet_id | wings1 | wings1_hex | wings2 | wings2_hex | horns1 | horns1_hex | ...
-------------------------------------------------------------------------------
| 1 | 1 | ffffff | NULL | NULL | 2 | 000000 | ...
| 2 | NULL | NULL | NULL | NULL | NULL | NULL | ...
| 3 | 2 | ff0000 | 1 | ffffff | 3 | 00ff00 | ...
| 4 | NULL | NULL | NULL | NULL | 1 | 0000ff | ...
etc...
这个表继续这样,目前有100多列,但一般来说,一只宠物只有大约1-8个属性。每1-2个月添加一个新属性,这需要添加表列。该表很少更新并经常阅读。
我一直在提议我们采用更垂直的设计方案以提高灵活性,因为我们希望将来开始添加更多的属性,即:
----------------------------------------------------------------
| pet_id | attribute_id | attribute_color | attribute_position |
----------------------------------------------------------------
| 1 | 1 | ffffff | 1 |
| 1 | 3 | 000000 | 2 |
| 3 | 2 | ffffff | 1 |
| 3 | 1 | ff0000 | 2 |
| 3 | 3 | 00ff00 | 3 |
| 4 | 3 | 0000ff | 1 |
etc...
旧开发人员担心这会产生性能问题,因为用户经常搜索具有特定属性的宠物(即必须具有这些属性,必须至少有一个此颜色或位置,必须具有> 30个属性) 。目前搜索速度非常快,因为不需要JOINS,但引入垂直表可能意味着搜索的每个属性都会有一个额外的连接,也会使行数增加三倍。
我的问题的第一部分是,是否有人对此提出任何建议?我对数据库设计或优化并不是特别熟悉。
我已经针对各种情况进行了测试,但它们在很大程度上尚无定论 - 我运行的所有查询(即半秒到20秒之间)的时间变化非常大,所以我想我的问题的第二部分是,在PHP中使用microtime(true)是否有更可靠的分析查询时间的方法。
感谢。
答案 0 :(得分:17)
这称为Entity-Attribute-Value-Model,关系数据库系统根本不适合它。
引用认为是五个errors not to make之一的人:
那么为EAV吹捧的好处是什么?好吧,没有。由于EAV表将包含任何类型的数据,因此我们必须使用适当的列将数据PIVOT到表格表示,以使其有用。在许多情况下,有中间件或客户端软件在幕后执行此操作,从而为用户提供他们处理精心设计的数据的错觉。
EAV模型存在许多问题。
首先,大量数据本身基本上无法管理。
其次,没有可能的方法来定义必要的约束 - 任何潜在的检查约束都必须包括对适当属性名称的广泛硬编码。由于单个列包含所有可能的值,因此数据类型通常为VARCHAR(n)。
第三,甚至不要考虑使用任何有用的外键。
最后,查询的复杂性和尴尬性。有些人认为在必要时能够将各种数据插入单个表中是有益的 - 他们称之为“可扩展”。实际上,由于EAV将数据与元数据混合在一起,即使是出于简单的要求,操作数据也要困难得多。
EAV噩梦的解决方案很简单:分析和研究用户的需求并预先确定数据需求。关系数据库维护数据的完整性和一致性。如果没有明确定义的要求,设计这样的数据库几乎是不可能的。周期。
这个表继续这样,目前有100多列,但一般来说,一只宠物只有大约1-8个属性。
看起来像规范化的情况:将表分成多个,例如一个用于喇叭,一个用于翅膀,所有都通过外键连接到主实体表。但是请确保每个属性仍然映射到一个或多个列,以便您可以定义约束,数据类型,索引等。
答案 1 :(得分:0)
加入。该数据库专门用于支持用例的连接。如果有任何疑问,那么基准。
编辑:分析查询的更好方法是直接在CLI上的MySQL解释器中运行查询。它将为您提供运行查询所需的确切时间。 PHP microtime()函数还将引入其他延迟(Apache,PHP,服务器资源分配,网络,如果连接到远程MySQL实例等)。
答案 2 :(得分:0)
您提出的建议称为“normalization”。这正是关系数据库的用途 - 如果您处理索引,连接的运行速度几乎与数据在一个表中的速度一样快。
实际上,它们甚至可能更快:您可以只加载所需的列,而不是加载1个包含100列的表格行。如果宠物只有8个属性,则只加载8个。
答案 3 :(得分:0)
这个问题非常主观。如果您有资源更新中间件以反映已添加的列,那么无论如何,使用水平方向,没有什么比固定结构更安全,更容易学习。有一点需要记住,无论何时更新表结构,您都必须更新其中的每个依赖项,除非有一些类似*的全部内容,我建议您留意,除非您只是将数据转储到屏幕并且列的顺序是无关紧要的。
话虽如此,如果您没有满足所有要求或者不想在n个区域更新代码,Verticle就是您的选择。大多数情况下,您只需要存储容器来存储数据。我会在单独的列中分隔数字,日期,二进制和文本等内容以保留一些数据完整性,但Verticle存储没有任何问题,只要您知道如何制定和构造查询以将数据恢复到适当的位置。格式。
仅供参考,Wordpress使用Verticle数据存储来存储它必须存储的大部分动态内容,以用于数百万用途。
答案 4 :(得分:-2)
从数据库的角度来看,第一件事就是你的数据应该垂直增长而不是横向增长。因此,添加新列并不是一个好的设计。第二,这是数据库设计中非常常见的情况。要解决这个问题的方法,你必须创建三个表。第一个是Pets,第二个是属性,第三个是两个之间的映射表。这是一个例子:
表1(宠物)
Pet_ID | Pet_Name
1 |狗
2 |猫
表2(属性)
Attribute_ID | Attribute_Name
1 |翅膀
2 |眼睛
表3(Pet_Attribute)
Pet_ID | Attribute_ID | Attribute_Value
1 | 1 | 0
1 | 2 | 2
关于效果
Pet_ID和Attribute_ID是索引的主键(http://developer.mimer.com/documentation/html_92/Mimer_SQL_Engine_DocSet/Basic_concepts4.html),因此搜索速度非常快。这是解决问题的正确方法。希望,现在你会很清楚。