本文内容来自 Community Over Code Asia 2025 大会 (CommunityOverCode 是 Apache 软件基金会(ASF)的官方全球系列大会,其前身为 ApacheCon),OLAP & Data Analysis track 分享议题。本文主要介绍了 Apache Doris 在菜鸟的大规模落地的实践经验,数据分析已经渗透到每个业务线的同学,每天在不同的数据分析报表、数据产品上查数和用数,OLAP 数据库在其中承担着重要作用。我们为什么选择 Doris,以及 Doris 如何在菜鸟从 0 开始,一步步的验证、落地,到如今上万核的规模,服务于各个业务线,Doris 已然成为菜鸟 OLAP 的最优选型。 当然这背后是我们为此做了大量的工作,而这篇文章将围绕此分享我们整个过程的建设经验。
本文目录预览如下:
一、背景
1.1 菜鸟介绍
菜鸟成立于 2013 年,是电商物流行业的全球领导者。菜鸟孵化于阿里巴巴全球最大的电子商务生态系统中,构建起了一张全球智慧物流网络,通过不断创新,以满足高速增长的复杂电商物流需求。领先的科技能力,与深刻的电商理解相结合,让菜鸟在每一个业务领域均为领导者。菜鸟是全球第一的跨境电商物流公司,现设有中国、欧洲、亚太、美洲、中东&非洲 5 个大区,围绕全球快递、全球供应链、全球科技三大核心业务持续建设。通过“全球 10 日达”、“全球 5 日达”等颠覆性解决方案帮助中小企业开展跨境贸易。作为中国顶尖的电商综合供应链解决方案提供商,帮助品牌和商家轻松应对全渠道的供应链复杂难题。凭借开创性的逆向物流产品,同时也成为中国最大的逆向物流解决方案提供商。
- 全球第一的跨境电商物流公司、拥有全球最大的物流网络之一:物流网络覆盖全球 200+ 国家和地区
- 全球最大的物流仓库网络之一:全球 1,100 多个仓库,总建筑面积约 1,650 万 ㎡。
- 全球最大的跨境电商快递服务企业:2024 财年,菜鸟全年日均跨境及国际包裹量超 500 万件,规模超过目前全球头部物流企业。
- 全球最大数字末端网络和物流应用
- 菜鸟驿站是中国首个亦是全球最大的数字化未端网络,每天处理快递量超 8,000 万个。
- 菜鸟 APP 让消费者更方便地查看、提取及寄送包裹,是全球最大的物流 APP,平均月活用户超过 6,000 万。
1.2 实时数据架构
菜鸟的实时数据架构经过最近 3 年的优化和迭代,在选型上已经逐步收敛。
- 流计算引擎: 流计算引擎可以说在菜鸟也经历了百花齐放的年代,从最初业务线都存在自建的计算引擎,当然大部分的出发点都是为了解决业务当下的痛点问题(比如:面向物流行业的长周期大状态问题),而经过 3 年的发展,已经逐步收敛到 Flink 和自研的大状态流计算引擎上。
- 存储引擎: 在早期的时候,我们确实存在想通过一种存储引擎解决所有业务问题的想法,但实际操作起来发现无法实现,没有任何一个存储可以做到成本&稳定&研发效率都拉满的银弹存储。 不同的业务场景往往需要选择最适合的存储,从横向上我们大体归为 4 大类,分别是 OLAP、HTAP、NOSQL、搜索。每大类的垂直细分是我们主要的发力点,比如在 OLAP 上,Doris 已经逐步变成菜鸟在 OLAP 上的最优选型,而其他垂类我们也基本收敛到 1 到 2 种选型。
1.3 为什么选择 Doris?
面向成本友好型的 OLAP 选型,一直是大家探讨最多的话题,尤其在菜鸟仓储业务,在成本和稳定性上是非常痛的一个点,最近几年除了做精细化的集群治理,还要面向业务做合理的数据架构,在这上面我们投入了很大的精力,但在成本和稳定性上均无法达到预期的终态,所以我们在早期也同步在积极探索更高性价比的 OLAP 选型。
而 2 年前,Doris 逐渐走入到我们的视野。 作为 Apache 软件基金会的顶级项目,Apache Doris 在开源持续性方面具有显著优势:一方面,Apache 基金会的治理模式确保了项目的长期稳定发展和社区驱动的创新;另一方面,开源的透明性让我们能够深入了解技术架构,避免厂商锁定风险,同时活跃的 Doris 社区也为我们提供了丰富的技术支持和最佳实践分享。
在前期我们做了大量的调研和性能以及稳定性测试,面向业务视角,成本和稳定性的最重要的两个指标,面向我们自己,运维效率是首要关注的指标,这涉及到未来的大规模推广和部署,而 Doris 在这三项指标上,均超出我们的预期。
值得一提的是,物流数据主要来源于 OLTP 数据库的实时同步,其中的业务数据持续更新(如订单状态变更、物流轨迹跟踪、库存变动等)、查询实时性要求高、业务场景复杂等特点,传统批处理 OLAP 系统难以满足这些需求,而 Doris 的实时更新能力恰好解决了这些痛点:
Doris 默认所有场景都采用 MOW(Merge-on-Write)模式,结合 Delete Bitmap + Primary Index 技术实现。相比传统 Copy-on-Write 机制,MOW 模式通过 LSM tree 数据组织和主键索引优化,在写入阶段为旧数据打上删除标记,查询时直接跳过已标记删除的数据行,无需实时计算删除逻辑。这种设计使得大部分场景下数据写入后可在秒级可见,查询响应时间稳定在百毫秒以内,足以支撑物流场景对实时性的要求。比如补货业务的延迟要求基本都在 1 到 2 秒内数据需要可见。(对延迟要求不高的,Doris 一般 10 秒内可见)
核心业务场景的应用价值 :
- 补货业务 :商品库存变化需要实时反映到补货系统,Doris 的秒级数据可见性确保补货决策基于最新的库存状态,避免缺货或过量采购。
- 库存管理 :仓库内商品的入库、出库、调拨等操作产生的库存变化需要实时更新,Doris 的高并发写入能力和强一致性保证了库存数据的准确性和实时性。
- 订单处理 :从订单创建、支付确认、拣货、打包到发货的全流程状态变更,都需要实时更新并支持高频查询,Doris 的主键表和 UPSERT 语义能够更好地支持这类场景。
- 物流轨迹 :包裹在运输过程中的位置信息、状态变更需要实时更新,为客户提供准确的物流信息,Doris 的实时更新能力确保了轨迹信息的及时性和准确性。
通过 Doris 的实时更新能力,菜鸟物流不仅提升了业务响应速度,也为构建面向用户的实时数据服务奠定了技术基础。这些技术优势是我们选择 Doris 作为 OLAP 最优选型的重要考量因素。
1.4 Doris 在菜鸟的这 2 年
Doris 在菜鸟的这 2 年,从时间轴看,其实走得并不快,我们没有急于求成,而是确定好每个里程碑,把每个时间节点把控好,做到极致。可以看到在 2023 下半年,我们只做了一件事,选一个代表性的核心场景做验证,开始并没有选择在小场景做验证的原因是,如果在最重要的核心场景都无法验证通过,那基本不可能推动业务侧做后续的迁移。在 2023 年的双 11,我们第一个小集群完成大促的验证,当时这个集群规模只有 300 多 CU。核心场景验证通过后,2024 年新财年伊始,我们确定了 Doris 大规模推广的计划和方案,内部完成项目 KO。
9 月首批核心集群全部完成迁移,11 月第一次大规模部署征战双 11,在成本和稳定性上均表现出色。截止到目前,Doris 在菜鸟已经有 25+ 集群,遍布 3 个地域(已具备全球化多地域部署能力),日常上万核的规模,整个迁移过程,未发生一起线上故障。在应用场景上,除了 OLAP 分析,菜鸟也探索应用了 Apache Doris + Apache Paimon 的湖仓一体解决方案。
二、从验证到大规模迁移到挑战
2.1 核心场景验证
在最开始的验证上,我们选择了仓内数据产品使用频率最高的包裹生产进度场景,这个场景的主要用于仓库的生产进度监控,订单结构分析,出库达成监控,排班排产等生产场景。此场景是比较典型的多表级联的 AD-HOC 场景,涉及到的维度指标组合很多以及多张亿级别大表 Join,并且对稳定性的要求也是极高,不能容忍出现数据延迟,查询超时,否则极易造成故障。
在验证过程中,我们先选择了新老集群双跑方案,并没有急于做灰度切流的方式,我们利用流计算的能力,1:1 回放线上 SQL,验证 SQL 的查询 RT 是否达到预期,以及语法上的兼容性。持续跑了一段时间后,才开始进行仓粒度的灰度切流。灰度切流的过程中,逐步对 Doris 集群扩容,直至 100%流量全部切到 Doris。
凭借 Doris 其完整的实时更新技术,在菜鸟物流场景中展现了卓越的性能表现:
- 点查场景:单表主键查询 QPS 可达 1000-2000,查询 RT 在几十毫秒到 100-200 毫秒之间。这得益于 Doris 的主键索引技术和 LSM tree 存储结构,能够快速定位和检索数据。
- 多表 join 聚合查询:复杂的多表关联聚合查询一般在 1 秒内返回结果,即使是非常复杂场景也能在 4-5 秒内完成。Doris 通过向量化执行引擎、分区分桶裁剪和 CBO 优化器的协同作用,大幅提升了复杂查询的执行效率。
在包裹生产进度这个场景,我们取得了阶段性的成果,在成本和性能上远超我们的预期,成本上降低了 90%,平均 RT 降低 72%。
2.2 大规模迁移的事前准备
在核心场景的成功验证后,我们着手开始为大规模迁移做事前的准备工作,主要工作归纳为六大类:
- 语法兼容性: 从 A 存储迁移到 B 存储,必然会存在语法兼容性的问题,但好在 Doris 也是高度兼容 MySQL 语法,我们只需做少量的兼容适配。
- 全量同步: 针对历史全量数据同步到 Doris,涉及到 A 存储全量数据同步到 Doris,由于没有开放 binlog,大表的全量同步相对比较艰难。
- 离线加速: ODPS 离线数据同步到 Doris,在原有能力扩展,支持 Doris 自动建表和写入。
- 数据导出: 导出在业务侧是一个很高频的场景,我们要兼容原有系统,所以 select...outfile 是我们首选的方式,改造量相对较低。
- 增量同步: 增量数据同步的场景,主要工作在适配我们自研的流计算引擎上,支持 Doris output 的能力上。
- 全链路血缘: 基于我们现有全链路血缘能力,加速业务侧数据链路的梳理,对迁移工作的提速起到了关键作用。
2.3 大规模迁移到挑战-语法兼容
在语法兼容这块,最初的方案考虑到业务存在较多 SQL 改写的成本,想在数据服务层中做适配,做方言的转换,但实际操作下来,我们 case by case 分析后,发现语法不兼容的种类没有想象中的多,所以我们选择了投入成本更低的方式,我们将这些语法不兼容的问题全部梳理归类,给出改写建议,业务侧只需按 case 改写即可。
如果遇到语法不支持的,无法改写的,只能进行二开,比如业务场景用的最高频的 case 就是多值列,类似指定分隔符拼接的一个字符串,用户可以任意匹配分隔后的一个或多个。在原存储中有明确的数据类型和语法支持,而在 Doris 中,最开始我们考虑使用数组或正则替换,但性能和稳定性无法满足预期,而能够最优适配的只有倒排索引的检索函数 match_[any | all | phrase],但目前倒排索引不支持按照指定分隔符分词,为了满足业务场景,我们在倒排索引分词器的基础上增加了指定分隔符分词并构建倒排索引的能力,最终完成这一语法的支持。
2.4 大规模迁移的挑战-数据导出
数据导出是业务侧每天都要使用的一个高频场景,用于做数据分析,我们有统一的导出服务,会对接底层的存储,在原存储上也类似使用 select into 到 oss 的方式,所以在 Doris 上,我们也要优先支持这一能力,但在测试的时候发现导出的中文全是乱码,并且官方文档中也并没有相关的编码参数可以配置。查看 Doris 输出 OSS 部分的源码, Content-Type 设置的为 application/octet-stream,octet-stream 是通用的二进制流类型,用于表示未知或非标准格式的文件,如果文件流中未显示指定编码格式,excel 打开会乱码。所以我们在输出流增加了 bom 头标记来解决此问题。
2.5 大规模迁移的挑战-数据同步
在原存储未开放可用的 Source connector 和日志的条件下,进行全量数据同步是我们比较头疼的点,我们采用了两种方式:
- 基于切分键的 Range Split 拉取方式: 我们采用了类似 datax 的 range split,源表的表结构必须具有物理主键,用来将表进行切分,ChunkSplitter 可以根据主键将表均匀切分为 (max - min) / chunkSize 个 split(min/max 指主键的最小值和最大值)。但是针对超大表(百亿级),同步还是不太稳定,并且同步周期较长,资源消耗也较大,另外有些表存在没有主键的情况。
- 基于外表方式先导出再导入: 针对 range split 方式无法覆盖的场景,我们使用外表的方式先导出再导入,新建一张外表映射到 MaxCompute 的目标数据表,然后将内表的数据导出映射到 MaxCompute 目标表,最后再使用 Flink 批模式将全量数据同步到 Doris。
且物流行业的数据写入呈现出独特的特征:不但并发比较高(单表就可达 5 万行/S),而且字段数较多(通常 300-400 个字段)。这种"宽表"的写入模式对数据库的实时更新能力提出了特殊要求。Doris 凭借其 LSM tree 数据组织方式和 Delete Bitmap 标记删除机制,能够高效处理这类场景:
- 宽表优化:通过主键索引和分区分桶技术,即使面对 300-400 个字段的宽表,也能快速定位和更新目标数据行,避免全表扫描带来的性能开销。
- 批量写入优化:基于 LSM tree 架构,支持高效的批量写入操作,相比传统 Copy-on-Write 机制显著减少 I/O 操作,提升写入效率。
- 实时可见性:通过强一致性语义确保数据写入后立即可见,满足物流场景中订单状态、库存变更等实时更新需求,数据延迟控制在秒级。
- 并发冲突处理:通过 SEQUENCE COLUMN 机制处理高并发写入时的数据冲突,确保在订单状态频繁变更的场景下数据的最终一致性。
三、日常和大促态的稳定性工作
3.1 不同场景下的稳定性问题
如何减少日常和高频大促的繁重运维工作,是我们保障所有集群长期稳定性最重要的职责。物流行业业务复杂并且多元化,背靠前台的电商业务,会有频繁的大促,业务对稳定性的要求很高,已经不是一个纯粹简单的数据分析引擎,在业务全链路各个环节都承担的重要作用。所以可以把整个运维工作分成三大类:
第一类是摆脱人肉运维,比如我们每个月可能都有大促,所以会有频繁的扩缩容需求。
第二类是定规范,收口所有对集群不稳定的高频操作,比如导入和删数据。
第三类是优化集群结构,在集群建立之初,就要从业务视角对集群做好物理资源隔离,将影响范围最小化。
3.2 从人肉运维到自动化运维
我们将 Doris 集群创建、扩容、缩容等流程拆解,主要操作包含两部分,一部分是内部的基础设施平台,一部分是 Doris Manager。整个流程其实是很长的,如果不做运维流程整合,单纯靠人肉做运维,是很难的,并且极容易因为操作失误导致线上故障。我们有着高频的运维操作,各种大促,都需要频繁的操作集群。并且我们还要面对全球多地域集群的运维部署。
为了让整个自动化运维的开发工作降低,通过对整个运维流程的拆解,得益于内部基础设施和 Doris Manager 能力完善,我们将环节中可以复用且独立的节点全部抽成一个个最小的原子能力,将原子能力全部融合到 Doris 运维平台中,然后按照 workflow 的方式去编排所有核心流程,比如:创建集群只需要选择集群地域,规格,即可 10 分钟完成集群初始化交付业务侧使用。而针对大集群的资源组隔离,我们也做了深度融合,支持快速新增资源组,并且 By 资源组做批量扩缩容(针对不同的业务容量需求,扩缩容更加精细化)。在全球化部署上,也不需要多地域系统来回操作,全部收口到一个系统中。
3.3 快速定位 BadSQL
BadSQL 可以说是 OLAP 数据库最痛的点,尤其在报表的取数场景上,很多大查询,BadSQL 较多,极易影响集群稳定性。想事前完全隔绝掉 BadSQL 是几乎不可能的,所以事中事后的工作也很重要。针对大查询的场景,尤其临近大促前新上的一些场景,我们都会做流量打标,打标的目的有助于我们快速定位到 SQL 的来源和使用场景,也防止人员更替,后来者对实际 SQL 的使用场景不熟,出现问题后,无法做出快速决策。而另一方面,Doris 的审计日志是我们的重要分析手段,我们可以基于 cpu 耗时、内存使用量、扫描行数、扫描字节数等不同维度分析大查询,还可以基于用户级别的查询统计分析等,进而快速找到实际的 BadSQL Detail。
3.4 BadSQL 快速止血
除了常态化的 BadSQL 治理,在紧急情况下的 Bad SQL 应急处置措施也很重要(这里主要指的是报表类的分析场景,应用侧的数据产品基本有数据服务收口,管控较严,BadSQL 基本没有),这里我们有两种较为常用的方式:
- SQL Block Rule: 官方提供的 SQL Block 就是一个很好用的能力,搭配了前面提到的打标和慢 SQL 识别,我们可以快速找到 BadSQL,提取打标标记作为 Block Rule 的关键词,实现 Bad SQL 的快速精准阻断,并且将降级范围只控制到某个报表下,既能保障集群稳定性,又能将业务影响最小化。
- Kill All Query: kill All 的能力,是我们在单个 query_id kill 中做的升级,主要应对 BadSQL 无法在短时间内找到,相对更为激进的应急处置手段。这一能力已经融合到我们的运维系统中,只需一键操作即可完成对当前所有正在查询的 SQL Kill 操作。
3.5 配置化的数据清理工具
为什么我们要做配置化的数据清理工具,因为分区表 TTL 无法满足,业务侧的历史数据清理逻辑较为复杂,不是简单的按分区清理就能解决,如果不做这个能力的支撑和收口,业务侧在没有 Doris 知识背景的前提下,会直接执行 delete 大批量删除,我们知道 delete 操作在 Doris 里是一个很重的操作,会影响集群的稳定性。数据清理工具上我们支持了用户自定义删除逻辑、每批删除条数、删除操作可执行时间段、执行间隔等等配置项,而在删除方式上,我们选择了基于导入的标记删除方式,相比 delete 方式,性能会提升很多,并且对线上的查询性能影响可忽略不计。
3.6 用好资源硬隔离
Doris 的资源组隔离是一个很 nice 的能力,所以在大集群物理结构设计时,我们高度结合的 resource tag 能力。比如针对仓储业务的集群,内部的业务会分订单、出入库、补货、库存、发运等等。站在业务的视角将独立的业务拆分,每个对应的一个 DB,都有自己独立的账号(应急处置时,可以 By 账号做限流),并且每个 DB 都是独立的资源组,在 BE 层面是完全物理隔离的。
而针对有跨业务之间的关联查询,我们上层使用逻辑视图来建立关系,针对跨业务的逻辑,比如订单域的表需要被出入库使用,我们没有在做资源组上的拆分,在设计之初我们也想过一个表在多个资源组下,但考虑逻辑结构设计上过于复杂,虽然能够隔离的更彻底,但不利于我们以后的维护,所以选择了现在看到的折中方案,已经能够最大程度的保证业务之间的相对独立,互相影响做到最低。
3.7 更合理的使用规范
合理的使用规范一定是保障集群稳定性的重要手段,有些规范我们可以基于产品能力做收敛,有些规范要靠文档的方式给大家做宣导,这是一个长期逐步培养的过程。尤其是使用 Doris 的第一步就是建表,我们对建表有着严格的要求,表建的好不好直接影响查询和写入的效率,甚至是集群的稳定性。表格中我们列举了一些比较典型代表性的使用规范,这些规范的背后很多都是使用中犯过的错误,导致集群出现抖动的根因。
3.8 促前压测
OLAP 压测可能是一个比较小众的词,很多时候大家是在做选型的时候做个性能测试对比,而实际面向大促的时候,结合整个集群的所有业务场景的促前压测是很复杂的,OLAP 数据库压测的复杂性,它跟传统 TP 数据库压测有着本质的区别,所以每年大促在压测上我们也是花的时间最多的。
整个压测的流程,压测样本准备和压测模型的评估是相对耗时的部分:
第一步:我们会基于历史查询的 SQL Pattern 做聚类,将 SQL Pattern 的数量收敛降低。
第二步:针对聚类后 SQL Pattern 会做特征的提取,这里会结合业务侧明确提供的关键词/打标标记,建立关联关系。
第三步:结合血缘,建立实际压测场景和特征的关系,压测场景实际就代表一个明确的数据查询场景,是一个接口、报表等。
第四步:压测场景明确后,用户要基于压测场景评估 QPS、偏移量等。
第五步:线上 SQL 采集,基于特征随机采样,当然这里也会考虑到一些 SQL Pattern 查询权重。
第六步:采集到 SQL 会做偏移量的 SQL 改写,因为大促单量同比日常会翻几倍,比如这个 SQL 日常扫描 10 天的数据只有 100 万,但实际大促期间 10 天的数据是 1000 万,所以要通过对日期的偏移,模拟大促的数据规模。
第七步:By 压测场景的 QPS 进行全局压测。
第八步:压测结果复盘,目标 QPS 和实际 QPS 是否满足预期,观察集群的各项指标是否满足预期。
注:这里只讲解了查询压测流程,写入压测没有展开讲(写入压测相比查询会容易),实际压测的时候,是查询和写入一起压测。
3.9 一站式监控的核心策略
整个监控的核心策略,全局视角紧盯大盘核心指标(主要聚焦在大促高峰段),日常视角关注集群告警,配合每个集群的不同维度的指标信息,基本能够事半功倍。我们将问题总结为三类,也是日常运维我们使用较为高频的:
- 严控高频小批导入: 我们将 stream Load 的数据准实时的同步落库,关键指标结构化展示出来,有助于我们从集群和表视角观测异常导入情况,高频小批的导入在集群负载较高的时候,极容易导致 Publish 事务堆积,继而造成整个集群的写入雪崩。
- 严禁小 tablet 增长: 表建的不好,很容易导致集群 tablet 数量飙增,这是大家很容易犯的一个错误,tablet 过多或者过少都不行,需要在一个合理的范围内(官方有合理建议),除了事前的长期宣导,事后的监控也很重要,所以要善于使用 rowsets 元数据,要将异常的表全部抓出来,更早的发现更早的治理。
- 狠抓大查询和异常流量: BadSQL 治理是一个长期的过程,要变成一个常态化的事项,而不是大促前临时抓一把,所以审计日志是个很重要的东西,要将审计拆解,形体结构化的监控数据,定义规则,发治理任务。而针对 JDBC 的 Insert 方式是严令杜绝的,要全部收口到 Stream Load 的方式。
四、后续规划
后续规划,我们会聚焦四个点:
- 存算分离: 经历 2 年的发展,Doris 存算一体架构在菜鸟得到了充分的稳定性验证,存算分离架构会作为后续的阶段性发展目标,在未来的大促中,存算分离能够给我们提供更加丝滑的弹性扩缩容,进一步提升扩缩容效率和降低成本。
- 湖仓一体: 数据湖是我们后续坚定投入的一个方向,结合菜鸟的数据现状,会探索落地数据加速、增全量 Merge、流批一体三个大方向,目前 Doris+Paimon 的数据湖方案已经在部分场景落地。
- 同城容灾: 目前 Doris 在菜鸟部署形式还是单可用区的模式,为了应对未来稳定性风险,防止单可用区故障,导致集群不可用,方案上我们已经明确同城三机房的部署架构作为后续容灾的方向,结合的 Doris 资源组隔离的能力,后续也会推进落地。
- 运维底座: 虽然目前运维平台已经初见雏形,但离目标仍有较大的差距,在后续的演进过程中,我们会继续升级打磨整个运维平台,将日常的运维工作逐步变成自动化,或许在不久之后,结合 AI 的能力,会大大提升我们的运维效率。