京东零售数据湖应用与实践

news2024/10/11 5:25:17

作者:陈洪健:京东零售大数据架构师,深耕大数据 10 年,2019 年加入京东,主要负责 OLAP 优化、大数据传输工具生态、流批一体、SRE 建设。

当前企业数据处理广泛采用 Lambda 架构。Lambda 架构的优点是保证了数据的完整性,但缺点是系统的复杂性较高,需要维护两套系统,并且服务层的复杂合并逻辑可能会导致延迟。为了解决数据的完整性和实时性之间的矛盾,京东零售在数据架构上做出了一系列的革新。

本文将从以下四个方面展开介绍:

1. 背景和痛点

2. 迭代和优化

3. 效果和收益

4. 未来展望和规划

01 背景和痛点 

1. 数据实时性和完整性的矛盾

Lambda 架构设计的初衷是同时提供即时的实时数据处理和高度精确的批量数据处理,但是这种架构也带来了数据实时性和完整性的矛盾。

在线分析和在线训练场景需要数据具备实时性(T+0)和一定的历史数据。但当前实时数据和历史数据分别存储在不同介质中,使得在需要同时具备实时性和完整性的数据应用场景下,用户需要分别对接不同的系统,使用不同的 API 实现需求,并且需要接受口径差异问题,这样的设计低效且不友好。

2. 架构维护成本高

当前京东的数据处理架构分为离线处理和实时处理两条链路,离线处理的流程分为业务接入、采集服务、埋点数据存储、数据入仓、数仓 BDM 层、数仓 FDM 层、数仓 GDM 层等。实时处理的流程分为业务接入、采集服务、Kafka 缓存/Topic 划分、Flink 处理、Kafka 缓存/Topic 划分等。这样的架构存在着以下问题:

  • 离线批处理的 ETL 任务繁重,当前的埋点日志入仓采用自运维的 Plumber 任务,对物理机资源有强依赖,日常需求达到百台,大促期间更需大量扩容。但整个互联网的趋势是降本增效,如何在减少物理机使用的情况下满足业务需求成为我们需要解决的问题。

  • 实时数据为达到秒级处理,通常采用 Kafka+Flink 的架构实现,整体计算和存储资源消耗较高。实际业务中存在着低优先级或者实时性要求不高的场景,在目前的架构下无法灵活实现,存在资源浪费的情况。

  • 离线处理的链路冗长,不含中间表的情况下,也需要至少四层的计算。另外,T+1 批处理的时间集中,如果遇到数据量级波动,网络堵塞,或者机器故障等情况,都会严重影响任务产出。比如波动时 GDM 资产完成时间可能超过 4:00,任务爆发雪崩并开始集中抢占资源,导致大量任务延迟。

3. 状态数据的更新和存储问题

在当前的数据仓库架构中,数据状态的更新是一种重量级操作,它的操作方式是将分区内全部数据重写,即使其中的大部分数据没有发生变化。这不仅浪费了大量的计算资源,也降低了系统的效率。另外,为了能快速查询到历史时刻的数据快照,我们每天或每小时都要存储全量数据,这同样消耗了大量的存储资源。

举个例子,对于流量数据,我们通常关注 PV 和 UV,也就是累加的数量和去重的数量,那么按天增量存储就很容易计算出相应的指标,计算最近三十天的访问量只需要累加最近三十天每天的数量即可;但是对于存在 Update 场景的数据,比如每天商品都会发生增加,删除和修改,那么我们计算 SKU 和 SPU 等指标,主流解决方案就是每天加工一份商品全量表,更进一步考虑如果每天商品的变化数量只在 10%,却要按天产出全量数据,那么数据的重复存储,以及数仓中每一层的大量计算,都使得数据产出的性价比较低。

02 迭代和优化 

针对上述架构、更新和存储中存在的问题,我们进行了一系列的改造。

1. 架构变更

  • 流量涉及的生产库写实时 Topic:原先埋点数据采集过后写入 CFS,HDFS 接入 CFS 数据开始入仓,改造后 CFS 上的数据成为实质上的 Topic。

  • 将处理的离线 MR 作业改为流处理的 Flink 作业:使用 Flink 任务采集 CFS 的 Topic 数据,来代替数仓中使用 MR 做引擎的 ETL 任务,提升数据时效。

  • 将数据通过 Flink 作业写入 Hudi 表:Hudi 旨在将流处理和批处理的优势结合起来,允许处理增量数据,这意味着可以仅处理自上次查询以来发生变化的数据,而不是每次都加载整个数据集;同时提供了索引和事务的支持,如 Bloom Filter 索引和列值索引有助于查询加速,对事务的支持可以保证多并发写入下的数据一致性。

  • 对数据进行逻辑加工和不同表的 JOIN,生成 GDM/RDDM 对外开放模型表。

2. 多流合并

如下图所示,商品后台包括自营、pop、book 和其它一些业态,从生产库实时抽取 binlog 日志,生成对应的流。在 BDM 层,直接通过 Flink 任务将对应的 binlog 日志,变成 Hudi 的 BDM 表。在 BDM 到 FDM 层,做了一些简单的加工。再往后是全量商品表和不同维度表的更新。在这一流程中,具体的改进点如下:

(1)存储模型变为分区表+MOR+Bucket 的组合来提升性能

  • 降低list 操作频次、计算离线往期分区大小,Bucket 不超过 2GB

  • 为了减少小文件,将非分区表改为了分区表

  • 限定保留版本数 288/分钟、25/小时(版本数*平均提交周期),定时 clean、Archive

  • Flink fdm 层'compaction.async.enabled' = 'false',spark 层创建合并任务进行异步 Compaction 操作

  • Flink 切换到 Spark 引擎 eventtime.field=ts 保持数据更新规则一致

(2)降低成本

  • 多表资源复用,把原本分散在各个业务形态中的数据进行了合并处理,从而降低资源成本

  • 建设 DMS 系统自动建表,表增删改统一管控收口,创建相关任务,并实现了对任务状态和异常的可视化,使异常定位和处理变得非常便捷,从而降低了人力成本

(3)数据一致性

  • 数据保序:表主键 Hash 分组传输

  • 数据完整性:根据 Hudi 的心跳机制和业务的时间窗去判断数据的完整性, Precombine=业务时间,多个时间编写多时间 payload 函数进行更新

(4)可持续性

  • 健壮性,对数据积压、任务异常、数据时延等创建监控策略进行监控

  • 元数据更新,业务变更带来的分析库结构变更

  • 稳定性,实现了资源隔离,保证上游集中刷数、定时跑批时的稳定性

3. 外键关联

Hudi 在大表的外键关联场景下存在问题,为了保证数据的完整性和准确性,我们的解决方案是整合 Flink 和 Spark。流转批 eventtime 下发,具体做法为,每 10min 一批次,执行以下操作:

  • SKU 增量数据关联维表(SPU)全量数据

  • SPU 增量数据关联 SKU FDM 全量数据

  • union 后写入 m03 表

关联复杂降低策略:分主体进行维度建模,分层存储,对中间业态采用临时表。

过载控制:记录级限流,资源配置模型。

开发方式:FlinkSQL+SparkSQL 能力增强:

  • Hudi 维度表的能力,维表 lookup

  • MOR 表增量读优化,优先读取 Log 文件

  • Spark 与 Flink 混写一致性优化(索引、数据格式、eventtime 等)。spark 任务 compaction 数据 call run_compaction(op => 'run', path => '{path}');

  • 状态后端表 TTL 设定,表级别 TTL

  • 持续稳定:异常恢复、监控告警增强,对数据积压、限流、checkpoint 失败、处理流量等问题及时处理。

4. 查询优化

(1)数据缓存

  • Hudi 元数据缓存

  • Block 级文件缓存:通过将外部存储系统的原始数据按照一定策略切分成多个 block 后,缓存至 StarRocks 的本地 BE 节点,从而避免重复的远端数据拉取开销,实现热点数据查询分析性能的进一步提升。

(2)异步物化视图

物化视图是特殊的物理表,能够存储基于基表的预计算结果。当对基表执行复杂查询时,StarRocks 可以自动复用物化视图中的预计算结果,实现查询透明加速、湖仓加速和数据建模等业务需求。

具体实现如下:

  • 本地存储加速:物化视图可以利用 StarRocks 的本地存储加速优势,如索引、分区分桶和 Colocate Group,从而相较直接从数据湖查询数据具有更好的查询性能。

  • 无需维护加载任务:物化视图通过自动刷新任务透明地更新数据,无需维护导入任务。此外,基于 Hive、Iceberg 和 Paimon Catalog 的物化视图可以检测数据更改并在分区级别执行增量刷新。

  • 智能查询改写:查询可以被透明改写至物化视图,无需修改应用使用的查询语句即可加速查询。

03 效果和收益 

以上介绍了我们整体架构的优化,在抽取数据时,通过 Flink 对数据进行加工,生成大表做连接时又利用了 Spark 的相关能力,最终在 BI 查询部分,又通过 StarRocks 进行了加速。这些优化为我们带来了诸多收益。

1. 时效提升

实现了离线数据的近线时效,原本 3:00-4:00 才能完成的计算现在提升到了 0:00-0:20,仅用 20 分钟即可完成。另外,通过增量式的数据处理链路,最大化地提高了数据产出的时效性。

2. 作业效率提升

降低了构建大宽表的资源成本,将数据修改原子化(刷数、刷岗),使效率得到了大幅提升。当前 BC 每月例行刷数,需要刷 ADM 层-APP 层-在线存储层(ClickHouse)至少三层,涉及交易、用户、财务主题,约数十任务,每个任务都需重新处理数千 E 左右数据,但是 BC 维度变化影响数据量不足 5%,其中有 95% 的不变数据在浪费资源,后续可做到只修改变化数据。

3. 存储节约

数据由快照改为增量存储,降低了存储代价,同时支持回看有状态的历史快照。当前全量商品数千亿,为了能回看历史每天全量存储,一年共消耗约数 PB,使用 Time travel +Savepoint 能力,一年减少存储 90%。

4. 统一口径和 API

采用流批一体的计算链路,统一了计算引擎,天然做到了数据口径一致,较 Lambda 架构降低了 50% 的维护和对接成本。对外实现了离近线一套查询 API,业务方无须异构取数,有效提高了算法侧迭代和 AB 实验的效率。

5. 查询分层

使数仓具备索引能力,降低了模型使用的开销,提升了查询效率,同时可以直接对接主流引擎(Trino、ClickHouse、StarRocks 等),实现了查询分层。

04 未来展望和规划 

目前我们正在推进以下改进:

  • 容灾措施(机房宕机、任务重启、数据修复等)。

  • 与批任务的资源隔离,实现弹性伸缩能力,优化资源消耗。

  • 针对 Hudi 流式写入带来的小文件问题,我们尝试了通过定时的 compaction,以及分桶、分区等方式,进一步将开发一些插件使问题得到自动的解决。

  • 数据免疫系统建设。

  • 提升 Hudi 表的自管理能力,降低维护成本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2204201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

毕业设计选题:基于php+vue+uniapp的新闻资讯小程序

开发语言:PHP框架:phpuniapp数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:PhpStorm 系统展示 管理员登录界面 管理员功能界面 新闻类别管理 新闻信息管理 用户管理 管理员管…

云栖实录 | 大模型驱动,开源融合的 AI 搜索产品发布

本文根据2024云栖大会实录整理而成,演讲信息如下: 演讲人: 郭瑞杰 | 阿里云智能集团资深技术专家,阿里云 AI 搜索负责人 邹 宽|阿里云智能集团高级产品专家,阿里云 AI 搜索产品负责人 活动:…

【CSS Tricks】鼠标滚轮驱动css动画播放,使用js还是css?

目录 引言一、js实现1. 实现思路2. 实现案例3. 看下效果 二、css实现1. 代码修改2. 属性介绍2.1 看下浏览器支持性2.2 常用属性值2.2.1 scroll()2.2.2 view() 三、总结 引言 本篇为css的一个小技巧 页面中的动画效果随着滚轮的转动…

Unity 从零开始的框架搭建1-2 事件的发布-订阅-取消的小优化及调用对象方法总结[半干货]

该文章专栏是向QFrameWork作者凉鞋老师学习总结得来,吃水不忘打井人,不胜感激 Unity 从零开始的框架搭建1-1 unity中对象调用的三种方式的优缺点分析【干货】-CSDN博客 原来 其实就是对上一节的事件发布订阅类的小优化,原来是这样子的 p…

达梦DBLINK访问ORACLE配置方法

目录 1、概述 2、测试环境 3、语法简介 4、配置访问DM的DBLINK 5、配置访问ORACLE的DBLINK 5.1 通过OCI配置 5.2 通过ODBC配置 1、概述 本文介绍了达梦DBLINK的配置方法。有3部分内容,1)达梦访问到达梦的配置方法;2)通过OC…

视频切分成指定大小片段

某些时候,由于上传限制,我们可能想把视频切分成尽量少且满足大小限制的片段,不改变视频原先的格式 实现思路:得到视频的总时长,总文件大小,根据大小限制,确定分割片段个数, 得到每段…

rpa批量发送邮件如何通过编辑器编发邮件?

rpa批量发送邮件的技巧?怎么使用rpa邮箱群发助手? 手动发送邮件变得越来越繁琐且效率低下。为了解决这一问题,越来越多的企业开始采用RPA技术来批量发送邮件。AokSend将详细探讨如何通过编辑器来实现rpa批量发送邮件的功能,从而提…

75.【C语言】文件操作(3)

目录 6.文件的顺序读写 1.几个顺序读写函数 1.fgetc函数 代码示例 代码改进 2.fputc函数 3.fputs函数 如果需要换行,应该写入换行符(\n) 4.fgets函数 1.读取单行字符串 2.读取多行字符串 6.文件的顺序读写 1.几个顺序读写函数 分组:(fgetc,fputc),(fgets,fputs),(f…

如何快速给word文件加拼音?请跟着步骤完成吧

如何快速给word文件加拼音?在日常工作中,我们时常会遇到需要为Word文件中的文字添加拼音的情况,这尤其在教育、出版或国际交流等领域显得尤为重要。为文字配上拼音,不仅能帮助学习者准确发音,还能提升文档的可读性和普…

3.6.xx版本SpringBoot创建基于Swagger接口文档

介绍 基于Swagger构建的JavaAPI文档工具&#xff0c;实现后端功能的测试&#xff0c;并撰写API接口文档。 方法 pom.xml中引入依赖,要注意的是&#xff0c;本依赖使用的SpringBoot版本为3.6.xx <!--Knife4j--><dependency><groupId>com.github.xiaoymin<…

W25Q64学习 非易失性存储器

嵌入式开发之Nand-Flash和Nor-Flash的区别_nand flash谁定义的-CSDN博客 w25q64是nor FLash 用SPI通信 W25Q64模块硬件电路&#xff0c;这里的HOLD,WP功能都没用到 对于w25q64整个存储空间&#xff0c;划分为128个块&#xff0c;对于每个块&#xff0c;划分为16个扇区&#…

【python实操】python小程序之如何使用私有公有属性和方法

引言 python小程序之如何使用私有公有 文章目录 引言一、如何使用私有公有属性和方法1.1 题目1.2 代码1.3 代码解释1.3.1 逐行解释1.3.1 代码行为总结 二、思考2.1 名称修饰2.2 总结 一、如何使用私有公有属性和方法 1.1 题目 如何使用私有公有属性、方法 1.2 代码 class P…

Python快速编程小案例——打印蚂蚁森林植树证书

提示&#xff1a;&#xff08;个人学习&#xff09;&#xff0c;案例来自工业和信息化“十三五”人才培养规划教材&#xff0c;《Python快速编程入门》第2版&#xff0c;黑马程序员◎编著 蚂蚁森林是支付宝客户端发起“碳账户”的一款公益活动:用户通过步行地铁出行、在线消费等…

华为云应用侧Android Studio开发

本文将介绍如何使用AndroidStudio开发APP完成与接入华为云IoTDA设备的对接&#xff0c;包括属性参数获以及取命令下发。 一、鉴权认证 应用侧需要通过IAM服务鉴权&#xff0c;获取token&#xff0c;华为账号创建 IAM 用户&#xff0c; 可以为创建的用户分配权限 认证鉴权_设…

开源全文搜索(搜索引擎)

吃水不忘挖井人&#xff0c;介绍Doug Cutting大牛是十分有必要的。 最早&#xff0c;接触到搜索引擎&#xff0c;知道有个Nutch&#xff08;开源搜索引擎&#xff09;&#xff0c;于是开始查看Nutch相关的资料&#xff0c;发现了Nutch的创始人Doug Cutting&#xff0c;随着项目…

Python 如何使用 Redis 作为缓存

Python 如何使用 Redis 作为缓存 一、引言 在现代 Web 应用程序和数据密集型服务中&#xff0c;性能 和 响应速度 是至关重要的因素。而当应用需要频繁访问相同的数据时&#xff0c;直接从数据库获取数据会耗费大量的时间和资源。因此&#xff0c;缓存系统成为了提升性能的重…

做一只由 OpenCV 控制的仿生手

这个项目介绍了如何制作和控制一只仿生手。作者最初受到Instagram上一个视频的启发&#xff0c;该视频展示了使用MPU6050传感器追踪手部动作并在屏幕上显示3D模型。作者决定将这个想法进一步发展&#xff0c;使用OpenCV来控制一只真实的仿生手。 大家好&#xff0c;在这篇教程中…

强大的PDF到Word转换工具

Solid Converter&#xff1a;强大的PDF到Word转换工具推荐 在日常工作和学习中&#xff0c;PDF是最常用的文件格式之一。然而&#xff0c;编辑PDF文档并不总是那么方便&#xff0c;尤其是当你需要将PDF文件转换为Word文档时。Solid Converter 是一款强大的工具&#xff0c;专为…

SpringBoot美发门店系统:数据驱动的决策

3系统分析 3.1可行性分析 通过对本美发门店管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本美发门店管理系统采用SSM框架&#xff0c;JAVA作为开发语…

7.并发编程之并发安全问题

1 线程安全性 什么是线程安全性&#xff1f;可以这么理解&#xff0c; 我们所写的代码在并发情况下使用 时&#xff0c;总是能表现出正确的行为&#xff1b;反之&#xff0c;未实现线程安全的代码&#xff0c;表现的行为是不可预知的&#xff0c;有可能正确&#xff0c;而绝大多…