不使用外键的性能影响(许多专用 1:许多键-键表与非 fk 通用键-键表)

ouc*_*cil 2 mysql innodb index join polymorphic-associations

我们使用大量关联表来管理系统中各种不同对象之间的一对多关系。

为了说明这个问题,举两个例子:

  1. users, events, ass_users_events. 在ass_users_events将只包含User_IDEvent_ID列,都与外键关系。
  2. projects, tasks, ass_projects_tasks. 在ass_projects_tasks将只包含Project_IDTask_ID列,都与外键关系。

NB1:每个对象表实际上都使用了一个自动递增整数主键和一个带有唯一索引的 UUID 列的组合,该索引是实际的记录 ID。出于这个问题的目的,我们仅使用 UUID,因此不会发生冲突。

NB2:我们使用这种格式而不是直接的外键列/索引的原因是实际情况比这个例子复杂得多,许多不同的表有许多不同的连接,我们不希望 ORM 做很多每次加载记录时进行不必要的工作。

问题是我们开始为系统中的几乎所有新对象类型为系统中的许多其他现有对象创建这些关联表,从长远来看,这似乎不可持续,我们最终会数百种关联类型。

我们正在考虑的一个潜在解决方案是摆脱所有当前的关联表,而是创建一个具有以下结构的表:obj_1_id, obj_1_type, obj_2_id, obj_2_type。每列都将被索引,可能作为复合索引(即INDEX object_1 (obj_1_type,obj_1_id)INDEX object_2 (obj_2_type,obj_2_id))。

上面屁股表中的示例将变为:abc,user,123,event, 和def,project,456,task

该解决方案使我们能够灵活地在不同对象之间构建任意数量的关系类型,并且 ass 表上有足够的索引以保持性能。我的问题是,仅在连接期间使用索引与在较小的表中定义外键关系相比是否存在缺点,但可能有数百个?


编辑: 我认为下面有一些误解,所以这可能会澄清一些事情:

  1. 我使用多态对象结构,但每个对象都存储在它自己的表中,即用户、产品、类别、事件等。

  2. 至于提议的 ass 表,它将只有 4 个功能字段,具有非常简单的数据类型(加上一个ai_col作为主要数据类型)。typecols的数据类型为 varchar(10),id 的数据类型为CHAR(32) / BINARY(16)。SELECT 只会有一个连接到 ass 表,即

    SELECT event.* FROM event INNER JOIN ass ON ass.obj_1_type = 'event' AND ass.Object_ID_1 = event.Event_ID AND ass.obj_2_type='location'

我没有理由一次要求其中两个对象,我只会通过存在链接来过滤一张表中的结果。

  1. UUID 在这里是真正的唯一 ID,但是每个表都使用ai_col它是一个 INNODB 构造来提高其聚集索引的性能。

  2. 这是一个读取繁重的环境,我不太关心插入/删除性能。

  3. 我们使用 handlersocket 进行简单的读写查询,消除了 SQL 开销。

  4. 在此特定用例中,该表的完整性在最终一致时是可以接受的。


编辑 2:为了清楚地说明这一点,这就是为什么......如果我在自己的表中有 200 个对象,我希望能够以所有可能的方式链接,我要么必须有 199 个外键关系每个对象表,否则我必须创建 40,000 个表,其中包含所有可能的组合,然后是所有业务逻辑。使用我们正在考虑的方法,有一个表,我在应用程序级别处理了 RI,我表示最终一致是可以的。

Vér*_*ace 6

在我看来,您正在考虑 EAV(实体属性值)模型,即将每个对象嵌入一个表中,而不是为不同的对象设置不同的表。EAV 通常被认为是一种反模式

它可以被称为开放和/或灵活的模式。它也被(讽刺地)称为 OTLT(一个真正的查找表),(甚至更讽刺地)称为 MUCK(大规模统一代码密钥 - Joe Celko),也许最好的(我才刚刚发现这个)是恶魔(恶魔般的诱人)信息存储方法 - 改编自Mike Smithers)。

有趣的是,Celko(他是确定/编辑 SQL 标准的 ANSI/ISO SQL 委员会的成员)说(参见参考资料)关于编写这样一个系统的程序员,提问者应该“找到那个白痴对你做了这个,然后杀了他”。

有关针对 EAV 模式编写查询的困难,请参阅 Smithers 的帖子(以及 Celko在这里这里的著作)。Celko 将他的第一篇文章命名为“避免破坏的 EAV”并非巧合。看看这里的EAV系统的比尔Karwin绝对爆笑视觉类比-一本书“的德高望重的大师SQL和作者SQL反模式”。

您提到“在较小的表中定义了外键关系,但可能有数百个”。现代 RDBMS 可以处理数百个表和它们之间的 FK 关系,除非你有非常充分的理由(在你的论点背后有经过验证的测试),然后坚持传统的关系原则。

要直接回答你的问题,在所有的可能性中,查询的复杂性,你将需要使用获得的EAV系统数据结果将对FAR比的系统性能产生负面影响与清晰,正确定义FK很多表更使用 INT(11) 外键甚至 UUID 的关系。

关于性能的另一点 - 使用 EAV 系统将迫使您使用不适当的数据类型,这将严重干扰您的 RDBMS 查询优化器(错误...)优化查询的能力。在这些 EAV 系统下,许多值(再次不恰当地)存储为字符串并即时转换,这既耗时又使优化器感到困惑 -不推荐

从我在这里接受的答案,为了完整性和诚实性,仅作进一步说明。只有一个重要的系统使用 EAV 模型——那就是 Magento ( 1 , 2 )。它的主要利基是时尚行业,其中 EAV 模型可能适用于稀疏桌子(时尚单品往往有多种颜色、款式、尺寸……)。它很受欢迎(1 , 2),但 MySQL 也是如此,它在很多方面都不如 PostgreSQL、Firebird 和(除了多用户功能)SQLite。

我也在评论中注意到了这一点

但我从不完全依赖数据库的 RI,我通常在应用层使用 RI 作为备份。我已经考虑使用偶尔运行的维护任务来删除孤立记录来支持它。

这简直是​​疯了。

MySQL(和其他 RDBMS)的 RI(参照完整性)功能已经过数百万用户的测试。如果您在两个表之间建立了正确的 PK-FK 关系,那么这种失败的可能性几乎为零!你会远远更好使用你的时间和精力专注于您的应用程序比确保适当RI的其他领域-除非你犯的应用中实施RI,而不是使用RDBMS的能力的不可饶恕的罪过。见我的回答乔纳森·刘易斯的灾难二号这里。Lewis 写了这本书,所以他对数据库略知一二。