【问题标题】:In a database, when should you store derived data?在数据库中,什么时候应该存储派生数据?
【发布时间】:2011-05-06 20:08:10
【问题描述】:

我的问题是关于非规范化。在数据库中,何时应该将派生数据存储在自己的列中,而不是每次需要时都计算?

例如,假设您的用户因他们的问题而获得支持。您在其个人资料上显示用户的声誉。当用户被点赞时,您应该增加他们的声誉,还是应该在检索他们的个人资料时计算它:

SELECT User.id, COUNT(*) AS reputation FROM User
LEFT JOIN Question
  ON Question.User_id = User.id
LEFT JOIN Upvote
  ON Upvote.Question_id = Question.id
GROUP BY User.id

为了获得用户的声誉,查询的处理器密集程度必须达到多少,才值得用自己的列增量跟踪它?

继续我们的示例,假设 Upvote 的权重取决于投它的用户拥有多少 Upvote(而不是声誉)。检索他们的声誉的查询突然爆炸:

SELECT
  User.id AS User_id,
  SUM(UpvoteWeight.weight) AS reputation
FROM User
LEFT JOIN Question
  ON User.id = Question.User_id
LEFT JOIN (
  SELECT
    Upvote.Question_id,
    COUNT(Upvote2.id)+1 AS weight
  FROM Upvote
  LEFT JOIN User
    ON Upvote.User_id = User.id
  LEFT JOIN Question
    ON User.id = Question.User_id
  LEFT JOIN Upvote AS Upvote2
    ON
      Question.id = Upvote2.Question_id
      AND Upvote2.date < Upvote.date
  GROUP BY Upvote.id
) AS UpvoteWeight ON Question.id = UpvoteWeight.Question_id
GROUP BY User.id

这与增量解决方案的难度相去甚远。规范化何时值得,规范化的好处何时会失去非规范化的好处(在这种情况下是查询难度和/或性能)?

【问题讨论】:

    标签: mysql normalization


    【解决方案1】:

    确实没有明确的答案,因为它取决于很多因素,例如网站的数量以及您显示声誉的频率(即仅在他们的个人资料页面上或在其用户名的每个实例旁边,无处不在) .唯一真正的答案是“当它变得太慢时”;换句话说,您可能需要测试这两种情况并获得一些真实世界的性能统计数据。

    在这种特殊情况下,我个人会去规范化,并在 upvote 表上使用插入触发器或定期更新查询来更新去规范化的信誉列。 真的世界末日是不是有人的代表在页面刷新之前说“204”而不是“205”?

    【讨论】:

      【解决方案2】:

      获取用户声誉的查询必须占用多少处理器,才值得使用自己的列增量跟踪它?

      这里确实存在两个问题:(1)这种变化会提高性能,(2)性能提高值得付出努力吗?


      至于性能是否提升,这基本上是标准的利弊分析。

      归一化的好处基本上有两个:

      • 更轻松的数据完整性

      • 重新计算没有问题(例如,如果基础数据发生变化,则需要重新计算派生列)。

      如果您使用稳健实施的解决方案(例如触发器、仅 Sstored-proc 的数据更改以及撤销的直接表更改权限等)来涵盖数据完整性,那么这将成为验证成本是否的简单计算源数据更改是否需要重新计算派生数据与每次都重新计算派生数据。 (注意:保持数据完整性的另一种方法是强制按计划重新计算派生数据,在这种情况下,数据可以承受一定的时间容忍度不准确。StackExchange 采用这种方法及其一些数字)。

      在典型情况下(更多的数据检索而对基础数据的更改要少得多),数学很明显倾向于将非规范化的派生数据保留在表中。

      在一些罕见的情况下,基础数据经常更改,但派生数据却没有经常检索,这样做可能是有害的。


      现在,我们要解决更重要的问题:性能提升值得付出努力吗?

      请注意,与所有优化一样,最大的问题是“优化是否值得?”,因此需要考虑两个主要问题:

      1. 测量准确的性能差异并进行一般分析。

      2. 在您的系统大图中此特定优化的上下文。

      例如如果查询性能的差异——在优化时必须首先测量——缓存的派生数据和计算的数据之间的差异为 2%,那么实现信誉缓存列的额外系统复杂性可能不值得首先。但是,就边际改进而言,关心与不关心的门槛是多少取决于您的应用程序的整体情况。如果您可以采取措施在不同的地方将查询性能提高 10%,请专注于这一点而不是 2%。如果您是 Google,并且额外 2% 的查询性能需要 20 亿美元的额外硬件成本来承受,那么无论如何都需要对其进行优化。

      【讨论】:

        【解决方案3】:

        我只是想从另一个角度谈谈 DVK 在上面的回复中很好地涵盖的数据完整性问题。想想其他系统是否可能需要访问/计算派生数据——甚至像报告系统这样简单的东西。如果其他系统需要使用派生值或更新 upvote 值,那么您可能需要考虑如何重用计算代码或如何确保无论哪个系统更改 upvote 都始终更新派生值。

        【讨论】:

          猜你喜欢
          • 2017-12-28
          • 2021-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-24
          • 2013-04-25
          • 2011-01-22
          相关资源
          最近更新 更多