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 存成 4exp=-1),或者用字符串存,完全模拟手算。面试官提到的”科学计数法 / 两个整数相除”就是这个意思。

3. Redis 缓存异常

Q1: 缓存穿透 vs 缓存击穿 vs 缓存雪崩 (必须分清!)

异常现象原因解决办法
缓存穿透 (Penetration)不存在的数据恶意攻击 / 代码 Bug (查 ID=-1)1. 缓存空值 (Key=X, Val=null)
2. 布隆过滤器 (BloomFilter)
缓存击穿 (Breakdown)热点数据热点 Key 刚好过期,并发巨大,全打到 DB1. 互斥锁 (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)没问题,但深度原理实战经验有缺口:

  1. CAP:不能只背定义,要理解”为什么断网时必须二选一”。
  2. 精度问题:这是计算机组成原理的基础,理解二进制转换原理很重要。
  3. Redis 实战:穿透/击穿/雪崩是必考题,一定要背熟对应的解决方案(特别是击穿的互斥锁)。
  4. 项目缺失:面试官问无可问才去问 Kaggle,你需要把你的 Kratos 学习经历包装成一个”微服务改造项目”来撑场面。