项目md
面试官您好,我叫李飞翔,本科毕业于金陵科技学院,研究生就读于太原理工大学。毕业后通过校招加入瑞幸咖啡财务组,担任后端 Java 开发,至今已有一年多的工作经验。我主要的技术栈包括 Spring Boot、Redis、 MySQL、Kafka、Zookeeper 和 Dubbo。接下来我简单介绍两个参与度比较高的系统。
第一个是财务数字化平台,在订单模块中,我主要负责支付渠道的扩展接入,并设计实现了新的优惠券计算方案。此外,我还承接了费控模块的保障单管理,并完成了税务模块与百望、航信等第三方开票系统的对接。此外,还涉及多个第三方账单的拉取与处理,例如通过 API 拉取旺店通的账单,以及处理抖音、微信等渠道的 Excel 账单,将其入库并进行对账。对账过程中产生的差异数据会写入差异表,系统每天自动扫描,并根据错误类型进行告警或自动补偿。
后续我还参与了 AI 报账单功能的开发,结合 RAG、LangChain4J 和 Dify 技术,实现了报账单审核的预警提示。在用户提交前,系统会提示报账单可能存在的拒收风险,帮助用户减少反复提交的次数;在审核过程中,系统也会提示审核人员注意用户填写错误的地方,从而避免因审核问题导致流程被打回。
构建了一个基于维度分片的轻量级抢占式任务调度框架。
调度器将大数据量报表任务(如2亿底表数据)按业务维度(如门店、时间)进行智能拆分,生成大量细粒度互不干扰的子任务,比如订单汇总表,假设有1千万的数据,1w家门店,原先一个线程跑需要很久,拆分完每个线程负责10家门店,每个线程之间负责的数据,将任务拆分为1000个子任务,每个子任务负责10000条数据。执行器通过ZK分布式锁抢占这些任务,实现了任务的水平扩展与无冲突并行执行,充分利用了公司50多台机器资源,将报表生成时间从7个多小时大幅降低。
后续报表如果用到相同的维度配置,则无需重复开发,实现通用化设计高可靠性,内置故障转移与补偿机制,能自动处理机器宕机、重启等异常;3) 完善的运维体系,提供可视化界面与实时告警,保障了任务的稳定性和可观测性。
1. 项目背景与核心问题
- 业务需求:承接来自数仓的财务审计报表需求,数据量大(底表2亿+,汇总后20万+)。
- 初期痛点:
- 性能瓶颈:第一版采用简单分片+多线程,耗时长达7小时,主要瓶颈在于RPC调用填充财务字段。
- 架构僵化:代码与业务强耦合,每接入一种新报表(如咖啡券、调拨单)都需重写一套逻辑,开发效率低。
- 资源利用不均:无法有效利用线上50多台机器,且手动分片繁琐,易导致数据冲突或负载不均。
- 容错性差:线程级重试代价高,系统脆弱。
2. 系统设计与核心流程:基于维度分片的抢占式调度
核心思想:将一个大任务拆解为无数个互不干扰的小任务,让执行器动态抢占,实现真正的水平扩展。
数据流向与执行流程:
数据库里面会有一张任务表,字段有,任务名称,任务id,任务流编号,状态,分片参数,分片键,重试次数,任务最高并发数,告警地址,超时时间
一条任务对应一行任务表信息。
- 任务定义与分片(调度器):
- 维度分片:在任务创建时,根据其业务特性选择一个预设的分片维度(如
门店ID、时间范围、仓库ID)。 - 生成子任务:根据选定维度,预先计算出所有待处理的数据范围。例如,按
门店ID分片,将全国1万家门店分成1000个任务片,每个任务片处理10家门店的数据。这些任务片的状态被初始化为“待执行”。
- 维度分片:在任务创建时,根据其业务特性选择一个预设的分片维度(如
- 任务抢占与执行(执行器):
- 抢占触发:调度器发布任务后,通知所有执行器集群开始抢占。
- 分布式协调:执行器中的工作线程通过ZooKeeper分布式锁来抢占“待执行”状态的任务片。抢到锁的线程,即获得了处理该任务片对应数据(如那10家门店)的独占权。
- 数据流处理:
- 查询:线程根据任务表里面的分片参数(如
门店ID列表)从TiDB底库查询数据。 - 填充:调用各类RPC服务,并利用Redis缓存,对查询出的批次数据进行字段填充(这是最耗时的环节)。
- 入库:将处理完成的最终报表数据写入目标数据库。
- 查询:线程根据任务表里面的分片参数(如
- 状态更新:任务执行成功或失败后,更新任务片状态。
- 完成与清理:一个后台扫表任务每分钟检查,当某个报表的所有子任务片均完成时,通知所有执行器停止抢占,流程结束。
3. 系统特色与技术创新
- 通用化与水平扩展:
- 通过抽象“分片维度”,将任务拆分逻辑通用化。新报表只需配置维度即可接入,无需编码,极大提升了开发效率。
- 抢占机制使得任何一台空闲机器都能参与计算,真正实现了资源的水平扩展,将50多台机器的算力发挥到极致。
- 高可靠性与故障恢复:
- 故障转移:执行器与ZK心跳断开超过10秒,自毁并将任务置为失败;调度器监听执行器存活,15秒无心跳则主动将其上任务失败。
- 补偿机制:兜底扫表任务 + 机器重启时自检,能发现并清理“僵尸任务”(状态为执行中但无人处理的任务),确保任务不会无限期卡住。
- 精准重试:由于每个任务片的数据量很小,失败后重试的代价极低,系统容错能力显著增强。
- 可观测与可运维:
- 可视化界面:用于监控所有任务的状态、并发度、耗时等信息。
- 实时告警:对企业微信发送执行超时、失败等告警,让研发能快速响应。
- 数据库优化:对任务表的关键字段建立联合索引,保障高并发抢占场景下的扫表性能。
总结:该系统通过“维度分片”和“抢占调度”的核心设计,成功将一个笨重的ETL流程改造为一个高效、通用、稳定且易于运维的数据平台,完美解决了大数据量财务报表的生成难题。
几个不同的购买次数档次,以及每个档次对应的总价和计算出的“次均价”
执行器什么时候初始化出来,开始抢占任务?
执行器是一个常驻的后台线程,会一直处于阻塞状态,,其内部的工作线程并不会立刻去数据库里扫描任务。它们会处于一种“待机状态”,等待一个开始的信号。有触发的时机的。
调度器通知任务发布了。开始执行代码。然后发现当前机器可执行任务数量大于0时。开始执行抢占任务,抢到任务,把任务丢掉线程池里面执行,抢到任务数量后,会减少当前机器可执行任务数量。如果小于等于0。会直接休眠,当有任务执行完成后,会唤醒该线程。
报表系统需求,主要来源于,我接受到从数仓转过来的报表需求,因为数仓出报表不符合审计要求,只能财务中台做,数据量都很大,底表数据差不多在2亿多左右,汇总完大概在20w。一开始做这块需求,第一版本就是结合公司内部的定时任务+多线程完成。发现要7个多小时。我当时开始了10个分片机器,每个分片新开16个线程,一起查询。报表的生成,一般就是几个流程。查询数据,填充字段。数据入库,因为我们的底表是tidb,qps没有其他大业务情况下,在1000-2000左右,压力不大,每次查询基本在50-100毫秒左右,数据的查询和插入基本上压力不大,主要在填充字段,因为需要调用各个rpc进行填充,每个批次500条数,即使使用了redis缓存热点数据,想要填充几十个财务字段,比如财务分类,货物规格编码/核算主体,一个流程也需要2-3秒左右。
第一点:重试的代价比较大,因为线程数比较少,一共也就100多个线程,每个线程负责100多家门店数据。其中任何一个线程失败,就得重试。而且每个线程负责的数据就多,重试的代价很大。代码逻辑写的就非常复杂,不通用,后面又接入比如咖啡库劵的报表-基于咖啡优惠卷模版多/库存调拨单,基于仓库id。又得写一套。第二点我们线上有50多台机器,所有机器都想用上,你得点50个分片,压力又不在db上,所以就想着水平扩展,把这些机器都用上,问题就是怎么让多个机器之间执行的数据不冲突,不会说1个线程处理门店编号1-10的数据,另一线程又在执行门店编号1-10的数据,第三点,如果能够控制并发,做成通用模块,即使db压力大的报表我们也能够支持。
所以设计了一个抢占式任务。由调度器基于维度,比如门店/咖啡模版id/仓库中心/时间(内置好的)其他报表无需开发即可使用,如果有特殊需求支持自定义,举个例子,按照门店的话,一个线程负责10家门店,对于数据比较均匀的报表,可以使用时间维度,一个线程负责20分钟的数据。提前生成好任务分片参数,将每个线程涉及到的数据限制死。同时为了避免一直在抢占,调度发布完成任务后,通知执行器开始抢占,然后每分钟会有个扫表任务,发现全部任务都已完成,通知执行器不用抢占任务。每个任务有自己的前置任务名称,并发数,重试次数。
使用zk加锁抢占任务,每个线程由于已经提前规定好的数据范围,这样子就不会冲突,而且,基于门店或者时间。不会说,发生突变,比如执行一个小时,发现新增一家门店,即使新增了,也不会对报表数据有影响,财务端的性质,使用的入账数据,基本上都是终态。除非刷数,不会发生变更。实在不行,也可以选择时间维度,因为时间不会突然新增一秒。相同类型的子任务设置统一的任务编号。方便
后面因为报表越来越多,又做了可视化页面/告警模块,对于长时间处于执行中任务,超时任务进行告警,对于失败任务,进行企业微信告警。让我们研发手工接入自行。随着任务表越来越多,又对任务表做了索引优化。一个表里面的索引,对状态和id增加联合,方便我们扫表抢占。
故障转移-针对机器重启/宕机等特殊情况,做了补偿。当发现有机器宕机。我们约定,执行器呢,发现自己连接不上zk,超过10秒,则将任务设置成失败,并生成新的任务。超过15秒钟,调度器监听执行器的ip。没有重连回来的,还存在正在执行的,将任务设置成失败。还有兜底逻辑,扫表+机器重启时,根据ip+状态。检查是否有属于自己正在执行的机器。并且没有新的任务生成。则自动关闭掉任务。这样子保证任务不会一直处于执行中。
财务数智化平台,在我来的时候已经很成熟了,做的最多的就是月结1-3号,出现问题支持,新增的业务场景接入,比如我们在香港开店,做了很多关于币种,跟税率的新需求。后面为了审计要求,做了很多对账的需求,我们还有电商等订单,都是在其他平台销售的,所以需要从各个地方进行取数,进行对账,对于差异数据进行告警。三方的数据一般做不到自动补偿,只能通过告警提前预测。后面ai相关的。使用华为云的CSS向量数据库+dify。做报账单相关的需求。