代码之家  ›  专栏  ›  技术社区  ›  Alix Axel

数据库-设计“事件”表

  •  26
  • Alix Axel  · 技术社区  · 14 年前

    在阅读了来自 this great Nettuts+ article 我已经提出了一个表模式,它可以将高度不稳定的数据从其他需要大量读取的表中分离出来,同时降低整个数据库模式中所需的表的数量,但是我不确定这是否是一个好主意,因为它不遵循规范化的规则。我想听听你的建议,总的来说是这样的:


    我在一个 Class Table Inheritance 结构,在主“user”表中,我存储所有用户共有的数据( id , username , password 几个 flags ,…)还有一些 TIMESTAMP 字段( date_created , date_updated , date_activated , date_lastLogin ,……)

    引用上述nettuts+文章中的提示16:

    例2 :您有一个“最后登录名” 在你的桌子上。它每 用户登录网站的时间。 但是表上的每个更新都会导致 查询该表的缓存 脸红的你可以把那块地 另一个表来更新您的 用户表最小化。

    现在它变得更加棘手了,我需要跟踪一些用户统计数据,比如

    • 有多少 独特的 查看用户配置文件的次数
    • 有多少 独特的 时代A 从一个广告 特定类型的用户 被点击
    • 有多少 独特的 时代A 从一个帖子 特定类型的用户 被看见
    • 等等…

    在我的完全规范化的数据库中,这加起来大约有8到10个额外的表,虽然不多,但如果可能的话,我希望保持简单,所以我提出了以下建议” events 表:

    |------|----------------|----------------|---------------------|-----------|
    | ID   | TABLE          | EVENT          | DATE                | IP        | 
    |------|----------------|----------------|---------------------|-----------|
    | 1    | user           | login          | 2010-04-19 00:30:00 | 127.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    | 1    | user           | login          | 2010-04-19 02:30:00 | 127.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | created        | 2010-04-19 00:31:00 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | activated      | 2010-04-19 02:34:00 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | approved       | 2010-04-19 09:30:00 | 217.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | login          | 2010-04-19 12:00:00 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | created        | 2010-04-19 12:30:00 | 127.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | impressed      | 2010-04-19 12:31:00 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | clicked        | 2010-04-19 12:31:01 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | clicked        | 2010-04-19 12:31:02 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | clicked        | 2010-04-19 12:31:03 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | clicked        | 2010-04-19 12:31:04 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 15   | user_ads       | clicked        | 2010-04-19 12:31:05 | 127.0.0.2 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | blocked        | 2010-04-20 03:19:00 | 217.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    | 2    | user           | deleted        | 2010-04-20 03:20:00 | 217.0.0.1 |
    |------|----------------|----------------|---------------------|-----------|
    

    基本上 ID 指主键( 身份证件 )领域 TABLE 桌子,我相信其他的应该很简单。在这个设计中,我喜欢的一件事是,我可以跟踪所有用户登录,而不仅仅是最后一次登录,从而用这些数据生成一些有趣的度量。

    由于 事件 表我还考虑了一些优化,例如:

    • α9 :由于只有有限数量的表和有限(和预定)数量的事件, 桌子 EVENTS 列可以设置为 ENUM S而不是 VARCHAR 为了节省一些空间。
    • α14 商店 IP S AS UNSIGNED INT S 具有 INET_ATON() 而不是 瓦卡尔 S.
    • 商场 DATE S AS 时间戳 S 而不是 DATETIME S.
    • 使用 ARCHIVE ( CSV ? )发动机而不是 InnoDB / MyISAM .
      • 只有 INSERT S和 SELECT 支持,并动态压缩数据。

    总的来说,每个事件只需要14个字节(未压缩),这对我的流量来说是可以的。

    赞成的意见:

    • 能够存储更详细的数据(如登录)。
    • 不需要设计( 代码 )几乎有十几个附加表(日期和统计数据)。
    • 每个表减少几列,并保持易失性数据的分离。

    欺骗:

    • 非关系型(仍然没有eav差):
      • SELECT * FROM events WHERE id = 2 AND table = 'user' ORDER BY date DESC();
    • 每个事件6字节的开销( 身份证件 , EVENT )

    我更倾向于采用这种方法,因为利大于弊,但我还是有点不情愿… 我遗漏了什么吗?你对此有什么看法?

    谢谢!


    @酷客:

    有件事我会做 不同的是保持 实体类型表,并在 对象类型列(在您的情况下, “表”列)。你会想 对事件类型执行相同的操作 表。

    为了清楚起见,您的意思是我应该添加一个额外的表来映射表中允许的事件,并在事件表中使用该表的pk,而不是使用 桌子 / 事件 一对?


    本:

    这些都是从 现有的数据,不是吗?

    附加表主要与统计数据有关,但数据不存在,例如:

    user_ad_stats                          user_post_stats
    -------------                          ---------------
    user_ad_id (FK)                        user_post_id (FK)
    ip                                     ip
    date                                   date
    type (impressed, clicked)
    

    如果我删除这些表,我就无法跟踪谁、什么或什么时候,不知道视图在这里有什么帮助。

    我同意应该分开, 但更重要的是 不同的数据。什么样的人 有人做的是两件不同的事 东西。我不认为波动性是如此 重要的。

    我听过这两种说法,但在mysql手册中找不到任何一种说法是正确的。无论如何,我同意您的看法,它们应该是分开的表,因为它们表示各种类型的数据(另外一个好处是比常规方法更具描述性)。

    我想你错过了森林 可以说是树木。

    表的谓词是 “时间日期IP中的用户ID “似乎” 合理,但也有问题。

    我的意思是“不比eav差”是所有记录都遵循线性结构,而且它们很容易查询,没有层次结构,所以所有查询都可以用一个简单的 选择 .

    关于你的第二个陈述,我认为你在这里理解错了;IP地址不一定与用户相关。表结构应该如下所示:

    IP地址(IP地址) 知识产权 )做了些什么 ( 事件 )去PK( 身份证件 的 表(表) 桌子 )日期 日期 )

    例如,在我上面的示例的最后一行中,它应该读取IP 217.0.0.1(某些管理员),在2010-04-20 03:20:00删除用户2(其最后已知IP为127.0.0.2)。

    你仍然可以加入,比如说,用户事件 对于用户,但不能实现 外键约束。

    的确,这是我最关心的问题。但是我不完全确定这个设计会出什么问题,而传统的关系设计不会出什么问题。我可以找出一些注意事项,但只要搞乱数据库的应用程序知道它在做什么,我想应该不会有任何问题。

    在这个论点中,另一件重要的事情是,我将存储更多的事件,并且每个事件将比原始设计多出一倍,使用 档案文件 这里的存储引擎,唯一的问题是它不支持 FK (既不) UPDATE S或 DELETE s)。

    3 回复  |  直到 14 年前
        1
  •  4
  •   coolgeek    14 年前

    我强烈推荐这种方法。因为您可能正在为oltp和olap使用相同的数据库,所以通过添加一些星星和雪花可以获得显著的性能优势。

    我有一个社交网络应用程序,目前在65桌。我维护一个表来跟踪对象(blog/post、forum/thread、gallery/album/image等)视图,另一个表用于对象推荐,第三个表用于汇总十几个其他表中的插入/更新活动。

    我做的一件事稍有不同,那就是维护一个entity_type表,并在object_type列(在您的例子中是table列)中使用它的id。您可能希望对事件类型表执行相同的操作。

    澄清alix -是的,您为对象维护一个引用表,为事件维护一个引用表(这将是您的维度表)。事实表将包含以下字段:

    id
    object_id
    event_id
    event_time
    ip_address
    
        2
  •  3
  •   ben    14 年前

    这看起来是一个相当合理的设计,所以我只是想挑战你的一些假设,以确保你有具体的理由做你正在做的事情。

    在我完全规范化的数据库中 加起来大约8到10个 桌子

    这些都是从现有数据中得到的统计数据,不是吗?( 更新 :好吧,它们不是,所以忽略下面的内容。)为什么这些不只是视图,甚至是具体化视图?

    然而,收集这些统计数据似乎是一项缓慢的工作:

    • 适当的索引可以使它相当快
    • 这不是一般的手术,所以速度也没那么重要
    • 消除冗余数据可能会使其他常见操作快速可靠

    我想出了一个表模式 将分离高度不稳定的数据 从其他受重压的桌子上 读数

    我想你是在说用户(只需选择一个表)事件是如何从用户数据中分离出来的,而这些事件是非常不稳定的。我同意它应该是分开的,但更多的是因为它是根本不同的数据。一个人是什么和一个人做什么是两件不同的事情。

    我不认为波动性那么重要。dbms应该已经允许您将日志文件和数据库文件放在不同的设备上,这就完成了相同的任务,而争用不应该是行级锁定的问题。

    非关系型(仍然没有 EAV)

    可以说,我想你是因为树木而错过了森林。

    表的谓词是“从IP IP到时间日期事件到表的用户ID”,这似乎是合理的,但也存在一些问题。(更新:好吧,就是这样的。)

    您仍然可以将用户事件连接到用户,例如,用户事件,但不能实现外键约束。那是 为什么? eav通常是有问题的;究竟是不是eav并不重要。在架构中实现约束通常需要一两行代码,但在应用程序中可能需要几十行代码,如果多个应用程序在多个位置访问同一数据,则可以轻松地增加到数千行代码。所以,一般来说,如果你能用外键约束来防止坏数据,你就可以保证没有应用程序会这么做。

    你可能认为事件并不那么重要,但作为一个例子,广告印象就是金钱。我绝对希望在设计过程中尽早发现与广告印象相关的任何错误。

    进一步评论

    我可以找出一些注意事项,但只要 这个应用程序搞乱了数据库 知道它在做什么我想是的 应该没什么问题。

    有了一些警告,你就可以建立一个非常成功的系统。如果有一个适当的约束系统,你会说,“如果任何一个应用程序不知道它在做什么,数据库管理系统会标记一个错误。”这可能需要比你更多的时间和金钱,所以你可以拥有的更简单的东西可能比你拥有的更完美的东西要好。你不能。去拉维。

        3
  •  0
  •   coolgeek    14 年前

    我不能对本的回答加评论,所以有两件事…

    首先,在独立的olap/dss数据库中使用视图是一回事;在事务数据库中使用视图是另一回事。高性能的mysql用户 recommend against using views 表现重要的地方

    我同意wrt数据完整性,这是使用带有“event s”的star或snowflake作为中心事实表(以及像我一样使用多个事件表)的另一个优势。但不能围绕IP地址设计引用完整性方案