图来自PexelseBay广告数据平台,为eBay第一方广告主(使用PromotedListing服务的卖家)提供广告流量、用户行为和效果数据分析功能。广告卖家可以通过卖家中心(SellerHub)的营销标签页、业绩标签页和公共API,有效控制和对比店铺的营销活动和促销产品流量、销售的实时和历史数据,并通过下载数据分析网页或API报告。上线之初,该系统采用了基于对象存储系统的自研分布式SQL引擎。3年前,随着广告流量的增加,我们把数据引擎换成了Druid。该平台的主要挑战如下:数据量大:每天插入数百亿条数据记录,每秒插入值峰值接近百万。离线取数据:在不影响实时取数据的情况下,需要每天在线更换前1-2天的数据。根据上游数据团队发布的清洗后的每日数据,广告数据平台需要在不影响查询的情况下每天更换实时数据。数据切换需要跨节点的全局原子操作。完整性、一致性:面向卖家的财务数据,离线更新数据要求无遗漏、无重复;实时数据要求端到端延迟在十秒以内。DruidvsClickHouseDruid是Metamarkets于2011年开发的一款高性能的列式在线分析和存储引擎。2012年开源,2015年成为Apache基金会项目,Druid在业界应用广泛。提供千亿级数据的亚秒级查询时延,擅长高可用和横向扩展。此外,它还为数据摄取提供了很多非常方便的聚合和转换模板,内置了对多种数据源的支持,最快可以在几十分钟内配置一个新的数据表,包括数据定义和数据摄取链接(LambdaArchitecture),大大提高了开发效率。ClickHouse由俄罗斯最大的搜索引擎公司Yandex开发,其设计目标是支持Yandex.Metrica(全球第二大网络分析平台)生成用户分析报告等核心功能。ClickHouse是一个数据库管理系统(DBMS),具有数据库、表、视图、DDL、DML等概念,并提供了比较完善的SQL支持。其核心特点如下:高效的数据存储:通过数据压缩和列式存储,可以实现高达10倍的数据压缩率。高效的数据查询:通过主键索引、向量化引擎处理、多处理器并发分布式查询,充分发挥CPU的所有能力,特别适合中小数据量。灵活的数据定义和访问:通过支持SQL语言、JDBC和关系模型,降低学习和迁移成本,并可与现有其他数据产品无缝集成。为什么要迁移?运维Druid提供了很多非常方便的数据摄取功能,但是它的组件构成也比较复杂。有6种类型的节点(Overload、Coordinator、MiddleManager、Indexer、Broker和Historical)。除了自身的节点,Druid还依赖MySQL存储元数据信息,Zookeeper选举Coordinator和Overlord,HDFS备份历史数据。ClickHouse的架构采用点对点节点的设计。只有一种类型的节点,没有主从节点。如果使用replica功能,则依赖Zookeeper来保存数据段的同步进度。同时,eBay的基础架构团队提出在定制ClickHouse的基础上,为产品团队提供列式数据库存储服务。除了运维和生命周期管理,基础设施团队对ClickHouse进行了改造和再开发,进一步提升了数据摄取和存储的效率,弥补了与Druid在离线摄取方面的功能差距。延迟数据插入Druid通过引入实时数据索引任务,将实时数据处理成段,并归档为历史数据。成为分段数据后,这段时间的数据就不能写入了。由于实时索引任务并发数的限制,我们设置了3小时的窗口长度(每小时一个任务),所以超过3小时的数据无法写入。在一些极端情况下,比如上游数据延迟或者实时数据消费来不及,这部分数据会在离线数据替换之前丢失。ClickHouse没有这个限制,任何分区都可以随时写入。主键优化ClickHouse支持的主键不是传统意义上的关系型数据库的主键。传统的主键要求每条表记录都有唯一的键值,通过查询主键可以唯一查询到一条表记录。而在ClickHouse中,主键定义了记录在存储中的排序顺序,并且允许重复,因此将其称为排序键似乎更合理。事实上,ClickHouse中的主键定义是通过ORDERBY声明的,只允许在个别场景下与sortkey不一致(但必须是sortkey的前缀)。由于我们的产品提供了卖家分析功能,几乎所有的查询都限制在单一的卖家维度,所以通过主键对卖家进行排序可以大大提高查询效率和数据压缩率。系统架构图1如图1所示,系统由4个部分组成:实时数据采集模块连接eBay的行为和交易实时消息平台。离线数据替换模块连接到eBay内部数据仓库平台。ClickHouse部署及周边数据服务。报告服务,支持广告商、商家后台和eBay公共API。实战经验Schema设计ClickHouse提供了丰富的Schema配置。这方面需要根据业务场景和数据模式反复考虑和多次尝试,因为不同的选择会对存储和性能产生一个数量级的影响,错误的选择会导致后期巨大的调优和变更成本。①表引擎ClickHouse存储引擎的核心是合并树(MergeTree),基于它衍生出:求和合并树(SummingMergeTree)、聚合合并树(AggregationMergeTree)、版本折叠树(VersionCollapsingTree)等常用的表引擎。合并树引擎有对应版本的复制函数(ReplicatedXXXMergeTree)。我们广告数据平台的展示和点击数据选择了复制、汇总和合并树。这两类用户行为数据极其庞大,减少数据量以节省存储成本和提高查询效率是模式设计的主要目标。ClickHouse在后台根据给定的维度对数据进行汇总,减少了60%的数据量。销售数据选择普通的复制合并树。一方面,销售数据对汇总以外的一些指标有汇总要求。另一方面,由于数据量本身并不大,合并数据的需求并不迫切。②主键一般情况下,ClickHouse表的主键(PrimaryKey)和排序键(OrderByKey)是相同的,但使用汇总合并树引擎(SummingMergeTree)的表可以单独指定主键。将一些不需要排序或索引功能的维度字段从主键中剔除,可以减小主键的大小(运行时需要将主键完全加载到内存中),提高查询效率。③压缩ClickHouse支持列级数据压缩,可以显着降低原始数据的存储容量,这也是列存储引擎的一个巨大优势。在查询阶段,较小的存储空间也可以减少IO量。为不同的列选择合适的压缩算法和级别,可以在压缩和查询之间取得最佳的性价比平衡。默认情况下,所有ClickHouse列都使用LZ4压缩。另外,一般的数据列可以选择LZ4HC、ZSTD等压缩比更高的算法。对于类似时间序列的单调增长数据,可选择DoubleDelta、Gorilla等特殊压缩算法。LZ4HC和ZSTD等具有高压缩率的算法也可以选择自己的压缩级别。在我们的生产数据集上,ZSTD算法对String类型字段的压缩效果非常明显。LZ4HC是LZ4的改进版,压缩比高,更适合非字符串类型。更高的压缩率意味着更少的存储空间,并且可以通过减少查询IO量来间接提高查询性能。然而CPU并没有被风吹走,数据插入性能成为牺牲品。根据我们的内部测试数据,与LZ4相比,在我们的生产数据集上使用LZ4HC(6)可以节省30%的数据,但实时数据摄取性能下降了60%。④Lowcardinality值得一提的是,对于基数较低(即列值多样性较低)的列,可以使用LowCardinality来减少原有的存储空间(从而减少最终的存储空间)。如果在使用压缩算法时对字符串类型的列使用LowCardinality,则可以再减少25%的空间量。在我们的测试数据集上,如果整张表结合LowCardinality、LZ4HC(6)和ZSTD(15),整体压缩率大约是原来的13%。离线数据替换①挑战广告商的数据报告需要准确和一致的数据。实时行为数据中有少量bot数据(需要线下清理),广告的归属也需要线下重新调整。因此,我们引入了离线数据链路,在实时数据写入后的24-72小时内,用离线数据替换实时数据。挑战如下:广告系统每天需要处理近1TB的用户离线数据。在此之前,将数据从Hadoop导入Druid需要花费大量时间。另外,导入时的I/O、CPU、内存等开销给查询带来了很大的压力。如何在保证数据一致性的同时又能保证数据迁移的效率是问题的关键。如何保证数据替换过程中用户可见的数据波动最小。这要求数据替换操作是原子的,或者至少对每个广告商都是原子的。除了日常的离线数据更新,当数仓数据出现偏差和遗漏时,还需要支持大规模的数据修正和补偿。作业调度要求保证日常工作按时完成,数据修正工作尽快完成。此外,还需要监控数据更新中的各种指标,以应对各种突发事件。Druid原生支持离线数据更新服务,我们和基础设施团队一起在ClickHouse平台上实现了这个特性。②数据架构对于融合线上数据和线下数据的大数据架构,业界通常的做法是Lambda架构。即离线层和在线层分别导入数据,在展示层合并数据。我们也主要采用了这种架构。但具体做法与经典有所不同。ClickHouse中的数据分区是一个独立的数据存储单元,每个分区都可以独立地从现有表中分离、附加和替换。分区条件可以自定义,一般按照时间来划分。通过数据表中数据分区的单次替换,我们可以让查询层对底层数据的更新透明,不需要额外的数据合并逻辑。③Spark聚合分片为了降低ClickHouse导入离线数据的性能压力,我们引入Spark任务对原始离线数据进行聚合分片。每个分片可以单独拉取和导入数据文件,节省数据路由和聚合的开销。④数据更新任务管理锁定分区拓扑:离线数据更新系统在处理数据之前,向基础设施团队提供服务请求,锁定ClickHouse的分区拓扑,在此期间分区的拓扑不会发生变化。服务器根据预先定义的数据表结构和分区信息,返回数据的分片逻辑和分片ID。离线数据更新系统根据拓扑信息提交Spark任务。多张表的数据处理通过Spark并行完成,显着提高了数据更新的速度。数据聚合分片:对于每一张需要更新的表,启动一个Spark任务对数据进行聚合分片。根据ClickHouse服务端返回的表结构和分片拓扑将数据写入Hadoop,同时在数据替换阶段输出校验和以及用于校验一致性的分片行数。系统通过LivyServerAPI提交和轮询任务状态,如果有任务失败则重试,排除Spark集群资源不足导致的任务失败。离线数据更新不仅需要满足每天批量更新数据的需求,还需要支持对过去数据的再次更新,以同步上游数据超出每天定时任务更新的数据变化。我们使用平台团队封装的SpringBatch来管理更新任务,将每天的数据按照日期划分为子任务。通过SpringBatch实现的ContinuouslyJob保证了同时运行的子任务的唯一性,避免了任务竞争问题。对于过往数据的更新,我们将Batch任务分类。除了日常任务,还可以手动触发给定时间范围内的数据修正任务(如图2所示)。图2数据替换:子任务中的所有Spark作业完成后,离线数据更新系统会调用基础设施团队提供的数据替换接口,发起数据替换请求。服务器根据定义的分区直接从Hadoop写入数据到ClickHouse,如图3所示。图3离线数据更新系统架构如图4所示:图4MySQL数据库用于记录状态和优先级数据替换过程中的任务,当SparkJob失败或替换任务因其他原因失败重启时,恢复任务的进度。⑤原子性和一致性为了保证数据替换的原子性,基础设施团队提供了分区替换的方法。离线数据导入时,首先创建目标分区的临时分区。数据替换验证完成后,目标分区将被临时分区替换。为了解决不同机器上不同分片的原子替换问题,基础架构团队为每条数据引入了一个数据版本。对于每个数据分区,都有一个对应的活动版本号。待替换数据分区的所有分片导入成功后,更新分区的版本号。上游应用中同一条SQL只能读取同一个分区的一个版本的数据,每个分区的数据替换只感觉一个切换,不存在同时读取新旧数据的问题。广告平台报表生成应用因此在SQL层面引入了相应的修改。通过引入固定的WITH和PREWHERE语句,在字典中查询每个数据分区对应的版本号,将不需要的数据分区排除在查询计划中。.为了保证数据替换的一致性,Spark数据处理完成后,离线数据更新系统会计算每个数据分片的校验码和数据总量。替换完成后,ClickHouse服务器会对分片数据进行校验,确保数据迁移过程中不存在数据丢失和重复。数据查询ClickHouse支持SQL查询(不全),HTTP和TCP连接方式,官方和第三方的查询工具和库比较丰富。用户可以使用命令行、JDBC或可视化工具快速开发和调试数据查询。ClickHouse通过MPP(MassivelyParallelProcessing)+SMP(SymmetricMultiprocessing)充分利用机器资源,单条查询语句默认使用一半机器核心的CPU。所以ClickHouse不支持高并发的应用场景。在业务使用层面,核心问题是查询校验和并发控制。单个过大的查询或高并发会导致集群资源占用率高,影响集群稳定性。应用架构eBaySellerHub通过ReportsService接入ClickHouse查询,ReportsService提供了两套API,Public和Internal。内部API提供给卖家中心和其他内部已知应用程序。公共API对eBay开发者计划中的第三方开发者开放。详见:https://developer.ebay.com/图5内部API查询直接提交给内部线程池执行,线程池的大小根据ClickHouse集群机器数设置。查询请求在执行前会进行校验,过滤掉所有非法和不可预知的资源请求。公共API通过任务提交异步执行查询。用户提交的查询任务存储在DB中,Service内部的Schedule定时扫描表,根据任务的状态顺序执行查询任务。执行成功的任务上传并生成到文件服务器,用户获取URL后下载报表。执行失败的任务,根据错误类型(非法请求、资源不足等)选择是否在下一个周期再次执行。测试发布部署到生产环境后,我们开启了数据双写,不断将实时数据和离线数据插入到ClickHouse中,直到达到Druid的数据级别。在数据一致性校验之后,我们镜像一个生产服务查询,然后将这些查询转发给ClickHouse。通过收集和比较Druid和ClickHouse的响应,我们能够验证ClickHouse链接的数据质量和查询性能。在后续的灰度阶段,我们逐步增加ClickHouse服务生产系统的占比,并保持Druid运行,以保证问题能够及时回滚。在查询GUI数据可视化方面,我们需要为开发、测试和BI人员提供类似Turnilo的可视化工具。ClickHouse支持接入多种商业和开源产品。我们选择了Cube.JS,进行了简单的二次开发。图6总结了本文介绍广告数据平台的基本情况,ClickHouse/Druid的特点对比以及团队用ClickHouse替代Druid的架构方案。ClickHouse表现出了良好的性能和可扩展性,并且还在快速更新中。目前,该项目已经启动。接下来,我们将继续与大家分享过程中遇到的一些问题和解决方案。欢迎继续关注。作者:吴汉思、周璐、于河编辑:陶家龙来源:转载自公众号易趣科技(ID:eBayTechRecruiting)
