mixpanel系统架构(译)
作者:Vijay Jayaram
时间:June 2018
译者:ovasty
介绍
Mixpanel是一个用户分析平台,它让任何人都可以从数据中学习并采取行动。它从遍布全世界的客户的网站和移动App中获取数据并通过可交互的用户界面生成汇总的报告。为了能够提供一个强有力的,支持大规模用户分析的工具,我们花费了数年时间搭建自已的数据库,名叫Arb。在这篇文章中,我们将深入系统架构之中,一探究竟。
我们内部开发了Arb来满足自身严苛的需求:
- 以用户为中心:Arb是一个定制开发的数据库,为了满足复杂的以用户为中心的查询请求
- 速度:多数的查询响应时间都少于1秒
- 实时:采集的数据在1分钟之内即可被查询
- 可扩展:我们每天采集TB规模的数据,单个查询通常扫描100GB。我们使用Google Cloud,可以进行有效的扩展以满足客户的需求。
相关数字:
- Mixpanel一年接收超过8万亿条的新数据
- Mixpanel的查询引擎每天处理20,000TB的数据以及超过数百万次的查询
- Mixpanel的存储系统存储了超过1PB的压缩数据
- 包括采集与查询处理,系统的稳定运行时间超过99.99%
- Mixpanel符合SOC2,ISO27001数据中心,GDPR和EU Privacy Shield标准,同时也符合PCI DSS和HIPAA的规范
Mixpanel的架构可以分为3个主要的部分:
- 数据摄取:接收客户产品发送的数据,并将其进行存储
- 数据存储:提供了一个分布式、可复制,列式的数据存储
- 数据查询:接收客户的查询,返回聚合的、可视化的数据

接下来我们会按顺序介绍相关的组件。对于其它一些组件,诸如消息处理、A/B测试以及机器学习等,我们会在以后的文档中再进行介绍。
用户为中心的数据模型
Mixpanel数据模型的设计,围绕着如何理解客户产品中的用户行为。客户会发送数据到Mixpanel的项目中。每条数据包括事件(Event)或用户(user)的信息。当用户在产品上进行一个操作时,客户会发送一条事件数据。当获取用户的新信息时,会发送或更新用户的数据,比如注册。图2显示了一个样例项目的数据。
事件Event数据:
TIMESTAMP | USERID | EVENT | OS | NAME | ITEM |
---|---|---|---|---|---|
2016-12-15T08:02 | qrt-345 | Browse | iPhone | raincoat | |
2016-12-15T08:05 | qrt-345 | Signup | iPhone | Janet | |
2016-12-15T08:07 | pnm-321 | AddToCart | Android | popcorn | |
2016-12-15T08:10 | Janet | AddToCart | iPhone | raincoat | |
2016-12-15T08:10 | Vijay | AddToCart | iPhone | chocolate | |
2016-12-15T08:15 | Janet | Checkout | iPhone |
人People数据:
USERID | COUNTRY | PHONE | FIRST PURCHASE DATE | NUMBER OF PURCHASES | |
---|---|---|---|---|---|
Janet | janet@mixpanel.com | US | 650-555-1234 | 2016-12-15 | 1 |
Vijay | vijay@dogsrus.nl | NL | 2016-02-19 | 7 | |
Jessica | jw@gogogo.com | US | 914-555-5678 | 2015-07-29 | 12 |
别名Alias数据:
NAMED_USERID (ALIAS) | ORIGINAL_USERID |
---|---|
Jessica | abc-123 |
Jessica | azy-456 |
Janet | qrt-345 |
Vijay | xyz-789 |
mixpanel schema
Mixpanel的数据模型由事件和人的表组成。
- Event表记录用户的行为数据。包括:
- timestamp:时间戳,用户何时进行的操作
- identifier:用户识别符,记录谁执行了操作
- event name:事件名称,做了什么操作
- properties:属性,描述事件的更多信息,如对于“购买”事件描述具体购买的商品
- People表记录用户。包括:
- identifier: 用户操作符,用作记录是谁,同时也应用在event数据中
- properties:描述用户的更多信息。有些信息是用户直接提供的,如注册或首次交易时提供的email或邮寄地址。有些是更新的信息,如用户积累的购买次数或最近购买日期。
Event和People表都拥有一个灵活的结构。所有的事件记录都包括一个时间戳、用户标识符以及事件名称。所有的用户记录也包括用户标识符,可以用来与该用户的事件数据进行join查询。除此之外,剩下的数据结构就是所谓的属性(Properties),对于每一条不同的数据,它的结构可能都是不同的。大多数时候,对于同一个事件类型(指事件名称相同的数据),属性基本上固定的结构或者说是高度重合的。但是,对于不同的事件类型,属性中重复的内容很少。
Event记录是不可以修改的:它们都是一次性发送过来,再也不会发生改变。唯一的例外是为了符合GDPR的要求,如果用户请求删除它们的数据,那么我们需要将其相关数据进行删除操作。People的记录数据是可以修改的:他们包含了用户最近的信息,可能随时会发生改变。
以用户为中心的产品
Mixpanel的特点是分析用户的行为:在使用产品时,用户最喜欢做什么以及最不喜欢做什么?以用户为中心的数据模型使得这些特点可以方便地让客户去使用,同时对我们来说,实现起来也比较高效。
- 洞察:洞察报告可以根据事件和用户的属性数据,进行任意维度的切分。如果用户标识符同时存在于事件和用户记录中,那么就可以进行join查询;在报表的UI中,用户属性可以作为事件的属性进行显示并使用。此外,通过去用户标识符进行去重的统计,可以用来计算常见的指标:日活(DAU)。
- 漏斗:漏斗报表确定了用户经过一系列事件操作的转化率。举一个电子商务的例子,我们可以定义一个漏斗,它包括“查看商品”,“添加到购物车”,“购买商品”一系列事件操作,从漏斗中可以看到通过每个步骤的用户情况。与洞察功能类型,漏斗功能也支持通过事件和用户的属性进行过滤与分群。漏斗的需求并不是超前的,而应该早就存在的。
- 留存:留存报表用来分析用户的事件序列,用来判断产品对于用户的黏性。留存报表将用户按照他们首先触发事件的时间进行分组统计,然后再对其在时间窗口内触发另一个事件进行汇总统计用户数。留存同样可以按照属性进行过滤和分群操作。
- 消息:这里的消息是指通过Email或移动App的消息推送,将消息发送给某些用户。用户群可以是所有用户,也可以是根据数据属性或事件过滤的用户群。连接事件数据和用户数据的用户标识符,就包括email。
请注意,用户标识符对于接下来的内容非常重要,它使得Mixpanel可以根据用户对事件分组。
用户标识符的管理
对用户标识符进行管理,可以保证识别出同一个用户生成的event数据和people数据。大多数时候,用户标识符是足以用来识别一个用户的所有事件数据的。准确地识别每一个事件的用户对于以用户为中心的数据分析来说,是非常重要的。但是,匿名用户和一个用户使用多台设备的情况,会让情况变得非常复杂。更有甚者,同一个用户有多个用户标识符。
- 匿名用户:单个用户可能拥有多个用户标识符,例如,在注册之前他们在匿名状态下,执行了一些操作。在注册之前,每个用户都会有一个随机生成的hash值作为用户标识符。当他们注册之后,会选择一个不同的用户标识符,如帐号、email地址等。例如,在图2中,用户”qrt-123”,在后面会被标注为“Janet”。我们需要识别出“qrt-123”在8:02的事件数据和“Janet”在8:10的事件数据,都属于同一个用户。
- 多设备:一个用户首先在笔记本上识别为“qrt-123”,登录后被识别为“Janet”。而在她的手机上,开始被识别为“sdr-234”,随后被识别为“Janet”。那么我们就需要把“qrt-123”,“sdr-234”以及“Janet”均识别为同一个用户。
同一个用户的不同用户标识符必须进行整合,以便正确的进行Mixpanel的相关功能计算。例如,为了计算日活DAU,每一个用户必须仅使用一个用户标识符。如果一个用户有多个用户标识符,那么DAU的数据就会虚增。为了计算漏斗,必须追溯一个用户在整个时间段内的所有事件流。例如,一个购买流程开始于手机上的浏览,结束于笔记本电脑上的信用卡支付。
Mixpanel利用用户别名Alias,解决了用户标识符管理的问题。用户别名作为用户标识符,在event和people数据记录中被发送上来,但是在数据摄取和存储的时候可以被不同的用户标识符替换。在上面的例子中,“Janet”和“sdr-234”都变成了“qrt-123”的别名。在Mixpanel存储的数据中,包括相同用户的所有的event和people数据都含有相同的用户标识符。
摄取(Ingestion)
Mixpanel每年从客户方接收超过8万亿条数据。让我们跟踪了解一下一个event数据在系统内的生命周期是怎样的:
- 用户设备发送event数据到https://api.mixpanel.com/track
- 请求被我们的DNS路由到全球分布的机器上
- 验证event数据,并将其推入消息队列中
- 一个ETL处理进程运行在中心Google Cloud区域,从消息队列中消息event数据,执行数据检验,别名识别以及将记录路由到存储系统
- 我们的存储系统摄取event数据,快速将其持久化成文件,并在Google的Persistent Disk环境下进行复制与加密。在这个时候,数据就可以被查询到了。
- 经过一段时间,一批event数据将被索引成一个查询更高效、列式存储的格式,并上传到Google Cloud存储中。
99%的记录从抵达服务器到可被查询,花费时间不超过1分钟。这就是为什么Mixpanel可以提供实时分析的原因。

ETL处理程序还会将每条记录以kv形式存储用于实时报表。实时报表提供了一个实时查询数据记录,方便调试以及理解数据的功能。
存储(Storage)
Mixpanel的数据存储是一个分布式的,以用户为中心的基于事件的数据库,用于数十亿级事件数据的交互式分析。
列式存储
Mixpanel的数据原本是行式的:一个json对象描述了event或people记录的数据。但是列式存储的布局变成了分析型数据存储的标准。分析式的查询一般仅针对每条记录中的几列进行聚合的操作。列式存储允许查询仅仅访问需要的列,对于单列也可以进行更快的扫描。
在2015年,我们为Mixpanel开发一个新的列式存储系统。主要的原因是考虑功能性以及性能。
功能性:Mixpanel的数据格式比较灵活,没有固定的schema。我们需要处理一些没有申明的属性,有可能每条数据都发生变化。大多数的数据存储对schema的修改要求都很严苛。
性能:Mixpanel的数据和查询都是以用户为中心的。为了控制数据的布局与数据分布,我们对数据和查询进行了优化,通过了解数据布局,降低查询计划的消耗,减少执行步骤,减少网络数据传输,以便达到更快的查询。
- 数据在分布式的环境下,按照userid进行分片存储。因此,单个用户所有的event数据都将保存在同一台机器上。
- 查询过程能够利用每个用户在同一台机器上的所有数据。例如,可以在每台服务器上对DAU进行独立的计算。类似的,漏斗的计算可以在一台机器上将某一个用户的所有历史event数据进行计算。
列式存储对数据进行10倍的压缩,同时允许查询仅访问需要的列,这样就可以更迅速和有效的得到查询结果。当我们将存储转为列式之后,查询的等待时间提升了将近5倍,CPU使用率降低了10倍。结果我只需要更少的硬件就可以达到相同的查询负载要求。我们也有足够的查询能力去添加新的查询功能,比如通知和自动分群。
列式存储数据格式

接下来,我们介绍一下列式存储的数据格式。对于Mixpanel中的每一个项目,每天都会创建一个列式存储的文件。每个文件分4个部分(section),如上图所示:
- Header:开始和结束的时候戳,event的数量,后续section的地址以及其它的元数据
- Dictionaries:3个不同的字典,分别对应属性名称、属性值以及userid
- Index:每个event的每个属性的位置,按event名称,property名称排序
- Data(Columns):value的数量加上所有event所有properties的实际的值
每个字典都使用LZ4压缩算法,存储在文件中。一个属性的值存储在一个数据段中,并按事件的时间戳进行排序。属性的值或者保存原始值,或者利用字典进行索引,无论哪种都对属性进行压缩处理。对重复值使用RLE,对整数使用字节打包,同时利用索引节省空间。
数据分片
数据是被切分保存的,系统中数据以用户标识符作为key进行分片处理,保证了一个用户的所有数据都保存在同一台服务器上。为了能够支持灾备,我们在两个不同区域维护了多份数据的复制。为了增加可靠性,所有数据都会生成定期的快照保存在Google Cloud Storage(GCS)中。考虑到成本和扩展因素,历史数据同样会持久化在GCS中。
在数据摄取的过程中,每个区域的存储服务器都会消费所在机器上的分片数据,将他们保存为行式的存储文件,使得这些数据可以被立即查询到。同时,一个索引的进程在后端运行,将这些行式的存储文件转化为我们的列式存储格式。这样既压缩的数据,又提升了查询的性能。查询操作既可以从行式存储,也可以从列式存储中读取数据。
可扩展性与可靠性
数以百计的切片数据为数据摄取和查询提供了高扩展性。我们的系统可以随着数据的增长对其进行重新分片,所以支持水平扩展。
每个切片会有多份复制,这样的话只要有一份复制在,数据就不会丢失。Mixpanel因此在处理机器升级和错误的情况下,不会造成数据的丢失以及不可用的情况发生。数据摄取与查询维持稳定运行时间达到了99.98%。
查询(Query)
查询接口
Mixpanel的客户可以有多种方式进行数据的查询。他们可以通过浏览器,在Mixpanel的UI上进行操作,或通过手机上Mixpanel的App,或通过调用Mixpanel提供了查询API接口。
客户可以在Mixpanle的UI上创建以及查看数据报表。每一张报表都会通过一次或多次调用查询的API获取数据,并进行可视化的展示。在界面中有许多功能强大的报表类型,如分群、漏斗、留存等报表。客户也可以通过在Mixpanel的App上操作创建以及查看指标。
最后,客户可以直接调用查询的API。有些客户通过脚本查询,定期查看指标。另一些客户将查询嵌入到其它的应用内,将数据展示给他们的用户。这些应用场景均可以通过UI查询提供支持。
查询过程
为了查询的高性能、实时以及以用户为中心,Mixpanel自建了查询的技术栈。Distributed Query Server(DQS)负责接收查询请求,请求被分发到许多Local Query Servers(LQS)进行并行计算。查询同时基于历史数据以及实时获取的数据,以便提供实时的查询结果。每一个LQS将结果返回给DQS执行聚合操作,并将最终结果返回给客户。

2017年,我们基于Google的云平台对查询引擎进行了云原生的重构。其中一项就是解耦了存储层与计算层,以便可以对这两层分别进行扩展。当客户采集了更大规模的数据之后,我们的存储层可以无缝地进行扩展,保证安全可靠的存储。当客户进行海量复杂的查询时,我们使用数以千计的CPU核在几秒钟内计算TB级的数据。我们的MPP架构,结合我们对用户分析的系列优化,提供了独一无二的速度与扩展上的优势。
我们的基础设施为我们更高级的功能打下了基础,比如Messages和自动洞察。Messages就是由我们实时的能力所支撑,客户能够对数据进行及时、自动化的操作。我们的高级机器学习功能,像异常检测和自动分群等运行得非常棒,因为我们的模型每天都基于万亿级的数据进行训练。如果没有这些构成Mixapnel基础的可扩展的基础架构,想要达到这样的效果是不可能的。
安全性
Mixpanel技术栈中的安全
安全是Mixpanel技术栈中必须要考虑的部分,体现在以下几个方面:
逻辑数据隔离:客户所有的数据都会根据所属项目进行信息标注,而且这个信息在架构中的每一层都是有效的,从而保证了数据的请求的合法性,仅针对所属项目返回相应的数据。
数据保护:我们的生产环境与公网之间有严格限制的防火墙,而且所有客户的数据都是加密的。
审计:对于访问客户数据和生产系统的个人,有着严格的限制,必须有书面的许可。同时,所有的数据访问行为都会被定期审核,以防数据被滥用。
总结
Mixpanel提供了一个快速、可扩展、可靠以及安全的产品分析平台。接下来的图表总结了Mixpanel的架构设计是如何提供速度、扩展性、可靠性和安全性的。
Mixpanel产品分析平台的架构服务超过20000家客户,包括BMW、DocuSign、US Bank和Microsoft。我们了解到他们利用我们的产品帮忙他们回答了绝大多数用户与产品的问题,所以我们对于我们系统的性能、可靠性和安全性非常重视。我们有将近三分之一的工程师负责创建这篇文章中提及的采集、存储和查询系统。同时,我们每天都在从功能和性能上对其进行优化。
Ingestion | Storage | Query | |
---|---|---|---|
速度 Speed | 对不同数据量的客户使用不同的消息队列 | 自定义分布式列式存储,基于Google Cloud Platform扩展 | 分布式处理,仅读取需要的列,本地数据缓存,根据切片的复制进行任务划分 |
扩展性 Scale | 很多http服务器和消息队列。可根据需求水平扩展,增加新的服务器 | 许多的数据分片 | 数据分片的本地处理 |
可靠性 Reliability | 全球分布式采集中心。远程复制进行容灾处理 | 每个数据分片至少有2份复制 | 查询过程能够使用任意切片的复制 |
安全性 Security | 使用TLS 1.2,256bit AES加密 | 256bit数据加密 | 使用LTS 1.2,256bit AES加密 |
原文链接:https://mixpanel.com/wp-content/uploads/2018/06/System-architecture_June2018.pdf
mixpanel系统架构(译)