Ruby on rails 如何在数据库中组织基于时间的事件进行聚合(如访问)?

Ruby on rails 如何在数据库中组织基于时间的事件进行聚合(如访问)?,ruby-on-rails,database-design,Ruby On Rails,Database Design,我需要向我的用户提供关于他们页面的不同类型访问者的基于时间的统计数据,例如上周/上个月/总体的访问者数量,以及这些访问者可能来自哪里。理想情况下,数据将足够丰富,以便在可能的情况下进行扩展/定制/重新聚合 我的问题是如何最好地在数据库中组织它。一种选择是为每次访问创建一个新记录,其中包含访问者的id和日期 我怀疑这样一种方法在提供功能的同时,会占用大量的存储空间。有更好的方法吗?我会选择“每次访问都有一个新记录”。否则,编程将很复杂 根据站点的流量,您可以估计需要多少存储空间。这样做,您可能会决

我需要向我的用户提供关于他们页面的不同类型访问者的基于时间的统计数据,例如上周/上个月/总体的访问者数量,以及这些访问者可能来自哪里。理想情况下,数据将足够丰富,以便在可能的情况下进行扩展/定制/重新聚合

我的问题是如何最好地在数据库中组织它。一种选择是为每次访问创建一个新记录,其中包含访问者的id和日期

我怀疑这样一种方法在提供功能的同时,会占用大量的存储空间。有更好的方法吗?

我会选择“每次访问都有一个新记录”。否则,编程将很复杂

根据站点的流量,您可以估计需要多少存储空间。这样做,您可能会决定不担心存储问题,因为存储通常不是现代DBMS的问题


如果存储确实是一个问题,那么您需要事先决定(并修复)需要哪些统计数据。然后,您可以只记录一个月的每次访问。在每个月底,您将对当月进行总结,并将此总结添加到根据前几个月的统计数据编制的现有总结中

公认的行业惯例是有跟踪级表格和汇总级表格。跟踪数据表示事件的详细日志。即使存储不是一个问题,计数所需的时间也是一个问题。按小时查询访问计数比计算跟踪表的记录要快得多

假设您的跟踪级别表是

Trace = [datetime, userId, domain], ipaddr;
当您的流程插入跟踪表时,它还将创建/更新汇总表的记录

确定您希望累积事件的最佳时间粒度。可以是分钟、小时、天等

您还需要确定是希望按会话还是按页面请求计数。您是否会将页面上的重复访问视为多次访问?你必须确定所有这些分母

定义计数器:

class Counter{
  datetime start;
  int count;
}
假设您已确定按小时计算的粒度最适合站点上的流量

在整个站点上按会话累积
在小时开始时,计数器复位,开始日期时间设置为当前日期时间。每次发生的事件计数都会增加。在您的情况下,这将是一个新的会话事件

当下一个小时到达时,计数器数据将写入表中的新记录

ByHour = [start], count;
再次重置计数器,并将开始日期时间设置为当前日期时间

您现在拥有的是一个按小时累计新访问事件的表。因此,现在您可以选择指定范围内给定日期、周、月、年内所有按小时记录范围内所有按小时记录的计数总和

为了不影响web服务的响应,web服务应该将这些事件写入总线或队列中,由单独的数据加载过程拾取

累计每页访问量
但是,您可能希望按页累积

累积器进程需要为每个pageId维护累积器类的实例:

class Counter{
  datetime start;
  int pageId;
  int count;
}
并且,计数记录将是:

ByHour = [start, pageId], count;
PageID = [pageId], url;
ByDay = [start, paramId], count;
ParamID = [paramId], paramName;
按用户ID、pageId、sessionId、clientIp、任何内容进行累积
然后,您进一步确定需要累积每个用户id的访问量。在这种情况下,您可能会确定最好按天累积,而不是按小时累积。如果按小时累计,那么您也可以依赖于跟踪表,因为计算每小时启动新会话或访问的用户的稀缺性与依赖于跟踪表一样重要

累积器进程需要根据参数ID维护累积器类的实例:

class Counter{
  datetime start;
  int paramId;
  int count;
}
并且,计数记录将是:

ByHour = [start, pageId], count;
PageID = [pageId], url;
ByDay = [start, paramId], count;
ParamID = [paramId], paramName;
其中paramName可以是“pageId”、“userId”、“clientIp”等

为了防止数据丢失,您可以每十分钟更新对ByDay表进行更改的任何计数器实例,而无需重置计数器实例。仅当一天过渡到新的一天时,才重置开始和计数

class Counter{
  datetime start;
  boolean modified;
  int paramId;
  int count;
}
“修改”字段不会写入数据库表。它是一个标志,用于确定计数器实例是否需要更新ByDay表。如果发生更新,“修改”字段将重置为“false”