繁体   English   中英

数据库 - 设计“事件”表

[英]Database - Designing an “Events” Table

在阅读了这篇伟大的Nettuts +文章的提示之后,我想出了一个表模式,该表模式将高度易变的数据与经过大量读取的其他表分开,同时降低了整个数据库模式中所需的表数,但是我我不确定这是不是一个好主意,因为它不符合规范化的规则,我想听听你的意见,这里是一般性的想法:


我有四种类型的用户在类表继承结构中建模,在主“用户”表中我存储了所有用户共有的数据( idusernamepassword ,几个flags ......)以及一些TIMESTAMP字段( date_createddate_updateddate_activateddate_lastLogin ,...)。

引用上面提到的Nettuts +文章中的#16提示:

示例2 :表中有“last_login”字段。 每次用户登录网站时它都会更新。 但是表上的每次更新都会导致刷新该表的查询缓存。 您可以将该字段放入另一个表中,以将用户表的更新保持在最低限度。

现在它变得更加棘手,我需要跟踪一些用户统计信息

  • 查看用户个人资料的唯一时间
  • 单击特定类型用户广告的 唯一时间
  • 查看特定类型用户帖子的 唯一时间
  • 等等...

在我完全规范化的数据库中,这增加了大约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表中的主键( id )字段,我相信其余的应该非常简单。 我在这个设计中喜欢的一件事是,我可以跟踪所有用户登录而不是最后一个用户登录,从而生成一些有趣的指标与该数据。

由于events表的性质不断增长,我还考虑进行一些优化,例如:

  • #9 :由于只有有限数量的表和有限(和预定)数量的事件,因此可以将TABLEEVENTS列设置为ENUM而不是VARCHAR以节省一些空间。
  • #14 :使用INET_ATON()而不是VARCHAR IP存储UNSIGNED INT
  • DATE存储TIMESTAMP而不是DATETIME
  • 使用ARCHIVECSV )引擎代替InnoDB / MyISAM
    • 仅支持INSERTSELECT ,并且动态压缩数据。

总的来说,每个事件只会消耗14个(未压缩的)字节,这对我的流量来说是可以接受的。

优点:

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

缺点:

  • 非关系型(仍然不如EAV):
    • SELECT * FROM events WHERE id = 2 AND table = 'user' ORDER BY date DESC();
  • 每个事件6个字节的开销( IDTABLEEVENT )。

我更倾向于采用这种方法,因为专业人士似乎远远超过缺点,但我仍然有点不情愿...... 我错过了什么? 你对此有何看法?

谢谢!


@coolgeek:

我做的一件事略有不同,就是维护一个entity_type表,并在object_type列中使用它的ID(在你的情况下,就是'TABLE'列)。 您可能希望使用event_type表执行相同的操作。

为了清楚起见,你的意思是我应该添加一个额外的表来映射表中允许哪些事件,并在事件表中使用该表的PK而不是具有TABLE / EVENT对?


@ben:

这些都是从现有数据中得出的统计数据,不是吗?

附加表主要与统计相关,但我的数据尚不存在,例如:

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

如果我放弃这些表格,我无法跟踪谁,什么或何时,不确定视图如何在这里提供帮助。

我同意它应该是分开的,但更多的是因为它是根本不同的数据。 有人是谁,有人做了两件事。 我不认为波动性如此重要。

我已经听过两种方式,我在MySQL手册中找不到任何一种说法是正确的。 无论如何,我同意你的看法,它们应该是分开的表格,因为它们代表了各种数据(除了常规方法之外,它还具有更多的描述性)。

可以这么说,我觉得你错过了树林。

您的表的谓词将是“在DATE EVENTed to TABLE时从IP IP发送的用户ID”,这似乎是合理的,但存在问题。

我的意思是“没有EAV那么糟糕”是所有记录都遵循线性结构,并且它们很容易查询,没有层次结构,因此所有查询都可以通过简单的SELECT来完成。

关于你的第二个陈述,我想你在这里理解我错了; IP地址不一定与用户相关联。 表结构应该是这样的:

IP地址( IP )在日期( DATE )对表( TABLE )的PK( ID )执行了某些操作( EVENT )。

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

您仍然可以将用户事件加入用户,但无法实现外键约束。

的确,这是我的主要关注点。 但是我并不完全确定这种设计会出现什么问题,传统的关系设计不会出错。 我可以发现一些警告,但只要应用程序搞乱数据库就知道它在做什么,我猜应该没有任何问题。

在这个论点中另一个重要的事情是,我将存储更多的事件,并且每个事件将比原始设计多一倍,在这里使用ARCHIVE存储引擎是完全合理的,唯一的事情就是它没有t支持FK (既不是UPDATE也不是DELETE )。

我强烈推荐这种方法。 由于您可能在OLTP和OLAP中使用相同的数据库,因此可以通过添加一些星星和雪花来获得显着的性能优势。

我有一个社交网络应用程序,目前在65桌。 我维护一个表来跟踪对象(博客/帖子,论坛/帖子,图库/相册/图像等)视图,另一个用于对象推荐,第三个表用于汇总十几个其他表中的插入/更新活动。

我做的一件事略有不同,就是维护一个entity_type表,并在object_type列中使用它的ID(在你的情况下,就是'TABLE'列)。 您可能希望使用event_type表执行相同的操作。

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

id
object_id
event_id
event_time
ip_address

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

在我完全规范化的数据库中,这增加了大约8到10个额外的表

这些都是从现有数据中得出的统计数据,不是吗? 更新 :好的,他们不是,所以无视以下。)为什么这些只是视图,甚至是物化视图?

但是,收集这些统计数据似乎是一种缓慢的操作:

  • 适当的索引可以使它非常快
  • 这不是一个常见的操作,所以速度并不重要
  • 消除冗余数据可能会使其他常见操作快速可靠

我想出了一个表模式,它可以将高度易变的数据与遭受重读的其他表分开

我想你在谈论用户(只是选择一个表)的事件,这些事件很不稳定,与用户数据分开。 我同意它应该是分开的,但更多的是因为它是根本不同的数据。 有人是谁,有人做了两件事。

我不认为波动性如此重要。 DBMS应该已经允许您将日志文件和数据库文件放在不同的设备上,这样就完成了同样的事情,并且争用不应该是行级锁定的问题。

非关系型(仍然不如EAV)

可以这么说,我觉得你错过了树林。

您的表的谓词将是“在DATE EVENTed to TABLE时从IP IP发送的用户ID”,这似乎是合理的,但存在问题。 (更新:好的,所以它有点像那样。)

您仍然可以将用户事件加入用户,但无法实现外键约束。 就是 EAV通常存在问题的原因 ; 某些东西是否恰好是EAV并不重要。 在您的架构中实现约束通常是一行或两行代码,但在您的应用中,它可能是几十行代码,如果多个应用在多个位置访问相同的数据,它可以很容易地成倍增加到数千个代码行。 因此,通常,如果您可以使用外键约束来防止错误数据,那么您可以保证没有应用程序会这样做。

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

进一步评论

我可以发现一些警告,但只要应用程序搞乱数据库就知道它在做什么,我猜应该没有任何问题。

有一些警告你可以成为一个非常成功的系统。 使用适当的约束系统,您可以说,“如果任何混淆数据库的应用程序不知道它在做什么,DBMS将标记错误。” 这可能需要比你更多的时间和金钱,所以你可以拥有的更简单的东西可能比你不能的更完美的东西更好。 这就是生活。

我无法对Ben的回答添加评论,所以有两件事......

首先,在独立的OLAP / DSS数据库中使用视图是一回事; 在事务数据库中使用它们是另一回事。 高性能MySQL人员建议不要使用性能重要的视图

WRT数据完整性,我同意,这是使用带有'events'作为中心事实表的星形或雪花的另一个优点(以及像我一样使用多个事件表)。 但是您无法围绕IP地址设计参照完整性方案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM