我正在尝试找出为我正在撰写的基于事件的分析系统建模模式的最佳方法。我主要担心的是以一种简单快速的查询方式编写本文。我也将使用MySQL。我将回顾一些要求,并提出一个可能(但我认为很差)架构的概述。
跟踪事件(例如“APP_LAUNCH”事件的跟踪发生)
定义自定义事件
能够对> 1个自定义属性进行细分事件(例如,在“APP_VERSION”属性中获得“APP_LAUNCH”的出现次数)
跟踪会话
根据时间戳范围执行查询
我遇到的主要问题是如何建模分段和执行查询以获取事件的总体计数。
我最初的想法是定义一个EVENTS表,其中包含id,int count,timestamp,property(?)和EVENTTYPE的外键。 EVENTTYPE具有属于通用事件类型的id,名称和附加信息。
例如,“APP_LAUNCH”事件在EVENTS表中将有一个条目,其中包含唯一的id,count表示事件发生的次数,时间戳(不确定标记的内容)以及属性或列表属性(例如“APP_VERSION”,“COUNTRY”等)和名为“APP_LAUNCH”的EVENTTYPE的外键。
由于以下原因,我很确定这不是建模的好方法。这使得难以进行时间戳范围查询(“时间x和y之间的APP_LAUNCHES数”)。 EVENTTYPE表并不真正有用。最后,我不确定如何对不同的分段执行查询。最后一个是我最担心的。
我将非常感谢帮助正确建模或帮助我找到有用的资源。
最后一个问题(可能是愚蠢的):为每个事件插入一行是不是很糟糕?例如,假设我的客户端库对我的API进行以下调用:
track("APP_LAUNCH", {count: 4, segmentation: {"APP_VERSION": 1.0}})
我如何将其实际存储在表格中(这显然与架构设计密切相关)?为这些调用中的每一个调用只插入一行是不是很糟糕,其中可能有很大一部分?我的直觉反应是,我真的对整体聚合计数感兴趣。我没有足够的SQL经验来了解这些查询如何执行可能数十万这些条目。当我希望客户端实际获得分析时,聚合表或内存缓存是否有助于缓解问题?
我意识到这里有很多问题,但我真的很感激任何帮助。谢谢!
答案 0 :(得分:18)
我认为你的大多数担忧都是不必要的。接下来提出一个问题:
1)最大的问题是自定义属性,每个事件都有所不同。为此,您必须使用 EAV (entity-attribute-value) 设计。重要的问题是 - 这些属性有哪些类型?如果不止一个 - 例如字符串和整数,然后它更复杂。通常有两种类型的设计:
使用一个表和一列表示所有类型的值 - 并将所有内容转换为字符串(不是可扩展的解决方案)
为每种数据类型都有单独的表格(非常可扩展,我会这样做)
因此,表格如下:
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_LERSCH类型的所有事件,其中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)。