【问题标题】:Proper way to create dynamic 1:M SQL table创建动态1:M SQL表的正确方法
【发布时间】:2014-03-17 12:13:29
【问题描述】:

简化示例:两张表 - 人员和时间。目标是跟踪一个人走过门口的所有时间。

一个人每天在时间表中可能有 0 到 50 个条目。

跟踪这些记录的正确和最有效的方法是什么?是吗

times table
-----------
person_id
timestamp

我担心这张表会很快获得超过一百万条记录。插入和检索时间至关重要。

另外:显然是非标准化的,但这样做会更好

times table
-----------
person_id
serialized_timestamps_for_the_day
date

我们需要访问此人的每个时间戳,但仅查询日期或此人 ID 的记录。

【问题讨论】:

  • 您的简化示例与问题的其余部分不匹配。要跟踪一个人走过门口的天数,您只需要 person_id 和日期,每天最多 1 条记录。
  • 一个人一天可以“穿过门口”多次吗?如果是,您是否需要分别跟踪所有这些事件?
  • @Brian 更新更清晰。需要跟踪人走过门的个人时间,但可以按日期分组。无论架构如何,都只会查询 person_id 和 date。
  • @BrankoDimitrijevic 一个人每天可以穿过门口 0 到 50 次。我们需要能够访问每个单独的时间戳,但只会查询有关 person_id 和/或日期的记录。因此,我们可以将所有一天的时间戳分组为 1 条记录,但该记录可能在 1 天内更新 50 次。

标签: sql performance database-design architecture one-to-many


【解决方案1】:

第二种方案有一些问题:

  • 由于您需要访问单个时间戳1,因此serialized_timestamps_for_the_day 不能被视为atomic 并且会违反1NF,从而导致bunch of problems
  • 除此之外,您还引入了冗余:date 可以从 serialized_timestamps_for_the_day 的内容中推断出来,并且您的应用程序代码需要确保它们永远不会“去同步”,这很容易受到错误。2

因此选择第一个解决方案。如果properly indexed,现代硬件上的现代数据库可以处理的不仅仅是“超过一百万条记录”。在这种特定情况下:

  • {person_id, timestamp} 上的复合索引将允许您通过简单的索引范围扫描来查询人员或人员和日期的组合,这非常有效。
  • 如果您只需要“按日期”查询,则需要 {timestamp} 上的索引。您可以通过搜索指定日期的 00:00 到 24:00 范围轻松搜索特定日期内的所有时间戳。

1 即使您不查询单个时间戳,您仍然需要将它们一一写入数据库。如果您有一个序列化的字段,您首先需要读取整个字段以仅附加一个值,然后将整个结果写回数据库,这可能会很快成为性能问题。还有其他问题,如上面链接中所述。

2 作为一般规则,不应存储可以推断的内容,除非有良好的性能理由这样做,我不这样做'在这里看不到任何东西。

【讨论】:

    【解决方案2】:

    考虑一下我们在这里谈论的是什么。仅考虑原始数据(event_time, user_id) 这将是每 1M 行的(4 + 4) * 1M ~ 8MB。让我们尝试在数据库中粗略估计一下。

    一个整数4字节,时间戳4字节;行标题,比如 18 个字节——这会将行大小的第一个估计值带到4 + 4 + 18 = 26 bytes。使用大约 0.7 的页面填充因子; ==> 26 / 0.7 ~ 37 每行字节数。

    因此,对于 1 M 行,大约 37 MB。您将需要(user_id, event_time) 上的索引,因此我们只需将原始索引翻倍为37 * 2 = 74 MB

    这使得非常粗略、不准确的估计值达到每 1M 行 74MB。

    因此,要始终将其保存在内存中,此表的每 1M 行需要 0.074 GB。

    为了获得更好的估计,只需创建一个表,添加索引并用几百万行填充它。

    鉴于预期的数据量,即使在笔记本电脑上也可以轻松地用 1000 万行测试这一切——测试总是胜过推测。

    附:您的选项 2 对我来说看起来“显然更好”

    【讨论】:

      【解决方案3】:

      我认为第一个选项会更好。

      即使您选择第二种选择,索引的大小也可能不会减少。实际上会有一个额外的列。

      并且不同用户的数据不相关,可以根据person_id对数据库进行分片。即假设您的数据不能放在单个数据库服务器节点上并且需要两个节点。然后一半用户的数据将存储在一个节点上,其余数据将存储在另一个节点上。

      这可以使用 MySQL 等 RDBMS 或 MongoDBOrientDB 等面向文档的数据库来完成。

      【讨论】:

        猜你喜欢
        • 2018-07-10
        • 2015-05-22
        • 2016-02-07
        • 2013-07-28
        • 2023-03-12
        • 2017-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多