本文首发于微信公众号“Shopee技术团队”。
业务背景
随着 Shopee 业务不断扩张,为了更加了解用户对产品的行为反馈,更好地决策产品特性,各团队内部涌现出大量数据分析的需求。例如:客户端用户行为分析(如跳转行为、页面留存等),业务核心指标分析(购买量、购买品类),甚至于 A/B Test 的结果数据分析,都需要一套数据体系来支撑。而通过传统离线数据产出已然不能满足实时运营、活动投放、异常问题发现等需求。
为了支持这些实时数据分析能力,我们团队开发了 Boussole——多维数据实时分析系统,旨在通过低成本的方式支撑海量多维数据实时分析。本文将详细描述系统中的实时分析查询引擎 Boussole Engine 作为多维数据分析的核心一环,是如何通过对引擎的设计支撑毫秒级实时数据分析结果返回。
1. 介绍
Boussole 作为多维分析平台,与大多数实时分析系统有类似的数据流向。从数据源拉取数据并经过前置清洗,通过用户在平台中定义的指标和维度以及汇聚方式实时聚合后,将产生的结果数据落入持久化存储,用户通过平台前端配置的相关视图及 Dashboard 实时观测这些最新汇聚出的数据结果。
整个系统的核心在于如何能在海量数据上报时提供快速的查询能力。通过获取数据后的预汇聚处理流程,让引擎能在指定维度下快速返回查询结果,但这样带来了额外的存储开销。而通过引擎实现的二次汇聚能力,能够在部分维度不命中预汇聚规则时也能以较快速度查询到结果,从而减少了存储开销。系统提供了较大的灵活性来让用户感知并控制查询速度和存储开销之间的取舍。
我们在整个数据流中的每个阶段都投入了不少的设计精力,来应对海量数据带来的压力,本文仅就其中核心的数据查询引擎来介绍设计思路和具体架构。团队内部启动时面临的首要问题是如何设计一种前后端查询数据和交互的协议,使用户能方便地在前端通过自己的需求查询多维数据。我们在初期调研了一些主流时序数据分析产品,它们主要分为以下几类:
-
类 SQL 的时序数据查询方式,主要有 TimescalesDB[1] 和基于 InfluxQL 实现的 InfluxDB[2],核心思路是通过 SQL 的方式将维度筛选、维度汇聚、指标间运算和时间过滤等标准的时序数据操作通过 SQL 描述并将结果返回给用户。
-
通过 JSON 自定义查询 Schema,主要有 OpenTSD[3] 和 KairosDB[4],客户端需要查询的指标和维度明确指定在 JSON 字段中,服务端将查询的时序数据结果按要求返回。
-
自定义语言实现的查询,主要有 Promethues 的 PromQL[5] 和基于 Flux[6] 实现的 InfluxDB,它们各自都有一套独立的查询语法定义,并且能较好地支持筛选、指标计算和维度汇聚。
在选型上,我们最终使用了 PromQL 来作为前后端查询协议,核心原因是它的功能和易用性及业界的使用广泛程度。作为一种表现形式良好的时序数据查询语言,它能满足在前端查询时维度筛选、汇聚和指标计算的所有需求。并且,它的表现形式简单,在有复杂的汇聚需求(多维度复合指标运算、时序子查询等)时能通过自定义查询能力分析现有数据,相比于 SQL 的复杂表述和 OpenTSDB 过于简单的查询功能,PromQL 更符合需求。
要想做到实时分析查询,在项目初期就应该对未来能达到的效果有明确规划。我们希望不论有多少原始数据上报,在查询响应速度方面都能达到毫秒级,下文将详细描述我们是如何设计系统并达到这一目标的。
2. 存储模型
在了解如何实现查询流程前,先介绍一下 Boussole 底层的多维时序数据存储模型。关于多维时序数据的存储,业界大部分实现都是类似的,核心思路是将多维数据细化到粒度最小的单个维度转化为 KV 格式,再通过保存单维度与多维度之间的映射关系,从而将多维时序数据映射在持久存储中。
这里以温度为一个指标举例,说明系统内部如何处理多维时序数据:每个城市都有一个温度采集站,会定时收集此地的温度数据,将数据上报至气象局。并且,由于温度垂直递减的关系,采温站并不会只采集一个高度的数据,而是一批高度的数据。这些数据是不同的,通常情况下在对流层中海拔越高气温越低。这样,温度随时间、高度、地域的变化就形成了一组多维时序数据。
如上图所示,采集好的多维数据降维后转换成 KV 格式,方便落地在后端的持久存储中,这样做的好处是不论有多少维度,最终存储的格式是相同的。
按照这个思路,其实能够选型的具体存储引擎有很多,考虑到运维成本和社区的成熟度,最终我们选用 HBase 作为后端存储工具。引擎底层为了适配不同的存储类型,实现了一个存储适配层,使得系统可以在 Redis、Memcache、RocksDB、TiKV 等类似存储作为后端时快速对接,这种做法参考了 Promethues