14

我试图找出为我正在编写的这个基于事件的分析系统的模式建模的最佳方法。我主要关心的是以一种使查询简单快速的方式编写此代码。我也将使用 MySQL。我将介绍一些要求并概述可能的(但我认为很糟糕)架构。

要求

  • 跟踪事件(例如跟踪“APP_LAUNCH”事件的发生)

  • 定义自定义事件

  • 能够在 >1 个自定义属性上分割事件(例如,在“APP_VERSION”属性上分割“APP_LAUNCH”的出现)

  • 跟踪会话

  • 根据时间戳范围执行查询

可能的建模

我遇到的主要问题是如何对分段和查询进行建模以获取事件的总计数。

我最初的想法是定义一个 EVENTS 表,其中包含 id、int 计数、时间戳、属性 (?) 和 EVENTTYPE 的外键。EVENTTYPE 具有属于通用事件类型的 id、名称和附加信息。

例如,“APP_LAUNCH”事件将在 EVENTS 表中有一个条目,该条目具有唯一 id、表示事件发生次数的计数、时间戳(不确定标记在什么上面)以及属性或属性列表(例如“APP_VERSION”、“COUNTRY”等)和名称为“APP_LAUNCH”的 EVENTTYPE 的外键。

评论和问题

我很确定这不是一个很好的建模方法,原因如下。这使得很难进行时间戳范围查询(“时间 x 和 y 之间的 APP_LAUNCES 数”)。EVENTTYPE 表并没有真正发挥作用。最后,我不确定如何针对不同的分段执行查询。最后一个是我最担心的。

我将不胜感激帮助正确建模此模型或向我指出有用的资源。

最后一个问题(这可能很愚蠢):为每个事件插入一行是不是很糟糕?例如,假设我的客户端库对我的 API 进行了以下调用:

track("APP_LAUNCH", {count: 4, segmentation: {"APP_VERSION": 1.0}})

我将如何将其实际存储在表中(这显然与模式设计密切相关)?为这些调用中的每一个简单地插入一行是否很糟糕,其中可能有很多?我的直觉反应是,我真正感兴趣的主要是总体总数。我没有足够的 SQL 经验来了解这些查询如何在可能数十万个这些条目中执行。当我希望客户端实际获得分析时,聚合表或内存缓存是否有助于缓解问题?

我意识到这里有很多问题,但我非常感谢任何和所有的帮助。谢谢!

4

1 回答 1

26

我认为你的大部分担忧都是不必要的。一个接一个地回答你的问题:

1)最大的问题是自定义属性,每个事件都不同。为此,您必须使用EAV(实体-属性-值)设计。重要的问题是——这些属性可以有哪些类型?如果不止一个 - 例如字符串和整数,那么它会更复杂。这种设计一般有两种类型:

  • 对所有类型的值使用一张表和一列 - 并将所有内容转换为字符串(不可扩展的解决方案)

  • 每种数据类型都有单独的表(非常可扩展,我会这样做)

所以,表格看起来像:

Events             EventId int,  EventTypeId varchar,   TS timestamp
EventAttrValueInt  EventId int,  AttrName varchar,  Value int
EventAttrValueChar EventId int,  AttrName varchar,  Value varchar

2)分割是什么意思?查询事件的各种参数?在上面提到的 EAV 设计中,您可以这样做:

select * 
from Events 
  join EventAttrValueInt  on Id = EventId and AttrName = 'APPVERSION' and Value > 4
  join EventAttrValueChar on Id = EventId and AttrName = 'APP_NAME' 
                                          and Value like "%Office%"
where EventTypeId = "APP_LAUNCH"

这将选择 APP_LAUNCH 类型的所有事件,其中 APPVERSION > 4 并且 APP_NAME 包含“Office”。

3) EVENTTYPE 表可以达到一致性的目的,即您可以:

table EVENTS (.... EVENTTYPE_ID varchar - foreign key to EVENTTYPE ...)
table EVENTTYPE (EVENTTYPE_ID varchar)

或者,您可以使用 ID 作为数字并在 EVENTTYPE 表中使用事件名称 - 这样可以节省空间并允许轻松重命名事件,但您需要在每个查询中加入此表(导致查询速度稍慢)。取决于节省存储空间与降低查询时间/简单性的优先级。

4)时间戳范围查询在您的设计中实际上非常简单:

select * 
from EVENTS
where EVENTTYPE_ID = "APP_LAUNCH" and TIMESTAMP > '2013-11-1'

5) “为每个事件插入一行是不是很糟糕?”

这完全取决于你!如果您需要每个此类事件的时间戳和/或不同参数,那么您可能应该为每个事件设置一行。如果有大量相同类型和参数的事件,您可能可以做大多数日志系统所做的事情:聚合一行发生的事件。如果您有这样的直觉,那么这可能是一条路要走。

6) “我没有足够的 SQL 经验来了解这些查询如何在可能数十万个这样的条目中执行”

成百上千个这样的条目将被毫无问题地处理。当你达到一百万时,你将不得不更多地考虑效率。

7) “当我希望客户端真正获得分析时,聚合表或内存缓存是否有助于缓解问题?”

当然,如果查询变慢而您需要快速响应,这也是一种解决方案。但是你必须引入一些机制来定期刷新缓存。它过于复杂;也许更好地考虑聚合输入上的事件,见 5)。

于 2013-11-23T20:30:58.407 回答