1. 为什么工程数据岗的笔试要先搞明白"考什么、怎么考"
2024年秋招,我投了蚂蚁集团的工程数据岗,从简历筛选到笔试通知,中间隔了大概一周。说实话,收到笔试链接那一刻我是有点懵的——因为"工程数据岗"这个名字本身就有点模糊,它既不像纯后端开发岗那样大家都能说出个大概,也不像算法岗那样有明确的刷题路径。我身边很多同学都以为这岗考的是数据分析,结果进去一看,Java并发、SQL窗口函数、Flink原理、算法题全都有。所以这篇复盘我想先聊一个很多人忽略的问题:你到底在考什么。
工程数据岗在蚂蚁内部主要做的是数据管道、实时计算、数据治理、离线数仓这一类方向,也就是把业务数据从源头采集、清洗、加工成可用的数据资产。这个岗位的笔试不会只考数据分析,它更看重的是你能不能写对代码、能不能处理大数据场景下的计算问题。换句话说,它考察的是一个"会写代码的数据工程师",而不是"会看报表的数据分析师"。这一点直接从笔试题目分布就能感受到——算法题占了很大比重,SQL也是偏复杂查询而不是简单select。
还有一个很重要的点,工程数据岗的笔试和纯后端岗笔试的重合度大概有六成,剩下四成是大数据组件和SQL的专属考点。所以我后来复盘时有个很深的感受:如果你只刷LeetCode不练SQL,想靠运气过笔试是很悬的;反过来,如果你只背组件原理不刷算法,编程题大概率做不完。笔试的设计逻辑其实非常清晰,就是筛掉那些"偏科"的人。
另外要提醒一句,蚂蚁的笔试是在牛客网这种在线OJ系统上进行的,全程摄像头监控,不能切屏,切屏超过一定次数会被判作弊。所以考前一定要把网络、浏览器、摄像头都调试好。我笔试那场就有个同学因为电脑弹窗导致切屏被警告,后面整个人心态都受影响了。这些细节看起来很碎,但实际影响非常大。
2. 考前信息战:从岗位JD倒推考点,再定刷题计划
很多人准备笔试是看到什么刷什么,今天做几道数组题,明天看两个Flink原理视频,结果到考场上发现会的没考、考的不会。我的经验是,准备笔试一定要从岗位JD倒推考点,再倒推刷题计划,这样才能把有限的时间用在刀刃上。
2.1 JD里藏着笔试考察方向
我当时把蚂蚁工程数据岗的岗位描述翻来覆去看了好几遍,摘出几个关键信息:一是要求扎实的Java或C++编程基础,二是熟悉Hadoop、Spark、Flink等大数据框架,三是熟悉SQL和数据仓库建模,四是具备一定的算法与数据结构能力。如果你看到这样的JD,基本可以推断出笔试的两个大头:第一是编程题,考察数据结构和算法;第二是专业题,考察Java基础、SQL和大数据组件原理。
我把JD里的每个要求对应到了具体的笔试考点,列了一个表格,照着这个表格复习,效率和漫无目的刷题完全不一样:
| JD要求 | 对应笔试考点 | 复习优先级 |
|---|---|---|
| 扎实的编程基础 | 数组、链表、栈、队列、哈希、二叉树、动态规划 | 高 |
| 熟悉大数据框架 | Hadoop HDFS读写流程、Spark RDD/Shuffle、Flink 状态与Checkpoint | 高 |
| 熟悉SQL | 窗口函数、多表关联、去重排序、行列转换、连续问题 | 高 |
| 具备算法能力 | TopK、LRU、字符串处理、前缀和、双指针 | 高 |
| Java基础 | 集合源码、并发编程、JVM内存模型、类加载 | 中高 |
| 数据仓库建模 | 星型模型、拉链表、缓慢变化维、事实表与维度表 | 中 |
2.2 题库选择和刷题优先级
确定考点之后,接下来就是选题库。我个人的组合方案是:LeetCode Hot 100 + 牛客SQL大厂真题 + 大数据组件原理题。LeetCode不用多说了,Hot 100过两遍基本能覆盖笔试里大部分常规算法题。SQL部分牛客上有专门的大厂真题练习区,里面有大量真实面试难度的SQL题,比LeetCode的数据库题更贴近国内大厂风格。大数据组件题在牛客、LeetCode上都没有特别系统的题库,只能靠看面经总结,我当时是把牛客上关于蚂蚁工程数据岗的笔试面经翻了个遍,把能收集到的题目类型都整理出来了。
刷题优先级上,我建议是:SQL和算法题最优先,JVM和并发其次,大数据组件原理可以放在选择题冲刺阶段集中背。因为SQL和算法是编程题的主战场,占比高、拉分大,值得投入最多时间。组件原理虽然也重要,但更多出现在选择题里,背诵性价比高但理解深度要求没那么高。
2.3 四周复习计划参考
我实际用的复习计划是四周周期,比较适合时间适中、目标明确的情况,分享出来给大家做参考:
- 第一周:数据结构与算法强化。每天3道LeetCode中等题,优先数组、链表、二叉树、动态规划,周末做一次限时模拟。
- 第二周:SQL专项。每天5道SQL题,覆盖窗口函数、连续登录、行列转换、留存计算等高频题型,配合牛客SQL题库。
- 第三周:Java基础+大数据组件。白天看Java并发和JVM,晚上看Spark、Flink核心原理,周末刷选择题。
- 第四周:真题模拟与查漏补缺。每天按正式笔试的时间节点做一套模拟题,重点训练时间分配和抗压能力。
这个计划最大的好处是把"输入"和"输出"结合起来了,不是只看不练。第四周的限时模拟非常关键,我第一次模拟的时候编程题根本做不完,后来调整了答题顺序才改善,这个后面专门说。
3. 笔试题型拆解:从选择题到编程题的实战复盘
蚂蚁工程数据岗的笔试,我当时参加的是约120分钟的线上笔试,题量不算小。整体风格偏向"基础扎实+思维灵活",没有特别偏门的知识点,但想拿高分也不容易。下面按题型来复盘。
3.1 选择题:覆盖Java、SQL、大数据组件
选择题大概有20到25道左右,涵盖的范围很广。Java基础方面,考了ArrayList和LinkedList的区别、HashMap的底层实现、并发编程里synchronized和ReentrantLock的区别、线程池的核心参数等。这些都是很典型的八股题,难度不高,但知识点细,需要平时积累。
SQL选择题基本就是给一段SQL问你执行结果,或者给个业务场景让你选正确的SQL写法。这类题目考察的是对SQL语义的理解是否准确,尤其是JOIN的细节、GROUP BY与HAVING的关系、窗口函数的执行顺序等。我有一个比较深刻的教训是,ON和WHERE的执行顺序在LEFT JOIN里真的很容易混淆,笔试就考了类似的题,我当时差点选错。
大数据组件选择题是大头,也是工程数据岗和其他技术岗最大的区别。Hadoop、Spark、Flink都有涉及,比如HDFS的副本放置策略、Spark RDD的依赖关系、Flink的Checkpoint机制、Watermark和窗口的关系、数据倾斜的处理方式等。这些题目不会特别深,只要你系统看过组件核心原理,基本能答对。但如果只是知道组件名字、没深入了解原理,很容易被选项干扰。我复习时用了对比法,把Hadoop、Spark、Flink的架构、计算模型、存储方式、容错机制放在一个表格里对比记忆,效果很好。
3.2 编程题:常规算法题加一道SQL题
编程题一般有两到三道,通常包括一道算法题和一道SQL题,有时候会加一道大数据场景题。我这场的结构是两道算法题加一道SQL题。
算法题的方向比较常规,一道是TopK问题的变体,一道是字符串处理。难度大概在LeetCode中等,不会到Hard,但比Hot 100里的简单题要难一些。这类题目刷过LeetCode的人应该都能下手,关键是在有限时间内写出正确且够高效的解法。
SQL题是典型的"大厂业务题",给了一张用户登录表,要求计算每个用户连续登录的最大天数。这类问题在SQL面试里太常见了,核心是用窗口函数row_number + date_sub来做分组,之前如果专门练过连续登录类题目,基本属于送分题。但如果没准备过,考场上现场想可能要花不少时间。
3.3 说说我印象最深的一道编程题
有一道编程题我印象特别深,题目大意是给一个很大的日志文件,每行是一条用户访问记录,包含用户ID、访问时间和访问页面,要求统计每个页面独立访客数排名前10的页面。这道题本质上就是经典TopK问题,但是套了一个大数据场景的外壳。如果用Java写,核心思路就是先用HashMap统计每个页面的独立访客数,然后用小顶堆维护TopK。我在考场上很快想到了思路,但因为要处理"独立访客"这个条件,哈希表存的时候需要先去重,当时在去重的实现上稍微犹豫了一下,最后用了HashSet套在HashMap的value里。这道题给我的启发是,工程数据岗的算法题不会单纯考数据结构和算法,它会把实际业务场景包装进去,你需要快速把业务问题转化成算法问题。
4. 最拉开差距的算法题:思路、代码与现场心态
算法题在整场笔试里的重要性不用多说,它通常分值最高,而且直接决定你能不能进下一轮。这里我想用一道TopK变体题作为例子,完整还原我在考场的思考过程和解法。
4.1 一道TopK变体的完整解题过程
题目大致是:给定一个字符串数组,统计每个字符串出现的次数,返回出现次数最多的前K个字符串,如果出现次数相同则按字典序排序。这题在LeetCode上有原型,就是"前K个高频单词"。我看到这题的时候心情是比较平静的,因为它属于典型的"哈希统计+排序/TopK"套路。
我的解法思路分两步:第一步用HashMap统计每个单词的出现次数,key是单词,value是次数;第二步构建一个小顶堆,堆内按出现次数从小到大排序,如果次数相同则按字典序从大到小排序,这样堆顶是"最小"的元素,遍历完所有单词后,堆里留下的就是出现次数最大且字典序靠前的K个。
关键点在于,利用优先队列时要注意Comparator的写法。我现场Java实现的核心代码如下:
public List<String> topKFrequent(String[] words, int k) { Map<String, Integer> countMap = new HashMap<>(); for (String word : words) { countMap.put(word, countMap.getOrDefault(word, 0) + 1); } PriorityQueue<String> minHeap = new PriorityQueue<>((a, b) -> { if (!countMap.get(a).equals(countMap.get(b))) { return countMap.get(a) - countMap.get(b); } else { return b.compareTo(a); } }); for (String word : countMap.keySet()) { minHeap.offer(word); if (minHeap.size() > k) { minHeap.poll(); } } List<String> res = new ArrayList<>(minHeap); Collections.sort(res, (a, b) -> { if (!countMap.get(a).equals(countMap.get(b))) { return countMap.get(b) - countMap.get(a); } else { return a.compareTo(b); } }); return res; }这里要注意几个细节:一是小顶堆的堆顶是"当前最小的",当堆大小超过K时弹出堆顶,最终留下的就是最大的K个;二是当次数相同时,字典序小的应该留在堆里,所以比较器里字典序那一项要反过来写,也就是b.compareTo(a);三是最后从堆里取出结果时顺序是乱的,需要二次排序恢复成题目要求的顺序。这些细节如果平时没写熟,考场上很容易翻车。
4.2 遇到没思路的题怎么办:先暴力,再优化
笔试现场最怕的就是碰到一道题完全没有思路。我的策略是:首先快速判断这题的考点是什么,如果5分钟内想不出最优解,就直接写暴力解法。理由很简单,笔试OJ判题是按用例给分的,暴力解至少能过一部分用例,比交白卷强得多。写暴力解的时候,一定要把想法和注释写清楚,方便后面有时间回来优化。很多牛客的笔试是支持多次提交的,你可以先提交一个暴力解拿保底分,然后继续想优化方案。
我笔试时第二道算法题就差点卡住,题目涉及字符串的某种变换,我当时第一反应是这题跟滑动窗口有关系,但是窗口的左右边界怎么移动想了好几分钟没想清楚。后来我果断放弃了"一次写对最优解"的想法,先写了一个双层循环的暴力解,提交后大概过了30%的用例。拿到保底分之后,我重新理了一下思路,发现可以用前缀和数组把部分重复计算优化掉,最后优化完过了全部用例。这段经历让我彻底明白:笔试不是竞赛,得分才是硬道理,不要在纠结中浪费时间。
4.3 现场心态管理的几个细节
心态这件事听起来虚,但真的很影响发挥。我有几点实际感受:
- 拿到题先深呼吸,快速归类考点,不要一上来就写代码。脑子里的"锚点"很重要,看到求前K个就想到堆,看到连续子数组就想到前缀和,看到字符串匹配就想到双指针或KMP,这些套路能帮你快速进入状态。
- 遇到卡壳超过10分钟就跳过,做后面会做的题,回过头来再看卡壳的题往往会有新的思路。我笔试时有一道算法题就是这样,先跳过做SQL题,回头再来看,反而一下子想到了解法。
- 不要盯着计时器看,尤其是时间剩得不多的时候,越看越焦虑。可以把剩余时间拆成几个阶段,比如还剩60分钟时应该做到哪,还剩30分钟时应该做到哪,心里有个底就行。
5. SQL与大数据组件题:工程数据岗的"专业分水岭"
算法题大家都会刷,真正把工程数据岗和其他岗位拉开差距的,其实是SQL题和大数据组件题。这一块如果你有数仓实习或者平时工作接触过大数据组件,会觉得很简单;但如果没有实际经验,只能靠临时背,考场上很容易露馅。
5.1 SQL高频题型:连续登录、行列转换、留存率
SQL题在笔试里的地位非常高,因为它直接对应了未来工作里最核心的能力——写数据查询和分析SQL。我整理过工程数据岗笔试SQL题的高频题型,主要集中在三类:
一是连续登录问题。经典场景是给一张用户登录表,计算每个用户连续登录的最大天数。核心解法是用窗口函数给每个用户的登录日期编号,然后用登录日期减去编号得到一个新的日期字段,如果日期相同说明是同一段连续登录。具体SQL如下:
select user_id, max(continue_days) as max_continue_days from ( select user_id, date_sub(login_date, row_number() over (partition by user_id order by login_date)) as grp_date, count(*) over (partition by user_id, date_sub(login_date, row_number() over (partition by user_id order by login_date))) as continue_days from user_login group by user_id, login_date ) t group by user_id;这个解法是"分组求最大值"的思路,先给连续日期打上同一个标记,再按标记分组统计数量,最后取最大值。这种题没有任何捷径,就是多练几遍把套路刻在脑子里。
二是行列转换。比如把一张长表转成宽表,或者把宽表转成长表。这类题考察的是CASE WHEN和UNION的灵活运用,平时多写几遍就熟练了。
三是留存率计算。给用户活跃表,计算某天新增用户在之后第N天的留存率。核心是理解"新增用户"和"活跃用户"的区别,然后通过JOIN和日期差计算。
5.2 大数据组件题:掌握原理比背八股重要
大数据组件相关的选择题和简答题,是工程数据岗笔试的"个性题"。我当时复习时重点看了几个方向:Hadoop的HDFS读写流程、MapReduce的Shuffle过程、Spark的RDD依赖和DAG、Spark和Flink的容错机制区别、Flink的Watermark和窗口、数据倾斜的原因和解决方案。
这里最常考的一个点是数据倾斜。题目通常会给你一个场景,比如两张表JOIN时某个key的数据量特别大,导致reduce端负载不均,问你该怎么解决。我建议至少要掌握以下几个方案:对倾斜key加随机前缀打散、广播小表避免Shuffle、调整并行度、两阶段聚合。这些方案笔试会考,面试还会追问,值得深入理解而不是死记硬背。
还有一个高频考点是Spark和Flink在容错上的区别。Spark用Lineage血缘机制,通过RDD的血缘关系重新计算恢复数据;Flink用Checkpoint+分布式快照,通过周期性保存状态快照实现精确一次语义。这两个都是各自框架的核心机制,一定要能说清楚它们的设计思路和适用场景。
5.3 一道选择题启发我重新理解SQL执行顺序
笔试里有一道选择题我印象很深,问的是SQL语句中WHERE、GROUP BY、HAVING、SELECT、ORDER BY的执行顺序。看起来很简单,但很多平时写SQL的人根本说不清楚。正确答案是:FROM是最先执行的,然后WHERE,再GROUP BY,再HAVING,再SELECT,最后才是ORDER BY和LIMIT。这个执行顺序决定了你在WHERE里不能使用SELECT里定义的别名,但在ORDER BY里可以使用。
我后来把这个知识点记到了自己的SQL笔记里,因为很多业务SQL写错就是因为没搞清楚执行顺序。比如有人想对分组聚合后的结果过滤,把条件写在了WHERE里而不是HAVING里,结果报错或者结果不对。这种题考察的不是你会不会写SQL,而是你有没有真正理解SQL的执行逻辑,这也是工程数据岗需要具备的思维。
6. 时间分配与答题策略:别让会做的题被时间拖死
笔试时间有限,做题顺序和时间分配在很大程度上决定了你的成绩。我第一套模拟题就是吃了顺序的亏,一上来死磕算法题,结果后面简单的选择题都来不及做,白白丢了很多分。
6.1 我的答题顺序:先做选择题,再做SQL题,最后做算法题
我的实际策略是:拿到试卷先快速浏览一遍全部题目,对难度有个大致判断,然后按照"选择题 → SQL题 → 有思路的算法题 → 没思路的算法题"的顺序来做。理由是:选择题虽然知识点碎,但单个题耗时短、分值固定,适合在头脑最清醒的时候快速拿下;SQL题只要会写,得分效率很高,而且答案相对确定;算法题费时最长,不确定性最大,放到后面做更合理。
很多人的习惯是先做分值高的算法题,我觉得这恰恰是误区。大厂笔试的通过标准是总分过线,而不是某一道题拿满分。用更少的时间拿到更多的基础分,比花30分钟死磕一道算法题要划算得多。
6.2 每类题目的时间预算参考
我当时给自己定了一个时间预算,大概是这样:
| 题型 | 建议时间 | 策略说明 |
|---|---|---|
| 选择题 | 25-30分钟 | 每题控制在1-1.5分钟,不会的果断蒙一个并标记 |
| SQL题 | 25-30分钟 | 先写出正确版本,再考虑优化,不要一上来就写复杂解法 |
| 算法题第一题 | 20-30分钟 | 这是最容易拿分的算法题,全力以赴 |
| 算法题第二题 | 15-20分钟 | 10分钟没思路就写暴力解,拿保底分 |
| 检查和提交 | 10-15分钟 | 重点检查编译错误、输入输出格式、边界条件 |
这个预算不一定适合所有人,但有一个原则是通用的:一定要给检查留时间。我见过太多人写完代码就提交,结果因为Scanner读取格式不对或者没有处理空输入被扣分。这些不是不会做,而是太亏了。
6.3 编程题提交前的检查清单
说到提交检查,我总结了一个简单的清单,每次提交前都按这个过一遍:
- 输入输出格式是否正确?题目要求多组输入还是单组输入,用的是Scanner还是BufferedReader。
- 有没有处理边界条件?比如数组为空、k为0、字符串长度为1。
- 数据类型是否溢出?比如用int还是long,TopK和累加计算很容易溢出。
- 题目的方法签名是否符合要求?牛客笔试有时候要求你实现某个类方法,类名和方法名都不能改。
- 时间复杂度和空间复杂度是否会被卡?如果题目给的数据量很大,暴力解可能直接超时,至少要先算一下复杂度。
这些检查项看起来基础,但每次笔试都有人在上面翻车。我建议平时刷题的时候就养成这个习惯,不要只埋头写代码,要站在OJ的角度审题。
7. 笔试之后:复盘、面试衔接与抗遗忘安排
笔试结束不代表这件事就完了。我身边很多同学考完就彻底放飞自我,等收到面试通知才开始慌。实际上,从笔试到面试的间隔期是最重要的准备窗口,能不能利用好这段时间,决定了你在面试时是"讲得出深度"还是"只能聊表面"。
7.1 考后立刻做一次"趁热复盘"
笔试结束当晚,趁记忆还新鲜,我建议立刻做一次复盘。我当时是打开备忘录,把能回忆起来的题目类型、考察知识点、自己做错的点、有疑虑的题全部记下来。不要只记题目,要记"我为什么做错了"和"下一次遇到同类题应该怎么想"。这个动作看起来简单,但非常有价值,因为笔试考察的很多知识点在面试中会被问到,考后复盘相当于提前做了面试梳理。
比如我在SQL那道连续登录题上虽然做对了,但复盘时发现自己对"date_sub和row_number配合"的本质原理理解得不深。于是面试前我又专门搜了相关博客,重新理解了一遍"用日期减去编号把连续区间变成同一分组"这个思路的数学原理,结果面试时真的被追问了。如果不是考后复盘把这个薄弱点标记出来,我面试很可能答不好。
7.2 把笔试知识点延展成面试口语化表达
笔试是选择题和编程题,面试是口头交流和手撕代码,这中间有个转换过程。笔试时你只要选出正确答案,面试时面试官会追问"为什么选这个"和"还有没有其他方案"。
我的建议是,趁着笔试后对知识点的印象还很深,挑几个核心内容做一次"口述练习"。比如Flink的Checkpoint机制,你不能只说"通过Checkpoint实现状态恢复",要能讲清楚检查点是怎么触发的、Barrier对齐是怎么回事、精确一次和至少一次的区别、状态后端有哪些。再比如数据倾斜,不能只说"加随机前缀",要能说明加前缀后如何进行两阶段聚合、性能代价有多大、什么场景下适合用广播变量。这种把知识点从"认识"变成"会讲"的过程,是面试准备中最有价值的部分。
7.3 个人的一点体会:笔试是短板探测器
这次笔试给我最大的收获,不是"流程走完了",而是让我看清了自己在哪些方面还有短板。我在复习阶段自我感觉SQL还不错,但实际笔试时发现自己在窗口函数的某些边界场景下还是会犹豫;我以为自己对Spark很熟,但选择题里考到RDD的窄依赖和宽依赖时,有个选项让我纠结了很久。这些"自我感觉良好但实际还不够扎实"的地方,如果不去笔试检验,可能永远不会暴露。
所以我也想给正在准备秋招的人一个建议:不要只盯着笔试通过与否看,把它当成一次免费的压力测试和短板探测器。每一道做错的题、每一个卡壳的瞬间,都是后续复习最明确的指引。2024年秋招竞争确实不小,但工程数据岗的核心考察方向其实是清晰和稳定的,把算法、SQL、大数据组件、Java基础这四个方向扎扎实实准备好,笔试这关没有想象中那么可怕。