15-字节飞书面试真题解析 (Go 语言版)
💡 背景:这是一份 26 届校招/实习(外包)的真实面经,包含一二面真题。虽然原题包含 Java 问题,但我们可以将其转化为 Go 语言视角的回答。
🟢 一面 (基础夯实)
1. 计算机网络
Q1: 七层/四层网络模型
- OSI 七层:物理层、数据链路层、网络层 (IP)、传输层 (TCP/UDP)、会话层、表示层、应用层 (HTTP)。
- TCP/IP 四层:网络接口层、网络层、传输层、应用层。
- 面试话术:“OSI 是理论模型,TCP/IP 是实际标准。平常我们主要关注传输层(TCP)和应用层(HTTP)。“
Q2: TCP 的可靠性怎么保证?
- 关键词:校验和、序列号/确认应答(ACK)、超时重传、流量控制(滑动窗口)、拥塞控制(慢启动/快重传)。
- 一句话:“通过给包编号、丢包重传、以及控制发送速度不把对方和网络塞满,来保证可靠性。“
Q3: HTTP 1.1 vs 2.0 vs HTTPS
- HTTP 1.1:长连接 (Keep-Alive),但有队头阻塞。
- HTTP 2.0:多路复用 (一个连接并发处理多个请求),头部压缩 (HPACK),服务端推送。
- HTTPS:HTTP + SSL/TLS 加密(握手耗时但安全)。
2. 操作系统 (OS)
Q1: 线程状态流转
- 新建 (New) → 就绪 (Runnable) ↔ 运行 (Running) → 阻塞 (Blocked) / 等待 (Waiting) → 终止 (Terminated)。
- Go 视角:Go 只有 GMP 状态(Running, Runnable, Waiting, Syndcall 等),没有”新建”,goroutine 一创建就是 Runnable。
Q2: 进程/线程通信方式 (IPC)
- 管道 (Pipe):单向,父子进程用。
- 消息队列:有格式的数据块。
- 共享内存:最快,需要加锁。
- 信号量 (Semaphore):控制访问数量。
- Socket:不同机器通信(K8s/微服务常用)。
3. MySQL 数据库
Q1: 索引下推 (ICP)
- 是什么:MySQL 5.6 后,把部分过滤逻辑”下推”给存储引擎,减少回表次数。
- 例子:
Select * from user where name like '张%' and age = 10。- 无 ICP:引擎查出所有姓张的 ID → 回表查所有字段 → Server 层过滤 age=10。
- 有 ICP:引擎查姓张的同时,顺便过滤 age=10 → 只有符合的才回表。
Q2: B+ 树 vs B 树
- B+ 树 (MySQL):数据全在叶子节点,叶子之间有指针串起来(适合范围查询,如
id > 100)。 - B 树:数据在所有节点上,范围查询要中序遍历,不仅慢还对 CPU 缓存不友好。
4. 语言特性 (Java → Go 迁移)
Q1: ThreadLocal (Java) 在 Go 里对应什么?
- Java ThreadLocal:每个线程独立的存储空间。
- Go 观点:Go 官方不推荐 Goroutine Local Storage (GLS)。
- Go 替代方案:
- Context:显式传递参数(推荐,清晰明了)。
- 第三方库 (gls):黑科技获取 GID,容易造成内存泄漏,尽量别用。
5. 设计模式
Q1: 单例模式 (Go 写法)
- 饿汉式:
var instance = &Singleton{}(init 阶段创建,天然安全)。 - 懒汉式 (双重检查):
sync.Once(Go 里的满分答案)。
var once sync.Once
var instance *Singleton
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}6. 手撕代码
Q1: 最长回文子串 (LeetCode 5)
- 思路:中心扩散法。遍历字符串,以每个字符为中心向两边扩散。
- 注意:奇数长度
aba(中心是 b) 和偶数长度abba(中心是 bb 缝隙) 都要考虑。
🔴 二面 (深度与实战)
1. Redis
Q1: ZSet 查 member score 的复杂度?
- O(1)。
- 原理:ZSet 底层同时用了 跳表 (SkipList) 和 哈希表 (Hash)。
- 查排名/范围用跳表 (O(logN))。
- 查具体元素 Score 用哈希表 (O(1))。
Q2: Redis 哈希表扩容 (Rehash)
- 渐进式 Rehash:
- 不会一次性把几百万数据搬过去(会卡死主线程)。
- 而是分批搬运:每次用户访问(增删改查)时,顺便搬一点;如果不访问,也有定时任务搬一点。
- 搬运期间,新数据写新表,旧数据查旧表(找不到再去新表)。
2. MySQL
Q1: 联合索引与”最左前缀”
- 索引
(name, age)。 - ✅
where name='xx' and age=10(走索引) - ✅
where name='xx'(走索引) - ❌
where age=10(不走索引,因为不知道 name) - 姓名 vs 性别谁在左?
- 区分度高的在左边!姓名的区分度远高于性别,所以
(name, gender)更好。
- 区分度高的在左边!姓名的区分度远高于性别,所以
3. 操作系统
Q1: 管道是什么进程都可以通信吗?
- 匿名管道 (
|):只能用于父子进程或兄弟进程(有血缘关系)。 - 命名管道 (FIFO):任何进程都可以。
4. SQL 手撕
Q1: 查出刚好选两门课的学生 ID
SELECT student_id
FROM course_selection
GROUP BY student_id
HAVING COUNT(course_id) = 2;5. 手撕代码
Q1: K 个一组翻转链表 (LeetCode 25 - Hard)
- 这是面试里的”顶级难度”题之一,考察逻辑严密性。
- Go 思路:
- 统计长度,算出能翻转几组。
- 遍历每组,用”头插法”或”局部翻转”。
- 连接每组的头尾。
func reverseKGroup(head *ListNode, k int) *ListNode {
dummy := &ListNode{Next: head}
pre := dummy
// 1. 计算长度
length := 0
for p := head; p != nil; p = p.Next {
length++
}
// 2. 翻转每组
for length >= k {
cur := pre.Next
next := cur.Next
for i := 0; i < k-1; i++ {
cur.Next = next.Next
next.Next = pre.Next
pre.Next = next
next = cur.Next
}
pre = cur
length -= k
}
return dummy.Next
}💡 总结与建议
这份面经非常典型,涵盖了互联网大厂基础岗的核心考点:
- 基础过硬:OS、网络、数据库原理问得很细(如 Rehash 细节)。
- 不考偏门:除了 Java 题需要转换思路,其他都是通用计算机基础。
- 手撕看重逻辑:链表翻转是经典,建议熟练背诵
K 个一组翻转的模板。