在我的Rails应用程序中,我有各种包含用户数据的数据库表。其中一些表有很多行(在某些情况下每个用户多达500,000行)并经常查询。每当我查询任何表时,当前用户的user_id就在查询中的某个位置 - 直接,如果表与用户有直接关系,或者通过连接,如果它们通过其他表相关联。
我应该对user_id进行非规范化并将其包含在每个表中,以提高性能吗?
以下是一个例子:
一个常见的昂贵查询是选择特定用户的所有AddressesEnvelopes,我可以通过加入Address或Envelope来完成,即使我不需要这些表中的任何内容。或者我可以在此表中复制用户ID。
这是一个不同的场景:
在Recepient和RecepientOption中复制user_id是否有意义,即使我总是可以通过关联通过Letter来获取它?
一些注意事项:
那么我应该在每个表中包含user_id,以便在创建索引时使用它吗?或者那是不好的设计?
答案 0 :(得分: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查询时,这很好,但如果你用另一个对象查询它会损害性能(在这种情况下,像你想要的重复可能是一个更好的解决方案)
无论如何,在更改设计之前,首先要确保您的架构已经过优化,并且您的外键列上有适当的索引。如果性能确实至关重要,那么您应该尝试多种解决方案并进行基准测试。
答案 1 :(得分:1)
只要你
a)获得可衡量的绩效改善
和
b)知道数据库的哪些部分是真实的标准化数据,哪些是多余的改进
没有理由不这样做!
答案 2 :(得分:1)
您确实有测量的性能问题吗? 50万行表不是很大。如果选择不是很复杂并且列上有适当的索引,那么您的选择应该是合理的。
我首先会看到是否有慢查询并尝试使用索引优化它们。如果这还不够,那么我才会考虑非规范化。
如果您无法通过其他方式获得所需的性能,那么您建议的非规范化似乎是合理的。只需确保将非规范化字段保持最新。