16-字节飞书面试真题解析 Vol.2 (Go 视角)
💡 背景:这是第二份飞书面经,包含了一面(基础)和二面(压力面)的真题。相比第一份,这份面试更侧重于底层原理(精度丢失)、分布式理论(CAP)和 缓存实战。候选人在二面中感到吃力,说明这些是薄弱环节。
🟢 一面 (常规八股)
1. 计算机网络 & 安全
Q1: DDoS 攻击与 TCP 三次握手
- 面试官提示其实是 SYN Flood。
- 原理:攻击者伪造大量 IP 发送第一次握手 (SYN),服务端回复 (SYN+ACK) 后由于是伪造 IP 永远收不到第三次握手 (ACK)。
- 后果:服务端的”半连接队列”(Syn Queue)被占满,无法处理正常请求。
- 防御:
- 调大队列长度。
- SYN Cookie(不存半连接,而是通过计算 Cookie 验证,Go/Linux 默认开启)。
2. 操作系统
Q1: Epoll (IO 多路复用)
- 是什么:Linux 内核提供的处理大并发 IO 的机制。
- 对比 Select/Poll:
- Select/Poll:轮询所有连接 (O(N)),连接多了就慢。
- Epoll:事件驱动 (O(1)),只有活跃的连接才会触发回调。
- Go 的关联:Go 的网路库 (Netpoller) 底层就是 Epoll,所以 Go 能轻松承载百万协程。
3. Linux 操作
Q1: 常用命令
- 必须熟练:
top(看负载),ps -ef(看进程),grep(搜日志),netstat/ss(看网络),tail -f(追日志),chmod(改权限)。
🔴 二面 (原理与系统设计 - 汗流浃背版)
1. 分布式理论 (CAP)
Q1: 为什么 CAP 只能三选二?
- C (Consistency):一致性(所有节点数据同一时刻完全一样)。
- A (Availability):可用性(服务一直可用,不报错)。
- P (Partition Tolerance):分区容错性(网线断了,系统还能跑)。
- 核心逻辑:
- 在分布式系统里,网络一定可能断 (P 必须存在)。
- 当网络断了(P 发生):
- 如果你要保数据一致 (C),就得禁止写入(等网络恢复),那就不可用了 (A 牺牲)。
- 如果你要保服务可用 (A),就得允许写入,但此时无法同步给断联的节点,数据就不一致了 (C 牺牲)。
- 结论:P 是前提,只能在 CP (强一致,如 Etcd/Zookeeper) 和 AP (高可用,如 Redis Cluster/Eureka) 之间选。
2. 计算机基础 (精度问题)
Q1: 为什么 0.4 会丢失精度?(二进制表示)
-
十进制转二进制:小数部分是”乘 2 取整”。
-
推导:
0.4 * 2 = 0.8 -> 取 0 0.8 * 2 = 1.6 -> 取 1 0.6 * 2 = 1.2 -> 取 1 0.2 * 2 = 0.4 -> 取 0 (回到开头了!) ... 0011 0011 循环无尽 -
因为计算机位数有限(float 32位,double 64位),存不下无限循环小数,只能截断 → 精度丢失。
-
Python/C++ 怎么解决?
- 本质方法:使用 BigDecimal (Java) 或 decimal (Go
github.com/shopspring/decimal) 库。 - 原理:把小数变成两个整数存(比如 0.4 存成
4和exp=-1),或者用字符串存,完全模拟手算。面试官提到的”科学计数法 / 两个整数相除”就是这个意思。
- 本质方法:使用 BigDecimal (Java) 或 decimal (Go
3. Redis 缓存异常
Q1: 缓存穿透 vs 缓存击穿 vs 缓存雪崩 (必须分清!)
| 异常 | 现象 | 原因 | 解决办法 |
|---|---|---|---|
| 缓存穿透 (Penetration) | 查不存在的数据 | 恶意攻击 / 代码 Bug (查 ID=-1) | 1. 缓存空值 (Key=X, Val=null) 2. 布隆过滤器 (BloomFilter) |
| 缓存击穿 (Breakdown) | 查热点数据 | 热点 Key 刚好过期,并发巨大,全打到 DB | 1. 互斥锁 (Singleflight) 2. 逻辑过期 (后台异步更新,永不过期) |
| 缓存雪崩 (Avalanche) | 查大量数据 | 大量 Key 同时过期 / Redis 挂了 | 1. 过期时间加随机值 2. Redis 高可用 (Cluster) |
你的情况:你说”缓存击穿”没讲出来,其实就是热点 Key 过期的问题。解决核心是不要让所有人一起去查库,派一个人去(加锁),其他人等。
4. 设计模式 (编程实现)
Q1: 观察者模式 (Observer) - Go 实现
场景:EventBus,消息通知。
// 1. 定义观察者接口
type Observer interface {
Update(msg string)
}
// 2. 具体的观察者(比如:用户)
type UserObserver struct {
Name string
}
func (u *UserObserver) Update(msg string) {
fmt.Printf("[%s] 收到通知: %s\n", u.Name, msg)
}
// 3. 被观察者(Subject)
type Subject struct {
observers []Observer
}
func (s *Subject) Attach(o Observer) {
s.observers = append(s.observers, o)
}
func (s *Subject) Notify(msg string) {
for _, o := range s.observers {
o.Update(msg)
}
}
// 用法
func main() {
sub := &Subject{}
sub.Attach(&UserObserver{Name: "张三"})
sub.Attach(&UserObserver{Name: "李四"})
sub.Notify("开会了!")
}Q2: 工厂模式 (Factory) - Go 实现
场景:根据类型创建不同对象。
type Shape interface {
Draw()
}
type Circle struct{}
func (c Circle) Draw() { fmt.Println("画圆") }
type Square struct{}
func (s Square) Draw() { fmt.Println("画方") }
// 简单工厂
func NewShape(t string) Shape {
switch t {
case "circle":
return Circle{}
case "square":
return Square{}
default:
return nil
}
}💡 总结与弱点分析
从二面情况看,你的常规八股(网络、OS)没问题,但深度原理和实战经验有缺口:
- CAP:不能只背定义,要理解”为什么断网时必须二选一”。
- 精度问题:这是计算机组成原理的基础,理解二进制转换原理很重要。
- Redis 实战:穿透/击穿/雪崩是必考题,一定要背熟对应的解决方案(特别是击穿的互斥锁)。
- 项目缺失:面试官问无可问才去问 Kaggle,你需要把你的 Kratos 学习经历包装成一个”微服务改造项目”来撑场面。