12 月 7-8 日在北京举办的 ArchSummit 全球架构师峰会上,快手科技大数据平台架构师李远策分享了快手在 OLAP 平台的建设与实践。以下为演讲的主要内容,有删节。

快手 App 目前日活 1.5 亿,每天会产生数万亿规模的用户行为数据,对这些数据的高效探索是一件很有挑战同时也很有价值的工作。今天重点分享快手建设万亿级数据规模 OLAP 平台的设计方案以及主要改进过程。

快手的 OLAP 平台诞生的时间不长,在 2018 年 4 月份之前,一些多维分析的需求还是采用预定义指标加上离线计算的方案,其缺点很明显,首先指标预定义是非常固定的,另外因为采用离线计算,实用性也很差。

在今年 4 月份上线 Druid OLAP 分析引擎,加上 Superset 数据可视化平台,解决了不少业务的痛点。5 月,Druid 平台升级到了当时社区最新的 0.12 的版本,在升级过程中解决了时区、文件加载性能等问题。7 月,Druid 平台每天的录入消息数已经突破 1000 亿,用户配置的可视化图表也超过 1000 个。7 月份之后平台进入了一个快速发展的阶段,Druid 在查询性能和稳定性方面都出现了很多的问题,我们做了非常多的改进。9 月,上线了 Druid 探针系统、时序和维度物化视图功能、Indexing  Service 细颗粒资源分配等,另外在资源调度层面也做了大量优化工作。截至今年 11 月,OLAP 平台每天摄入消息的数据量峰值已经超过 5000 亿,用户配置的可视化图表数已经突破 1 万。

半年来 OLAP 平台发展速度非常快,得益于基于 Druid 的高可用架构设计,以及团队伙伴的努力,整个 OLAP 平台上线至今未出现中型或大型的故障,服务很稳定。

快手 OLAP 平台共有 150 台物理服务器,接入的数据源超过 2000 个,每天录入的消息数量在 5000 亿左右,索引的数据存量约 400TB。每天查询次数峰值 1000 万,这个量是非常大的,但是有很多在程序里触发 API 的调用,人为触发的比例较小。整体上平均查询时延为 50 毫秒,P90 为 100 毫秒左右,P99 为 500 毫秒到 1 秒。可视化方面,积累的用户看板数有八百多个,图表数超过 1 万。

首先是多媒体质量分析业务。快手使用了全国多家 CDN 厂商服务,涉及的域名有几百个,每天上报的 CDN 质量监控数据上百亿。CDN 服务质量会直接关系到主站 APP 用户使用体验,公司 CDN 质量团队需要实时对 CDN 监控数据做分析和智能调度,以及对调度效果进行实时的监测。另外,对于 CDN 质量问题需要做出快速分析和定位,这本身也是一个多维分析的过程,OLAP 技术能够很好地满足这个需求。

另外一个业务场景是 A/B Test,快手已经上线了约 1000 个 A/B 的实验,需要对比的 A/B 指标多达数千个,每天有数百亿的数据要流入 A/B Test 平台。对 A/B Test 指标的分析,也是一个很典型的多维分析的过程,OLAP 平台要满足每天几十万次的查询调用需求,查询的时延要保证在百毫秒级。

OLAP 平台选型时对公司多个业务团队的需求做了调研,总结来讲,大家对以下几个点关注度会比较高。比如超大数据规模的支持,单个数据源可能每天有上百亿的数据量需要录入;查询时延,要保证在毫秒到秒级;数据实时性,很多业务线明确提出实时数据分析的需求;另外还有高并发查询、平台稳定性等,除此之外还有一些相对权重比较低的需求:如数据 Schema 的灵活变更、精确去重的功能,以及 SQU 接口的支持等。

根据对用户调研的总结,我们对比了现在比较常用的 OLAP 技术。

首先,Hive/SparkSQL 在数据仓库的领域应用是比较广泛的,但是因为查询时延很难能够满足毫秒到秒级的要求,同时因为是离线计算,数据时效性也比较差。

其次,ES 是一个功能很强大的系统,在中等数据规模场景下能较好地满足需求,但是在万亿和更大的数据规模场景下,数据的写入性能和查询性能都遇到了很大的瓶颈。

Kylin 和 Druid 功能比较类似,考虑到 Druid 采用 OLAP 架构,数据时效性相对于 Kylin 来讲会更好,数据的变更也相对更加灵活,所以最终选用 Druid 作为 OLAP 平台的查询引擎。

上图是 Druid 系统架构图,其中 Coordinator 和 Overlord 是 Druid 的主节点;Middle Manager 主要是负责数据索引,生成索引文件,Historical 节点主要负责加载索引文件,同时提供历史数据的查询服务;Broker 是查询的接入节点;除此,Druid 还需要对元数据进行存储,比如选用 MySQL;Middle Manager 在产生索引文件的时候,需要把索引文件先发布到一个共享的存储系统里,我们选择了大家普遍采用的 HDFS 系统。

上面提到 Druid 的查询性能非常好,总结来说主要是因为采用了如下五个技术点:数据的预聚合、列式存储、Bitmap 索引、mmap、以及查询结果的中间缓存。下面针对两个点具体展开讲一下。

首先讲下数据预聚合。Druid 会把一行数据消息分成三个部分,包括时间戳列、维度列以及指标列。所谓预聚合,就是当数据录入到 Druid 系统时,会按照一定的时间周期把原始数据做一次预先聚合,会根据一个全维度聚合出要计算的指标,也就是要索引的内容。后续所有的查询都是通过这些预聚合的中间结果做二次查询。

接下来讲下 Bitmap 索引。Bitmap 索引主要为了加速查询时有条件过滤的场景。Druid 在生成索引文件的时候,对每个列的每个取值生成对应的 Bitmap 集合。如图上所示,Gender 为 Male 对应的 Bitmap 为“1001”,代表第 1 行和第 4 行的 Gender 为“Male”。举一个查询的例子,假设要筛选 Gender =‘Female’and City =‘Taiyuan’的数据,那么只需要把 Gender =‘Female’对应的 Bitmap “0110”和 Taiyuan 对应的 Bitmap “0101”进行与操作,得到结果为“0100”,代表第二行满足筛选条件。通过 Bitmap 可以快速定位要读取的数据,加速查询速度。

关于 Druid 模块,Druid 支持从 kafka 实时导入数据,同时也支持批量从 HDFS 或者 HIVE 系统进行离线导入;Druid 提供了丰富的查询 API 接口。除了默认提供的 Restful 接口之外,Python 、Java、Go 等编程语言都有第三方的实现 API 接口。此外,Druid 也提供了 SQL 接口的支持。值得一提的是,Hive 在 2.2 版本之后通过 StorageHandler 实现了对 Druid 的支持,这样可以通过 Hive SQL 查询 Druid 里的数据,快手内部也在用,但是需要做一些修改工作,比如解决时区问题、Druid 数据源维度和指标的大小写敏感问题,以及实现默认的 limit、默认时间范围选择等功能。

这是快手 OLAP 的平台架构图,中间部分是 Druid 自有的组件,数据通过 kafka 实时摄入和离线从 Hive 数仓中批量导入。除此之外,我们还配套了完善的 Metric 系统,探针系统、Druid 数据源管理系统等。

在万亿甚至几十万亿数据规模场景下,OLAP 平台使用过程中也面临了很多挑战。比如如何让查询变得更快,资源的利用率如何更高效,在数据的管理到数据的接入如何更方便,集群平台如何更稳定,针对这些问题我们都针对性的做了改进和优化。

首先,稳定性方面我们做了多种的资源隔离部署的方案,在接入层通过代理实现 Broker 的高可用和负载均衡。

在 Historical 数据存储层,做了两个层面的数据划分。一是数据的冷热分离,热数据存储在 SSD 的机器上,当热数据变成冷数据之后会自动地迁移到 HDD 机器上。因为大部分查询都是查询最近的数据,所以才用 SSD 的加速效果是非常明显的。考虑到 SSD 的成本比较高,可以在设置热数据的副本的时候,把其中一个副本放在 SSD 上,另外一个副本放到 HDD 的机器上,然后设置 SSD 副本的权重,大部分的请求还是能够落在 SSD 机器上。当 SSD 机器出现故障之后,请求才会发送 HDD 上,这样能节约不少成本。

除了冷热数据分离的考虑外,因为有些对查询稳定性要求更高,快手通过 Tier 配置也对特殊业务也做了隔离,特殊的业务数据源索引数据存储在专用的 Historical 机器上。这样在一些大查询可能会导致 historical 内存 GC 或者是系统 IO 支持 Load 较高的场景下,其查询性能仍然不受影响。

在大规模数据场景下查询性能的加速,我们也做了很多优化。首先是物化视图,会做两个层面的物化视图,一个是维度层面的物化,一个是时序层面的物化。

什么是物化视图,假设一个数据源的原始维度有十个列,通过分析查询请求发现,group1 中的三个维度和 group2 中的三个维度分别经常同时出现,剩余的四个维度可能查询频率很低。更加严重的是,没有被查询的维度列里面有一个是高基维,就是 count district 值很大的维度,比如说像 User id 这种。这种情况下会存在很大的查询性能问题,因为高基维度会影响 Druid 的数据预聚合效果,聚合效果差就会导致索引文件 Size 变大,进而导致查询时的读 IO 变大,整体查询性能变差。针对这种 case 的优化,我们会将 group1 和 group2 这种维度分别建一个预聚合索引,然后当收到新的查询请求,系统会先分析请求里要查询维度集合,如果要查询的维度集合是刚才新建的专用的索引维度集合的一个子集,则直接访问刚才新建的索引就可以,不需要去访问原始的聚合索引,查询的性能会有一个比较明显的改善,这就是物化视图的一个设计思路,也是一个典型的用空间换时间的方案。

时序物化视图:除了刚才提到的查询场景外,还有一种查询 Case,Druid 也不能很好满足。比如大跨度时间范围的查询,假设一个数据源的聚合力度是分钟级别,但需要查询最近三个月的数据就比较麻烦,因为需要把过去三个月的所有分钟级别的索引文件全部扫描一遍,然后再做一次聚合的计算。

为了解决这个问题,我们在数据源分钟级别的索引上再新建一个小时级别甚至级别的物化索引,这种情况下聚合效果就会更好,索引整体的 size 也会比较小。当收到一个新的查询请求时,如果查询要统计的粒度是天级别或者是更高级别的查询粒度,会把查询请求自动路由到天级别物化索引上,这样查询性能也会有一个比较明显的改善。

下面讨论下 Druid 元数据存储系统的性能优化,平台上线以来我们积累了大约几百万的 Segment 文件,对这些数百万 Segment 元信息的查询,或者说 MySQL Segments 表的查询也遇到的性能瓶颈。

首先是 Overlord 与 MySQL 之间的交互优化。Overlord 在发布新的 Segment 文件的时候会多次查询 Segments 表,监控发现会有大量的慢查询。解决方案很简单,针对性地对 Segments 表增加索引即可。对比优化后的 MySQL 查询性能,可以从 10 秒多降到 1 秒,有 10 倍以上的提升。

另外是 Coordinator 与 MySQL 之间的交互性能优化。Coordinator 会周期性的去全量扫描 Segments 表,每次扫描都会花费较长的时间。首先全量扫描完全是没必要的,我们改造成增量扫描的方案,整个扫描的耗时从原来的 1.7 分钟降到 40 秒左右。然后更进一步对增量扫描的 SQL 专门创建了 MySQL 索引,扫描耗时可以降到 30 毫秒,整体算下来有上千的性能提升。

接下来是 Segment 文件加载过程的优化,Coordinator 扫描 segment 匹配 Rule 过程默认是串行实现的,我们对此做了并行化的加速,再加上一些细节点的改进。集群几百万量级的 Segment 文件协调一遍的耗时从原来的 3 分钟降低到现在的 30 秒。Druid 元数据系统通过如上几个点的优化后,目前基本上不再有性能瓶颈。

首先,每个 Kafka indexing task 会对应一个 Supervisor 的服务,Supervisor 的 task count 是一个固定的值,当用户设置 task count 比较小时,可能会因为读取 Kafka 的 lag 过大而出现数据延迟,而如果设置的过大会造成资源的浪费。另外,用户在创建一个 indexing task 的时候,也很难估算 task count 应该是多少合适。我们的优化方案是让 Supervisor 根据当前消费 Kafka 时延的情况,自动调节 task count,这样业务高峰期不至于出现数据延时,数据低峰期时也能把资源还给集群,整个集群的利用率有明显提升。

另外是 Middle Manager 的 indexing task 资源分配问题。Druid 为每个 Middler Manager 分配一个固定的 Slot 数,但是因为相对 Kafka indexing task 来讲 Hadoop indexing task 其实只是一个 Hadoop 客户端仅负责提交一个任务,本身并不怎么占资源,这样的话会有一些资源的浪费的问题。针对这个问题的优化思路是,把 Middler Manager 的 task 调度配置从按照 Slot 数改成按照内存大小分配,我们会区别对待不同类型的 task,对于 Kafka 的 task 和 Hadoop 的 task 会默认不同的内存大小,当然用户在提交 task 的时候,可以指定自己的 task 内存大小,我们会做一些最大值的限制,防止恶意的提交。

此外,对 Segment 文件及时的做 Compaction 会有益于查询性能加速,也能节省存储空间。目前 Druid 在做 Compaction 的时候,会提交一个特殊的 Compaction task,串行扫描 Segment 文件进行合并,性能较差。我们对此做了一个并行化的方案,思路是提交一个 Hadoop 的任务,在 Hadoop 集群上去并行扫描 Segment 的信息,然后去做 Compaction,性能的提升还是非常明显的。

在平台易用性方面我们也做了很多的工作。在平台运营的时候会面临一个问题,每天都有很多数据源要接入,在平台上线初期,管理员是可以参与完成,但是当业务快速增长的时候,这个工作量非常大。数据源接入后,还会面临很多需要修改数据源的维度和指标定义的需求,这些都需要系统化的去解决。

除此之外,很多时候用户对 Druid 平台或者对自己的数据理解不够深入,也可能对业务的分析需求场景不够明确,在接入数据源时往往会导入大量的维度和指标信息,这就带来一个隐患:维度越多聚合效果就会变差,更甚至会有一些高基维严重影响数据聚合的效果和查询性能。

针对这些问题,我们设计了两套工具,分别是 Druid 数据源管理系统和 Druid 探针系统。

数据源的管理系统是一个 Web 管理系统,用户可以在这个系统上完成数据源接入、查看和管理,可以查看的信息包括维度和指标信息、Kafka 消费的速率、kafka 消费的 lag 等。上图展示的是数据源管理系统的 indexing task 列表信息,系统配有权限管理功能,只有数据源的负责人可以修改数据源的维度和指标等配置信息。

上图是 indexing task 详情页面,除了一些基础的信息之外,还可以看到像 Kafka 消费的速率情况,用户可以自主地去排查自己负责的数据源的线上问题。

这张是数据源的新建和编辑页面。用户新建 Kafka 数据源的过程非常方便, 其中 Kafka 的信息是从 Kafka 的管理系统里面直接抽取出来的,用户不需要手动填写,直接点选即可。对于时间戳列和时间戳列的格式,系统会自动抽取用户 Kafka 的数据做填充,如果是用户写错了时间戳列的格式,也能够自动纠正过来。对于维度和指标系统也预先做了数据的解析提供 Suggestion,用户只要用鼠标点选即可。

这张图展示的数据源的列表信息,可以在列表上清楚地看到这个数据源的数据量、Segment 文件的平均大小、维度和指标信息。此外,如果这个数据源是通过离线任务导入的话,能够会自动关联离线任务的名字,方便快速定位到自己的定时导入任务。

Druid 探针系统主要解决如下几个问题:

第一,数据源查询热度的分析。探针系统会对 Druid 所有的数据源做总体的查询热度排名,这样管理员可以知道哪些数据源是查询的大客户,会做针对性的“关照”。此外,还可以发现一些没有查询请求的冷数据源或者僵尸数据源,并通知用户去做下线处理,避免占用集群的资源。

对于单个数据源,探针系统还可以对这个数据源内部的维度和指标做查询热度的分析,了解哪些维度是经常被查询的,哪些维度和指标是不常查询的冷维度或指标,特别是还能发现一些既是冷维度又是高基维的维度,这种 Case 会严重影响查询性能,要及时通知用户进行优化。

下面讲一下 OLAP 平台数据可视化方面的工作。一个强大的可视化工具,是 OLAP 平台必备的组件,我们采用了开源的 Superset 方案。Superset 是 Airbnb 开源的、能与 Druid 深度集成的、交互式的、高效的、数据分析和可视化平台,它的功能非常强大,支持种类丰富的数据可视化的图表。

截至目前,我们的 Superset 已经积累了上万个图表,用户在使用 Superset 过程中也遇到很多问题,针对这些问题我们对 Superset 同样做了大量的改造。包括数据的同步、权限管理、报警功能、产品设计的一些交互改进等。

针对几个重点的改进点做下介绍,比如对多 time shift 的支持,所谓 time shift 就是可以在一张图里面同时绘制出来当前值与前一天同比和环比的指标对比。这里展示的是当前这一天与前一天,以及上周同天指标对比情况,用户可以加任意多的其他日期的指标对比到同一张图里面。除了这种时序线图之外,我们对其他图表也做了大量的 time shift 支持。

这里展示的是 Superset 同一个看板里面多个图表,在鼠标滑动窗口进行滑行的时候能够联动刷新的功能,对其中一个图表进行时间范围选择,其他图表能够关联进行刷新,这在进行多表关联分析的时候还是比较实用的。

这里展示的是 Superset 报警功能的设计。公司很多监控数据都是依赖 Druid 和 Superset 做数据分析,对报警需求也是非常强烈。我们参考了 Grafana 的报警功能的设计,在 Superset 上也实现了类似的功能,用户可以在平台上自定义一些报警维度、指标、检查周期、报警级别等。

在性能提升方面,我们做了时序和维度两个层面的物化视图以及元数据方面的交互优化。在资源管理层面,实现了 Supervisor indexing task 的自动伸缩、Middler Manager 细粒度资源分配以及并行 Compaction。在稳定性层面,设计了 Broker 和 Historical 的隔离部署。在平台易用性层面,自研了数据源的管理系统、数据探针系统,以及引入 Superset 数据可视化平台。

最后分享未来快手 OLAP 平台的一些工作计划。首先,我们会引入一些新型的 OLAP 的技术,比如 Clickhouse。第二,我们在考虑 OLAP 与 Adhoc,以及例行报表的整合,希望 OLAP 技术能够在离线数据分析方面也有更大的发挥空间。第三,从数据的流入到数据的可视化提供一站式的服务,降低技术人员和非技术人员的使用门槛。第四,希望平台能够从技术输出向产品化、服务化的方向去演进。

李远策,快手科技大数据平台架构师,数据查询引擎团队负责人。负责公司 SQL 引擎、OLAP 引擎、多维可视化平台的研发以及在公司的应用。曾供职于奇虎 360,是开源项目 XLearning 的作者。主要研究领域包括分布式计算、OLAP 引擎、SQL on Hadoop、AI on Hadoop 等。