Skip to content
掌握度

按简历实习三段 bullet 和自我介绍钩子组织的专项题库,每题带完整答案与演练状态。(演练状态见文末总览表)。配套:Binlog2Hive链路设计、可观测性埋点、专项 · CodeAgent CLI 题库。

第一部分 · Bullet 1:同步主链路 ​

1. 「Canal、Spark 是专门团队做的,你说的『编排与落地』具体做了什么,边界在哪?」 演练 9 分 ​

完整答案:

在这条 MySQL 到 Hive 的链路中,我主要负责把 Canal 采集、MQ 中转、HDFS 存储、Spark 计算这四段既有能力编排成一条可配置的同步链路。具体三块:一是任务创建与发布流程——12 个阶段的发布流程从 RDS 读权限、Canal 到 MQ 的路由选择、HDFS 落地配置、自动建表,到全量导入和 Spark 增量任务的注册,都是我打通的;二是全量和增量的衔接机制——以 wholeLoadBatchTime 作批次锚点,配合 Snap2Binlog 三种迁移模式;三是联调与故障排查——延迟按四段链路逐段定位,数据不一致做比对重导。Canal 内部的采集实现和 Spark 的计算逻辑分别是采集团队和引擎团队的,但选型、配置、上下游契约都是我的工作范围,原理我也掌握到设计级。

要点:三档动词守边界——「都是我打通的」(自有)/「选型、配置、契约是我的工作范围」(参与对接)/「采集实现和计算逻辑是团队的」(原理我掌握)。

2. 「为什么采集和计算中间必须隔一层 Kafka/DDMQ?Canal 直接写 HDFS 不行吗?」 演练 9 分 ​

完整答案:

五个理由。一是削峰:源库一次大事务或批量 update 会瞬间产生海量 Binlog,直接落 HDFS 既写不进去也会把 Canal 压垮,MQ 是缓冲池。二是解耦:Canal 的生命周期和数据消费完全分离,下游处理出错不影响采集。三是多订阅:同一份 Binlog 可以同时给实时数仓、索引构建、缓存失效等多个消费方。四是回放:下游处理逻辑有 bug 时,重置消费位点就能重放数据——这也是死信修复和历史批次补跑的地基。五是这条链路特有的:Dsink 是按 micro-batch 落 HDFS 的,天然需要 MQ 做聚合缓冲——直连的话小文件问题和可用性问题会一起爆炸。

3. 「全量和增量的重叠窗口怎么产生的?批次锚点怎么解决?」 两场复检通过 ​

完整答案:

重叠是结构性的:Canal 采集链路在发布流程第 2 步就建好,从那一刻起 Binlog 持续进 MQ;而全量是第 8 步才执行的快照导入,DataX 写完 Hive 首个分区才记录批次时间。所以从采集开始到全量完成之间,同一行数据既在快照里、又在 MQ 的 Binlog 流里——这个窗口消灭不了,只能收敛。

锚点的角色是把「不丢」钉死:全量完成时记录 wholeLoadBatchTime 作锚点,增量任务从锚点对应的位点之后开始消费,位点之后的变更一条不少。位点之前、已经进 MQ 的部分和快照天然重复,靠下游 Spark 合并按主键保留位点最大的末态收敛——重复写一百遍结果一样,这就是幂等。所以整条链路的口径是 at-least-once 投递加主键幂等,业务上不丢不重。

全量跑的几个小时里,增量一直在 MQ 里按「表加主键」分区有序积压;全量完成、锚点记录后,增量调度任务从锚点位点开始消费,这些变更按调度周期逐步写成 Hive 分区——同一主键全量里已有的,位点大的覆盖位点小的。

4. 「锚点为什么用 Binlog 位点,不用业务时间戳?」 快问过线 ​

完整答案:

核心一句:位点在单库内是全序单调的,业务时间戳不是。

位点由「Binlog 文件名 + 偏移量」组成,每一条事件唯一且严格递增——所以既能当切分锚点(wholeLoadBatchTime 精确落在某条事件上,增量从位点之后消费,一条不丢),又能当排序依据(同一主键多次变更,按位点保留最大的那条,末态一定正确)。

业务时间戳撑不起这两个职责:一是碰撞——同一秒多条变更,排序分不出先后;二是回拨——NTP 校时、主从切换都会让时间戳倒退,按时间戳切分会出现「后发生的变更被当成旧的」;三是它不是 Binlog 原生属性,应用层填的时间戳不可控。所以位点管顺序和切分,时间戳只做展示。

第二部分 · Bullet 1 延伸:数据语义 ​

5. 「整条链路是 exactly-once 吗?重复怎么产生、在哪收敛?」 演练 9.5 分 ​

完整答案:

诚实定性:链路整体是 at-least-once,靠下游主键幂等去重把效果做到 exactly-once。重复来源有三类:MQ 在消费者失败时重放、Spark 任务失败重跑会重读分区、全量快照与增量的重叠窗口——追求端到端 exactly-once 需要事务性 sink(比如 Flink 两阶段提交加事务表),复杂度和运维成本都高。

我们的做法是把「去重」放在最确定的位置:Spark 合并按主键保留末态,天然幂等——同一批数据重跑一百遍,结果都一样。再配合全量批次时间锚点和分区覆盖写,「可重跑、可重放、结果收敛」。对数仓场景来说,这套「at-least-once + 幂等」的性价比远高于强 exactly-once。

三类重复来源正好对应简历上的三种修复能力:MQ 重放 → 位点重置回放,任务失败 → 历史批次补跑,大面积不一致 → 快照重导。

6. 「Hive 不支持行级更新,变更怎么落地?同一主键一分钟改五次怎么保证留最新?」 演练后补强(本题曾丢末态半问) ​

完整答案:

落地上 Hive 是只追加的分区表,行级变更两种模式。合并模式(latest):每次调度时 Spark 读本周期分区的 Binlog 增量,和历史最新状态按主键合并,保留每条主键的最新版本写出新分区——下游看到的是「当前状态」表。保留变更模式:事件原样落表、带 INSERT/UPDATE/DELETE 标记,下游自己聚合,适合审计场景。DELETE 的处理由 saveDelete 配置控制:开是保留删除标记、可审计可回溯,关是物理剔除、不进结果集。

同一主键一分钟改五次,末态靠两层顺序保证。传输层:Canal 投 MQ 的分区键是「表 + 主键」,同一主键的所有事件永远落同一个分区、分区内严格有序。计算层:Spark 合并时按 Binlog 位点排序取末态——位点在单库内是全序单调的,比业务时间戳可靠(时间戳可能重复、可能回拨)。所以五次变更不管落在几个调度批次里,最终收敛的都是位点最大的那条。再引申一句:这个方案最隐蔽的关键设计就是分区键——如果用随机 key,同一主键的事件散到不同分区,跨分区无序,末态就可能是错的。

边界也主动交代:如果业务要求真正的行级更新和事务,业界现在的答案是 Hudi/Iceberg 这类 ACID 湖表格式;我们用「分区 + 合并」是平台既有体系的取舍——知道替代方案,才说明这个模式是选出来的,不是只会这一个。

第三部分 · Bullet 2:一致性保障与修复 ​

7. 「单事件 / 批次 / 全量三级修复粒度,分别指什么?各靠什么机制?」 未答过(完整答案已备) ​

完整答案:

三级粒度对应三种「坏的范围」——单条坏、一个周期坏、整表坏,各有一条修复链路。

**单事件级:死信队列修复。**场景是某个事件解析失败——这种「毒丸」不能留在主流程重试(反复失败会把整个分区的消费堵死),也不能直接丢(丢数据)。做法是原样转存死信、记录源端偏移量,主链路跳过继续消费;事后修好解析逻辑,按偏移量从源端重放,重放走正常入湖链路。成功率 98% = 修复成功重新入湖的条数 ÷ 进入死信的总条数;修不了的 2% 是源头数据本身损坏、怎么解析都不合法的,标记跳过加告警,不阻塞主链路。

**批次级:历史批次补跑(Gap Batch)。**场景是某个调度周期(小时 / 天)的任务失败或漏跑,导致那段时间的数据没进 Hive。做法是按指定时间范围把漏掉的批次调度任务补上,而且依赖关系自动处理——补 T-2 之前先确认 T-3 已经完成。因为 Spark 合并按主键保留末态,重跑是无害的,补跑就是「重新执行一遍调度」这么简单。

全量级:全量回溯(快照重导)。场景是 Binlog 丢失损坏、或大面积数据不一致,增量修复救不回来。做法是 DataX 低峰限流重新导一次快照、覆盖历史分区,完成后记录新的批次时间锚点,增量从新锚点重新衔接。

三级能做成「无害重放」,靠的是同一个底座:下游主键幂等 + 批次锚点。这也是为什么敢把修复拆成三个粒度——每一级的重放、补跑、重导都是幂等收敛的,不会越修越乱。

8. 「死信修复成功率 98% 的口径?」 毕业题(复检) ​

完整答案:

口径:死信修复成功率 = 修复成功重新入湖的条数 ÷ 进入死信的总条数。

为什么要有死信:解析失败的异常事件是「毒丸」——留在主流程重试会把整个分区的消费堵死,直接丢弃就是丢数据,所以折中:原样转存死信、记录源端偏移量,主链路跳过继续消费,事后集中修复。

修复的闭环:死信里带着原始事件和偏移量,大多数修复是「修好解析逻辑后按偏移量从源端重放」,重放走正常入湖链路,可观测、可审计。修不了的 2% 是源头数据本身损坏、怎么解析都不合法的——标记跳过加告警,人工确认后归档,不阻塞主链路。

9. 「位点重置回放是什么场景用的?回放会不会写重?」 题库内自答 ​

完整答案:

场景是下游消费逻辑有 bug:比如 Spark 的解析逻辑有问题,某个时间段的数据处理错了,这时不需要动源库,把消费位点重置到那个时间段之前,MQ 里的数据就能重放一遍——这是 MQ 解耦给数据修复留的地基。

会不会写重:会重复写入,但无害——Spark 合并按主键保留位点最大的末态,重放的数据重跑一百遍结果都一样,靠下游幂等收敛。这就是「at-least-once 投递 + 幂等」口径在修复场景的应用:回放只管把数据再给一次,正确性由幂等兜底。

第四部分 · Bullet 1 延伸:延迟与可观测 ​

10. 「入湖延迟从 3 分钟涨到两小时,怎么排查?」 演练 9 分 ​

完整答案:

分诊先说:告警三阈值(延迟超基线、错误记录数、数据量低于下限)先于人工发现问题,超基线先通知任务负责人自判——是不是自己源库在批量写;处理不了升级到平台侧,按链路四段从上游往下逐段看水位。

第一段看源:MySQL 写入 QPS 是否突增——大事务、定时批量 job,源头洪峰属正常延迟,等峰值过去或临时扩下游消费。第二段看 Canal:采集位点滞后多少,实例有没有重启或报错。第三段看 MQ:消费堆积是否持续增长——增长说明消费能力不足或消费者挂了;再看分区倾斜,热点表打满单个分区。处置优先加消费者实例,不随便扩分区——扩分区改变 key 到分区的映射,同一主键的新旧消息可能跨分区,分区内有序被打破,而末态正确性依赖这个有序性。第四段看 HDFS 和 Spark:Dsink 落地文件是否按时生成,Spark 调度有没有失败、资源是否排队——资源不足就提并行度。

实践中大多数就两类:源头批量写入的洪峰,等或扩消费;Spark 资源不足排队,提并行度。

11. 「可观测性那条:AOP 埋点切面怎么实现?埋了哪些字段?40+ 怎么统计?」 演练 5 分(口径已立) ​

完整答案:见 可观测性埋点 的「60 秒标准答案」——@Aspect + 自定义注解 @InterfaceLog 显式标注、@Around 环绕通知 proceed() 前后计时、统一执行记录(接口标识 / 入参摘要 / 出参摘要 / 耗时 / 状态 / 异常摘要 / traceId)、敏感字段脱敏、日志异步写;40+ 是显式标注注解的核心接口数(12 阶段发布流程各阶段接口 + 任务管理接口 + 数据源注册)。

要点:标准化是题眼;异常路径全覆盖;脱敏与发布流程加密步骤呼应;40+ 报构成不报约数。

演练状态总览 ​

题号主题状态
1职责边界与三档动词9 分
2MQ 五个理由9 分
3重叠窗口 + 锚点两场复检
4位点 vs 时间戳快问过线
5exactly-once 口径9.5 分
6Hive 落地 + 末态丢过末态半问,已补
7三级修复粒度未答(答案已备)
8死信 98% 口径毕业题
9位点重置回放题库内自答
10延迟排查9 分
11AOP 埋点5 分,口径已立

本站仅供个人学习使用,请勿外传