自我介绍
面试官您好,我具备大数据开发工程师背景,专注于实时计算与数据平台架构。工作期间参与了多个核心项目,积累了 Flink、Spark 等组件的开发与调优经验。对 Flink 内核机制、调度策略及状态管理有深入研究,曾负责过流计算平台的版本升级与架构重构。
本文总结了字节跳动大数据开发岗位的面试经验,涵盖 Flink 实时计算平台架构、Ranger 鉴权机制、SQL 解析流程、Checkpoint 失败处理、窗口类型、Spark 3.0 AQE 优化特性以及常见算法题。重点解析了 Flink 状态后端、增量快照、Barrier 对齐等核心原理,并提供了 Java 实现的 LRU 缓存与旋转数组最小值查找代码示例,适合大数据工程师备考参考。

面试官您好,我具备大数据开发工程师背景,专注于实时计算与数据平台架构。工作期间参与了多个核心项目,积累了 Flink、Spark 等组件的开发与调优经验。对 Flink 内核机制、调度策略及状态管理有深入研究,曾负责过流计算平台的版本升级与架构重构。
该平台对标阿里云实时计算 Flink,基于 Flink 构建,提供一站式大数据计算与分析能力。核心功能包括多 Source/Sink 插件支持、统一元数据管理、一键提交、应用管理、断点调试、监控告警及 Ranger 鉴权模块。
主要职责包括:
Ranger 鉴权主要针对表级别的读写权限进行控制。
实现流程:
为何选择 Flink SQL 而非 Hive SQL 或 HDFS 鉴权?
Flink SQL 的执行计划生成主要经历以下四个阶段:
Parse(词法与语法分析)
parse() 方法,将 SQL 文本转换为未经校验的抽象语法树(AST, SqlNode)。Validate(语义校验)
validate() 方法,将 AST 转换为校验后的 SqlNode。Rel(关系代数转换)
rel() 方法,将 SqlNode 转换为关系代数树(RelNode)和行表达式(RexNode)。Convert(算子生成)
convert() 方法,将 RelNode 最终转换为 Execution Plan 中的 Operation。ModifyOperation。在生成 Operation 后,Flink 会进一步将其转化为物理执行计划,主要包含以下步骤:
TranslateToRel
Optimize(优化)
optimize() 方法,将逻辑计划优化为物理计划(FlinkPhysicalRel)。TranslateToExecNodeGraph
TranslateToPlan
常见的 RBO 规则包括:
Spark 3.0 引入了自适应查询执行(AQE),主要包含三项核心优化:
动态合并 Shuffle 分区
动态调整 Join 策略
动态优化数据倾斜 Join
当两张表 Join 时,若 A 表数据量超过广播阈值,通常无法使用 Broadcast Hash Join。但 Spark 3.0 提供了灵活的处理方式:
Checkpoint 失败通常与反压(Backpressure)密切相关,主要原因如下:
1. 数据流动缓慢导致超时
2. 状态数据过大
Flink 窗口由两个属性定义:窗口长度(Size)和滑动间隔(Interval)。
示例:
timeWindow(Time.seconds(10), Time.seconds(5)):每 5 秒触发一次,计算过去 10 秒内的数据。countWindow(10, 5):每 5 条数据触发一次,计算最近 10 条数据。题目:给定一个有序数组,前 n 位往后移,求其中的最小值。 思路:利用二分查找,时间复杂度 O(log n),空间复杂度 O(1)。
public class Main {
public static void main(String[] args) {
int[] nums = {4, 5, 6, 7, 1, 2, 3};
System.out.println(test(nums));
}
public static int test(int[] nums) {
int low = 0;
int high = nums.length - 1;
while (low < high) {
int mid = (low + high) / 2;
// 如果中间元素小于右边界,说明最小值在左半部分(含 mid)
if (nums[mid] < nums[high]) {
high = mid;
} else {
// 否则最小值在右半部分(不含 mid)
low = mid + 1;
}
}
return nums[low];
}
}
原理:最近最少使用(Least Recently Used),淘汰最久未使用的数据。 实现:哈希表 + 双向链表。
public class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) { key = _key; value = _value; }
}
private Map<Integer, DLinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
DLinkedNode newNode = new DLinkedNode(key, value);
cache.put(key, newNode);
addToHead(newNode);
++size;
if (size > capacity) {
DLinkedNode tailNode = removeTail();
cache.remove(tailNode.key);
--size;
}
} else {
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
复杂度分析:

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online