1

在我的 Rails 应用程序中,我有各种包含用户数据的数据库表。其中一些表有很多行(在某些情况下每个用户多达 500,000 行)并且经常被查询。每当我查询任何表的任何内容时,当前用户的 user_id 都在查询中的某个位置 - 如果表与用户有直接关系,或者通过连接,如果它们通过其他一些表相关,则可以直接使用。

我是否应该对 user_id 进行非规范化并将其包含在每个表中,以获得更快的性能?


这是一个例子:

  • 地址属于用户,并且有一个user_id
  • 信封属于用户,并且有一个 user_id
  • AddressesEnvelopes 连接了地址和信封,因此它有信封_id 和地址_id——它没有用户id,但可以通过信封或地址(必须属于同一用户)到达它。

一个常见的昂贵查询是为特定用户选择所有 AddressesEnvelopes,我可以通过加入 Address 或 Envelope 来完成,即使我不需要这些表中的任何内容。或者我可以在此表中复制用户 ID。


这是一个不同的场景:

  • 字母属于用户,并且有一个user_id
  • Recepient 属于 Letter,并且有一个 letter_id
  • RecepientOption 属于 Recepient,并且有一个recepient_id

在 Recepient 和 RecepientOption 中复制 user_id 是否有意义,即使我总是可以通过关联,通过 Letter 来获得它?


一些注意事项:

  • 用户之间永远不会共享任何对象。相关对象的整个层次结构始终属于同一用户。
  • 对象的用户所有者永远不会改变。
  • 数据库性能很重要,因为它是一个数据密集型应用程序。有许多查询和许多表。

那么我应该在每个表中包含 user_id 以便在创建索引时使用它吗?或者那会是糟糕的设计?

4

3 回答 3

2

我想指出,如果您愿意使用复合主键,则没有必要进行非规范化。AddressEnvelop 案例示例:

user(
    #user_id
)
address(
    #user_id
,   #addres_num
)
envelope(
    #user_id
,   #envelope_num
)
address_envelope(
    #user_id
,   #addres_num
,   #envelope_num
)

(# 表示主键列)

如果可以避免的话,我不是这种设计的粉丝,但考虑到您说所有这些对象都与用户相关联,这种类型的设计将使您的数据分区相对简单(逻辑上,放置范围使用多个数据库甚至机器的不同表中或物理上的用户)

对这种设计有意义的另一件事是使用聚集索引(在 MySQL 中,InnoDB 表的主键是从聚集索引构建的)。如果您确保 user_id 始终是索引中的第一列,它将确保对于每个表,一个用户的所有数据都紧密地存储在磁盘上。当您始终按 user_id 查询时,这很好,但如果您按另一个对象查询,它可能会损害性能(在这种情况下,像您建议的重复可能是更好的解决方案)

无论如何,在您更改设计之前,首先确保您的架构已经优化,并且您的外键列上有正确的索引。如果性能真的是最重要的,您应该简单地尝试几种解决方案并进行基准测试。

于 2009-12-24T10:39:40.877 回答
1

只要你

a) 获得可衡量的性能改进

b) 知道数据库的哪些部分是真正的规范化数据,哪些是冗余改进

没有理由不这样做!

于 2009-12-24T10:28:29.540 回答
1

你真的有衡量的性能问题吗?500 000 行不是很大的表。如果它们不是很复杂并且您的列上有适当的索引,那么您的选择应该是合理的快速。

我会先看看是否有慢查询,并尝试使用索引来优化它们。如果这还不够,那么我会研究非规范化。

如果您无法通过其他方式获得所需的性能,您建议的非规范化似乎是合理的。只要确保您保持非规范化字段是最新的。

于 2009-12-24T10:37:41.037 回答