Zer0e's Blog

2026面试复盘3

字数统计: 12.3k阅读时长: 46 min
2026/09/21 Share

前言

又是两场面试,一场是北京的量化公司好像,感觉很不尊重面试者,被我自己pass了。另一家火山模型的SRE,经历不太匹配没后文了。
挺迷茫的。

复盘

按主题分四块整理:

  • 一、网络与算法基础
  • 二、容器与 K8s
  • 三、存储与中间件
  • 四、大模型 SRE 与 AI Agent

一、网络与算法基础

Q1. TCP 三次握手的过程是怎样的?为什么不能是两次或四次?

面试的时候只答了个大概,”客户端发起握手包 → 服务端返回 ACK → 客户端再回复”,标志位和序列号细节都忘了。补一下完整版:

1
2
3
4
客户端                                     服务端
│ ──── SYN, seq=x ─────────────────────▶ │ 客户端:SYN_SENT 服务端:LISTEN→SYN_RCVD
│ ◀─── SYN+ACK, seq=y, ack=x+1 ───────── │
│ ──── ACK, seq=x+1, ack=y+1 ──────────▶ │ 双方:ESTABLISHED

三次握手的本质是**双方各确认一次”自己的收发能力”和”对方的收发能力”**,并同步双方的初始序列号(ISN)。

  • 为什么不能是两次:服务端一收到 SYN 就 ESTABLISHED,会有两个致命问题——① 网络中滞留的旧 SYN 报文姗姗来迟,服务端会误建连接、白分配资源;② 客户端的 ISN 没有得到 ACK 确认,双方序列号没达成一致
  • 为什么不需要四次:TCP 支持捎带确认(piggyback),SYN+ACK 可以合并成一个报文,一次 RTT 同时完成”确认对方 + 发起自己”

高频追问:SYN Flood 攻击

攻击者伪造源 IP 大量发 SYN,服务端维持海量半连接(SYN_RCVD 队列)耗尽资源。防御手段:

手段 说明
SYN Cookie 不占半连接队列,用 cookie 验算合法性,net.ipv4.tcp_syncookies=1
调大半连接队列 net.ipv4.tcp_max_syn_backlog
减少 SYN+ACK 重试 net.ipv4.tcp_synack_retries
全连接队列 somaxconn 与应用 backlog 取小,满了直接丢弃新连接,表现为偶发连接超时

Q2. TCP 拥塞控制有哪些算法?大致原理是什么?

面试时只答了”有个窗口,丢包就缩小”,具体算法名字一个没说出来。补齐一下:

经典四件套(Reno/Tahoe 体系):

阶段 行为
慢启动 初始 cwnd = 1 MSS,每收到一个 ACK 翻倍,指数增长,直到 ssthresh
拥塞避免 cwnd ≥ ssthresh 后改为线性增长(每 RTT +1 MSS)
快重传 收到 3 个重复 ACK 立即重传丢失报文,不等 RTO 超时
快恢复 快重传后 ssthresh = cwnd / 2,cwnd = ssthresh,进入拥塞避免(而不是回到慢启动)

现代主流算法:

  • CUBIC:Linux 默认,基于丢包的窗口增长曲线(三次函数),在长肥管道上比 Reno 收敛更快
  • BBR:Google 提出,不依赖丢包信号,主动测量瓶颈带宽(BtlBw)和最小 RTT(RTprop),以 BDP = BtlBw × RTprop 为目标窗口。在高延迟 / 有随机丢包的链路(跨洋、无线)上表现远好于 CUBIC
  • Vegas / DCTCP:分别基于 RTT 变化和 ECN 标记,做更早、更精细的拥塞判断

流量控制 vs 拥塞控制(很容易混):

  • 流量控制:管的是端到端的接收能力,接收方通过 window 字段通告缓冲余量
  • 拥塞控制:管的是网络链路的承载能力,是发送方基于丢包/延迟的自反馈
  • 实际发送窗口 = min(rwnd, cwnd)

Q3. 简单说下快排和归并排序的思路

面试时归并直接答不上来了,快排也讲得磕磕绊绊。这两个都是分治思想,但方向恰好相反:

快速排序(Quick Sort):先分再排

1
2
3
4
1. 选一个基准值 pivot(一般取首/尾/随机/三数取中)
2. 分区(partition):把 < pivot 的放左边,> pivot 的放右边
3. 对左右两侧递归执行 1-2
4. 递归出口:区间只剩 1 个元素
  • 时间复杂度:平均 O(n log n),最坏 O(n²)(有序数组 + 固定取首/尾做 pivot),随机化 pivot 可规避
  • 空间复杂度:O(log n)(递归栈)
  • 不稳定排序,但常数因子小、原地排序,工程上最常用

归并排序(Merge Sort):先拆再合

1
2
3
4
1. 把数组从中间一分为二
2. 递归对左右两半分别排序
3. 合并(merge):用双指针把两个有序数组合并成一个有序数组
4. 递归出口:区间只剩 1 个元素
  • 时间复杂度:**稳定 O(n log n)**,最坏也是这个
  • 空间复杂度:O(n),需要额外数组存合并结果
  • 稳定排序,天然适合链表(合并可以原地做)和外部排序(磁盘/多路归并)

一句话对比:快排”分区在合并前”、原地但不稳定;归并”分区在合并后”、稳定但要额外空间。工程上:内存排序多用快排(如 C 的 qsort、C++ 的 std::sort 是 introsort = 快排 + 堆排 + 插入排序),外部排序和链表用归并。

Q4. 你写代码吗?Python 熟不熟?

日常写运维脚本、Agent 逻辑都是 Python,够用。但确实”AI 加持下人与人差距越来越小”,很多东西直接让 AI 生成,自己反而对基础语法细节(装饰器、生成器、上下文管理器、asyncio)有点生疏。

准备面试的话建议至少能口头讲清楚:

  • GIL 是什么,为什么 CPU 密集型用多进程、IO 密集型用多线程/协程
  • 装饰器的本质是”接受函数返回函数”,functools.wraps 保留元信息
  • 生成器(yield)与迭代器的区别,惰性求值节省内存
  • asyncio 的 event loop、async/await、asyncio.gather 并发

二、容器与 K8s

Q5. 容器和虚拟机(VMware)有什么区别?

核心差异在于虚拟化的层次:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──────────────────────────────┐    ┌──────────────────────────────┐
│ 虚拟机(VM) │ │ 容器(Container) │
├──────────────────────────────┤ ├──────────────────────────────┤
│ App A │ App B │ App C │ │ App A │ App B │ App C │
│ Bins │ Bins │ Bins │ │ Bins │ Bins │ Bins │
│ Guest OS│ Guest OS│ Guest OS│ ├──────────────────────────────┤
├──────────────────────────────┤ │ Container Runtime (containerd)│
│ Hypervisor (VMware/KVM) │ ├──────────────────────────────┤
├──────────────────────────────┤ │ Host OS (Linux Kernel) │
│ Host OS │ ├──────────────────────────────┤
├──────────────────────────────┤ │ Hardware │
│ Hardware │ └──────────────────────────────┘
└──────────────────────────────┘
维度 虚拟机 容器
虚拟化层次 硬件级(Hypervisor 虚拟出 CPU/内存/设备) 操作系统级(共享内核,进程隔离)
是否含 OS 每个 VM 都带完整 Guest OS 只有应用与依赖,共享 Host Kernel
启动速度 分钟级 秒级 / 毫秒级
资源开销 GB 级 MB 级
隔离强度 强(Hypervisor 边界) 较弱(共享内核,靠 Namespace + Cgroup)
典型代表 VMware、KVM、Hyper-V Docker、containerd、CRI-O

公有云的形态:一台物理机上通常运行几十个甚至上百个 VM,Hypervisor 把物理资源(CPU、内存、磁盘、网络带宽)切分给上层 VM;VM 内再跑容器也很常见(”套娃”部署)。

Q6. CGroup 和 Namespace 分别起什么作用?

这题面试时答混了——把”隔离”也归给了 CGroup,实际上隔离是 Namespace 的活。厘清一下:

技术 定位 类比
Namespace 隔离:让进程”看不见”其他进程/资源 一堵墙,把视图分开
CGroup 限制:限制、审计、控制进程能用的资源量 一个配额表,规定你能吃多少

Namespace 的 8 种(Linux 5.6+):

Namespace 隔离内容
PID 进程 ID,容器内 PID=1 是主进程
Mount (mnt) 挂载点、文件系统视图
Network 网卡、IP、端口、路由表、iptables
UTS 主机名和域名
IPC 信号量、消息队列、共享内存
User 用户/组 ID 映射(容器内 root ≠ 宿主 root)
Cgroup cgroup 根目录视图
Time 系统时间(较新)

CGroup 的能力:

  • 资源限制:CPU(cpu.max 配额)、内存(memory.max)、IO 带宽(blkio)、PID 数量(pids.max)
  • 优先级:CPU 权重(cpu.weight)
  • 审计:cpuacct、memory.usage_in_bytes 统计用量
  • 控制:冻结/恢复进程组(freezer)

K8s 里 request/limit 的落地:

1
2
3
4
limit.cpu=1  → cgroup v1: cpu.cfs_quota_us=100000, cpu.cfs_period_us=100000
→ cgroup v2: cpu.max = "100000 100000"
limit.memory=1Gi → memory.max = 1073741824(超限触发 OOMKilled)
request.cpu=500m → cpu.weight 按比例分配(超卖时按权重抢占)

一句话:**Namespace 决定”能看到什么”,CGroup 决定”能用多少”**,两者共同构成了容器的底层实现。

Q7. K8s 中 Deployment、Pod、Service 三者是怎么关联的?

面试时”Selector”这个词一时没听清,其实答案很简单——通过 Label 和 Selector 串起来:

1
2
3
4
5
6
7
8
9
10
11
Deployment                ReplicaSet                Pod                    Service
│ │ │ │
│ spec.template.metadata │ │ │
│ labels: {app: web} │ │ │
├──────────────────────────▶ 创建 │ │
│ ├─────────────────────▶ 打上 label {app: web} │
│ │ │ │
│ │ │◀───────────────────────┤
│ │ │ spec.selector: │
│ │ │ matchLabels: │
│ │ │ app: web │
  • Deployment:定义 Pod 模板(spec.template)和更新策略,通过管理 ReplicaSet 间接管理 Pod
  • Pod:从 Deployment 模板生成,自动带上模板中的 labels
  • Service:通过 spec.selector 选中一组 Label 匹配的 Pod,把它们的 IP 收集到 Endpoints / EndpointSlice 中;访问 Service ClusterIP 时由 kube-proxy 转发到某个后端 Pod

关键点:

  1. Label 是唯一的粘合剂——Deployment 与 Pod、Service 与 Pod、Job 与 Pod 全靠 Label
  2. Selector 不匹配 = Endpoints 为空:80% 的”Service 访问不通”就出在这里
  3. Deployment 不直接管 Pod,中间隔了一层 ReplicaSet,这也是”滚动更新 = 新旧 RS 交替扩缩”的实现基础

Q8. 讲一下 K8s 的 QoS 等级?驱逐顺序是怎样的?

面试时答了个大概——三种 QoS,request=limit 是最高级,什么都没设的最先被杀。完整版:

QoS 等级 判定条件 驱逐优先级
Guaranteed 所有容器 requests == limits(CPU 和内存都设且相等) 最后被驱逐
Burstable 至少一个容器设置了 requests 或 limits,但不满足 Guaranteed 中间
BestEffort 完全没设置 requests/limits 最先被驱逐

驱逐触发时机:

  • 节点资源不足:kubelet 检测到 MemoryPressure / DiskPressure / PIDPressure
  • 超过 eviction threshold:默认 memory.available < 100Mi、nodefs.available < 10%、imagefs.available < 15%
  • cgroup OOM:容器超过自身 memory limit,内核 oom-killer 直接干掉

同 QoS 等级内的排序:按实际用量超过 requests 的百分比排序,超得越多越先被驱逐。这也是”Burstable 里 limit 定得离谱的容器先死”的原因。

驱逐流程:

1
2
kubelet 检测到压力 → 按 QoS + 超额比例打分 → 逐个 Evict(走 GracefulTermination)
→ 触发 API Server 的 eviction 子资源 → Pod 进入 Terminating → controller 补新副本

PodDisruptionBudget(PDB) 是另一层保护:即使被驱逐,也要保证 minAvailable / maxUnavailable 不被突破,防止一次驱逐干掉太多副本。

Q9. 如果节点上只剩 Guaranteed Pod 且内存不足,还会被驱逐吗?

面试时答”理论上不会,除非超卖”——这个说法不够准确。补一下:

结论:会,但通常是节点级 OOM,而不是 kubelet 主动驱逐。

分两种情况:

  1. kubelet 主动驱逐:只要节点资源低于阈值,即使是 Guaranteed 也会被驱逐。kubelet 驱逐是按分数排序的,Guaranteed 分数最低所以最后被杀,但不代表不杀。当所有 Pod 都是 Guaranteed 且节点内存持续下压时,最终一定会挑一个杀
  2. cgroup OOM:Guaranteed 的定义是 requests == limits,容器内存使用不会超过自身 limit(超了立即被 cgroup OOM Killer 干掉)。但如果 kubelet / 系统进程 / kernel slab 占用过多,节点整体内存告急,Guaranteed Pod 也会因为节点级压力被驱逐

为什么 Guaranteed 更安全:

  • 不会被 CPU throttle(cpu.cfs_quota_us 匹配 cpu.shares)
  • 内核 OOM 打分最低(oom_score_adj = -997,BestEffort 是 1000)
  • 节点资源规划更准(requests 就是实际占用)

极端情况下的兜底:system-reserved 和 kube-reserved 预留一部分资源给系统进程和 kubelet,避免业务 Pod 把节点吃死。

Q10. 什么是节点污点(Taint)?除了标记 NotReady 还有什么用?

面试时只答了”kubelet 掉线会打 NotReady 污点驱逐 Pod”,其他作用完全没答上。补全:

污点的本质:节点主动拒绝 Pod,与亲和性(Pod 选节点)方向相反。

语法与三种 effect:

1
2
kubectl taint nodes node1 gpu=true:NoSchedule
kubectl taint nodes node1 dedicated=team-a:NoExecute
effect 行为
NoSchedule 不调度新 Pod 上来,已有的不动
PreferNoSchedule 尽量不调度,实在没地方还是会上
NoExecute 不调度新 Pod,且驱逐已存在的不容忍 Pod

Pod 侧通过 tolerations 声明容忍:

1
2
3
4
5
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"

除了 NotReady,污点的核心用途:

场景 说明
专用节点池 GPU 节点、高内存节点、SSD 节点,打污点防止普通负载混入,配合 toleration 定向调度
节点故障自动隔离 kubelet 心跳丢失时,Node Controller 自动打 node.kubernetes.io/unreachable:NoExecute,5 分钟后驱逐 Pod(tolerationSeconds: 300)
节点维护 kubectl drain 会打上 node.kubernetes.io/unschedulable:NoSchedule,配合驱逐完成滚动维护
控制面隔离 Master 节点默认打 node-role.kubernetes.io/control-plane:NoSchedule,业务 Pod 不会调度上去
多租户/环境隔离 生产、测试节点池用不同污点隔离,防止误部署

K8s 内置的几个自动污点:

  • node.kubernetes.io/not-ready:节点未就绪
  • node.kubernetes.io/unreachable:节点失联
  • node.kubernetes.io/memory-pressure / disk-pressure / pid-pressure:资源压力
  • node.kubernetes.io/unschedulable:手动 cordon

排查 Pod Pending 时看 describe pod 的 Events,node(s) had taint ... that the pod didn't tolerate 就是污点没容忍。

Q11. 如何排查一个处于 Pending 状态的 Pod?

面试时答了调度慢和探针两块,其中探针其实不影响 Pending 阶段(探针影响的是 Ready),这是一个概念偏差。

Pending 的准确定义:Pod 已被 APIServer 接受,但至少一个容器还没创建——要么还没被 scheduler 绑定到节点,要么已经在拉镜像 / 挂卷。

排查思路:kubectl describe pod 看 Events

1
kubectl describe pod <pod-name> -n <ns>

按 Events 里的关键字对号入座:

Events 关键字 原因 处置
Insufficient cpu / Insufficient memory 节点可分配资源不足 扩容节点 / 调小 requests / 检查是否被其他 Pod 挤占
node(s) had taint ... that the pod didn't tolerate 污点没容忍 加 toleration 或去掉节点污点
node(s) didn't match Pod's node affinity/selector 节点选择器/亲和性太严 检查 nodeSelector / nodeAffinity 表达式
persistentvolumeclaim is not bound PVC 没绑定 检查 StorageClass、provisioner、容量、访问模式
Failed to pull image / ImagePullBackOff 镜像拉不下来 检查镜像名、tag、私有仓库凭证、网络
0/N nodes are available 综合原因 逐条看后面的详情
没有任何 Events Scheduler 未处理 检查 kube-scheduler 是否正常

排查流程:

1
2
3
4
5
6
1. kubectl get pod -o wide              看是否已被绑定节点(NODE 列有没有值)
2. kubectl describe pod 看 Events,90% 的 Pending 原因都在这里
3. kubectl get events --sort-by=... 集群视角看事件
4. kubectl describe node 看目标节点的资源、污点、状态
5. journalctl -u kubelet 已调度到节点后卡住时看 kubelet 日志
6. journalctl -u kube-scheduler 调度层面异常时看 scheduler 日志

已调度但仍 Pending(ContainerCreating):多半是镜像拉取慢或volume 挂载失败,看 kubelet 日志和 CSI driver 日志。

Q12. K8s 有哪几种探针?分别用来做什么?

三种探针,用途完全不同,触发时机也不同:

探针 目的 失败后果 什么时候开始
startupProbe 判断应用是否完成启动 未达到 failureThreshold 前屏蔽其他两个探针;超过则杀掉重启 容器创建后立即
livenessProbe 判断容器是否存活 重启容器(按 restartPolicy) startup 通过后开始
readinessProbe 判断容器是否能接流量 从 Service Endpoints 摘除,不重启 startup 通过后开始

探测方式(三种探针通用):

  • httpGet:请求指定 path,2xx/3xx 视为成功
  • tcpSocket:尝试建立 TCP 连接
  • exec:容器内执行命令,退出码 0 视为成功
  • grpc(1.24+):调用 gRPC Health Checking 协议

关键参数:

1
2
3
4
5
6
7
8
9
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30 # 容器启动后多久开始探测
periodSeconds: 10 # 探测间隔
timeoutSeconds: 1 # 单次探测超时
successThreshold: 1 # 连续成功几次算成功(liveness/startup 必须为 1)
failureThreshold: 3 # 连续失败几次算失败

startupProbe 的价值:以前处理慢启动应用只能把 livenessProbe.initialDelaySeconds 调很大,但这样又让”启动后崩溃”的检测变慢。startupProbe 用一个宽松的 failureThreshold × periodSeconds(比如 30 × 10s = 5 分钟)作为启动上限,通过后再让 liveness/readiness 用严格的参数接管。

Q13. 探针配置不合理会导致什么问题?

这题面试时答得还行,三种探针都能举出反例。系统化整理:

Liveness 配置不当 → 无谓重启

反例 后果
initialDelaySeconds 太小 + 没配 startupProbe 应用还没起来就被杀,进入 CrashLoopBackOff
failureThreshold 太小(如 1) 一次接口抖动就重启,把偶发变故障
探针路径依赖下游(如 /health 里查数据库) 下游抖动导致上游服务被集体重启,级联雪崩
timeoutSeconds 太小 + 应用 GC 卡顿 长时间 STW 时被误判死亡
CPU limit 太紧 + 探针用 exec 探针命令自己被 throttle,超时失败

最佳实践:liveness 只探测进程本身的健康(能否响应、是否死锁),不要探测下游依赖。

Readiness 配置不当 → 流量打飞

反例 后果
initialDelaySeconds 太小 应用还没预热完就接流量,前几个请求超时/报错
探针太宽松(如仅 TCP 探测) 端口通了但业务未 ready,请求进去报错
periodSeconds 太大 应用异常后,摘除延迟高,一段时间内流量还在打过去
Readiness 依赖下游 下游抖动导致所有上游被摘除,流量无后端可用

最佳实践:readiness 探测是否能处理业务请求(缓存预热完成、连接池就绪、依赖服务可达),但不要让它承担熔断职责。

Startup 配置不当 → 慢启动应用被杀

  • failureThreshold × periodSeconds 小于应用真实启动时间 → 永远起不来
  • 忘记配 startupProbe,直接用 liveness 兜底 → 老问题回归

排查手段:kubectl describe pod 看 Events 里的 Liveness probe failed / Readiness probe failed / Unhealthy;kubectl get events -A --field-selector reason=Unhealthy。

Q14. HPA 是怎么工作的?大模型场景下有什么变化?

传统 HPA 原理:

1
期望副本数 = ceil[ 当前副本数 × (当前指标值 / 目标指标值) ]

例如:当前 3 副本、平均 CPU 80%、目标 50% → 3 × 80/50 = 4.8 → 扩到 5。

指标链路:

1
2
3
kubelet(cAdvisor) → metrics-server → HPA Controller
↓
修改 Deployment.replicas
  • Resource Metrics(CPU/内存):走 metrics.k8s.io,由 metrics-server 提供
  • Custom Metrics(QPS、队列长度):走 custom.metrics.k8s.io,通常由 Prometheus Adapter 提供
  • External Metrics(消息队列堆积、外部服务指标):走 external.metrics.k8s.io,KEDA 是这层的常用方案

必备前提:Pod 必须设置 requests。CPU 利用率 = 实际用量 / requests,不设 requests HPA 直接算不出来,显示 <unknown>。

大模型场景的差异:

传统 CPU/内存指标在推理服务上不够敏感——GPU 打满时 CPU 可能才 20%,靠 CPU 扩容永远跟不上。业界主流是基于业务队列指标做扩容:

指标 说明 来源
Batch Size 当前连续批处理的样本数,接近上限说明容量吃紧 vLLM / TGI / rtp-llm 的 /metrics
Waiting Queue Length 排队中的请求数,反映真实积压 推理引擎或网关埋点
TTFT (Time To First Token) 首 token 延迟,SLO 关键指标 客户端或网关采集
Prefill 负载 / KV Cache 使用率 长上下文场景下 KV Cache 是最紧的资源 引擎内部指标
GPU 显存利用率 / SM 利用率 硬件层面兜底 DCGM Exporter

实现路径:推理引擎暴露 Prometheus 指标 → Prometheus Adapter 转成 Custom Metrics → HPA 按 batch size 或队列长度扩容。

注意点:

  1. 模型 Pod 启动慢(几十秒到几分钟:拉镜像 + 加载权重 + 预热),HPA 的 15s 决策周期跟不上突发流量,必须预扩容 + 预热池兜底
  2. 扩容粒度大:一个模型副本可能占 8 卡,扩容步长要控制,避免抖动
  3. 缩容要保守:behavior.scaleDown.stabilizationWindowSeconds 建议 5-10 分钟,防止流量稍降就缩掉

加分项:完整弹性链路

1
HPA (Pod 级) → Cluster Autoscaler / Karpenter (节点级) → 云厂商弹性伸缩组 (机器级)

大模型场景因为节点是 GPU,扩容速度和库存都受限,通常还会配合多集群切流(Karmada / 自研调度器)做跨可用区、跨集群的容量调度。

三、存储与中间件

Q15. MongoDB 和 MySQL 的区别?各自的高可用和扩容方案是什么?

核心定位差异:

维度 MySQL MongoDB
数据模型 关系型,行列表结构 + 强 Schema 文档型,BSON(二进制 JSON),Schema-less
查询语言 SQL(标准化) MQL(JSON 风格)
事务 完整 ACID,多表 JOIN 强 4.0 起支持多文档事务,但不推荐重度使用
适用场景 结构化业务数据(订单、账户、用户) 半结构化 / 灵活 Schema(日志、内容、IoT、向量)
扩展方式 垂直扩展为主,水平扩展靠分库分表中间件 原生分片(Sharding),水平扩展友好
AI 时代加分项 需借助外部向量库(Milvus / Pinecone) 内置向量索引(Atlas Vector Search),可直接做 RAG

MySQL 的高可用与扩容:

  • 高可用:主从复制(异步 / 半同步 / 组复制 MGR)、MHA / Orchestrator 做自动故障转移、云上一般用 RDS 三节点企业版
  • 读写分离:中间件(MyCat / ShardingSphere / ProxySQL)或应用层路由
  • 水平扩容:分库分表——按业务垂直拆分(订单库、用户库)+ 按 hash / range 水平拆分(user_id % 32)
  • 痛点:分片键选择难,跨分片 JOIN 和分布式事务复杂

MongoDB 的高可用与扩容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        ┌────────────┐    ┌────────────┐    ┌────────────┐
Client─▶│ mongos │───▶│ mongos │───▶│ mongos │ ← 路由层
└────────────┘ └────────────┘ └────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────┐
│ Config Server (副本集,存元数据和分片规则) │
└──────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (ReplicaSet)│ │ (ReplicaSet)│ │ (ReplicaSet)│ ← 数据层
└────────────┘ └────────────┘ └────────────┘
  • mongos:路由进程,客户端连它,它查 Config Server 决定去哪个 Shard
  • Config Server:存分片元数据,本身也是副本集
  • Shard:每个 Shard 是一个独立的副本集(Primary + Secondary + Arbiter),保证单分片高可用
  • 扩容:加 Shard → 在 Config Server 注册 → mongos 自动感知 → 数据按 chunk 迁移(balancer 后台均衡)
  • 分片键:一旦选定不可更改,选不好会导致数据倾斜(jumbo chunk)

选型建议:交易、账务等强一致 + 复杂关联用 MySQL;日志、内容、行为埋点、AI 向量检索等灵活 Schema + 水平扩展用 MongoDB。

Q16. Redis 和 InfluxDB 分别是什么?各自的应用场景?

Redis:内存 KV 数据库 / 缓存中间件

  • 数据结构:String、Hash、List、Set、ZSet、Bitmap、HyperLogLog、Geo、Stream
  • 典型场景:
    • 缓存:热点数据(用户信息、配置、Session),降低数据库压力
    • 分布式锁:SETNX + Lua 保证原子性(Redlock 有争议,一般用单点 + 短 TTL)
    • 限流计数:INCR + EXPIRE 或 Lua 脚本实现滑动窗口 / 令牌桶
    • 排行榜:ZSet 天然支持排序
    • 消息队列:List / Stream(轻量场景)
    • 发布订阅:Pub/Sub

InfluxDB:时序数据库(TSDB)

  • 数据模型:以时间戳 + tag + field 组织,写入即追加、极少更新
  • 典型场景:
    • 监控指标存储:Prometheus 自带 TSDB 默认存 15 天,长期存储接 InfluxDB / Thanos / VictoriaMetrics / M3DB
    • IoT 设备数据:传感器、车联网、智能家居
    • 业务指标:QPS、订单量、GMV 的时间序列分析
  • 优势:高写入吞吐、按时间范围压缩、内置降采样(continuous query)

Redis 的高可用与扩容架构:

形态 说明 适用场景
主从(Master-Slave) 主写从读,异步复制 读多写少,容量单机可容纳
哨兵(Sentinel) 主从 + 自动故障转移,哨兵集群监控主节点 需要高可用但容量单机够
Cluster(集群) 数据分片(16384 slot)+ 每个分片主从 + 自动 failover 大容量、高并发,突破单机内存限制
云 Redis 一般提供标准版(主从)/ 集群版 / 读写分离版 免运维

分片的价值:

  1. 突破单机内存:数据按 slot 分散到多个分片
  2. 抗热点:把大 key / 热 key 的影响半径限制在单个分片
  3. 提升并发:多分片并行处理请求

分片键设计:Redis Cluster 用 CRC16(key) % 16384,可以用 hash tag({user:123}.profile)把相关 key 强制分到同一 slot 支持多 key 操作。

Q17. 缓存穿透、击穿、雪崩是什么?怎么规避?

三个概念很容易混,用一张表拉清楚:

类型 现象 根因 影响
穿透(Penetration) 请求的 key 数据库里也不存在,每次都直查 DB 恶意攻击 / 数据本身不存在 DB 被无效查询压垮
击穿(Breakdown) 某个热点 key 突然过期,大量请求同时打到 DB 单点热 key TTL 到期 DB 瞬时压力激增
雪崩(Avalanche) 大量 key 同时过期(或 Redis 宕机),请求全部打到 DB TTL 设置雷同 / 集群故障 DB 被打死

穿透的规避:

手段 说明 权衡
布隆过滤器(前置) 请求先过 Bloom Filter,判定”一定不存在”的直接拒绝 有误判率(判无必无、判有可能无),需处理删除(用 Counting Bloom)
空值缓存 DB 查不到时把 null 写入 Redis,TTL 短(如 60s) 占内存,短期脏数据
接口鉴权 + 限流 拦截明显异常的请求(未登录、频率异常) 治标不治本
参数校验 前置校验 ID 合法性(如 UUID 格式、范围) 只解决部分场景

布隆过滤器原理速记:多个 hash 函数把元素映射到位数组,位为 0 一定不存在,位为 1 可能存在(不同元素的位可能重叠)。

击穿的规避:

手段 说明
互斥锁(Mutex) 热 key 过期时用 SETNX 抢锁,只放一个请求去查 DB 重建缓存,其他请求等待
热 key 不过期 逻辑过期(value 里存过期时间)+ 后台异步刷新,物理上永远不过期
提前预热 大促前把热 key 主动加载到 Redis
多级缓存 本地缓存(Caffeine)+ Redis,本地缓存挡住大部分请求

雪崩的规避:

手段 说明
TTL 加随机值 基础 TTL + random(0, 600) 秒,避免同时失效
Redis 高可用 哨兵 / Cluster,防止单点故障
限流熔断 应用层 Sentinel / Hystrix,DB 侧连接池上限
多级缓存 + 降级 Redis 挂了自动降级到本地缓存 + 兜底默认值
数据预热 服务启动或大促前预加载热点数据

一句话记忆:穿透是查不存在的东西、击穿是单个热 key 挂了、雪崩是一大批 key 同时挂了。

Q18. Redis 缓存和数据库如何保证一致性?

面试时答了”先更新 DB 再删缓存”,但表达有点乱,被面试官打断纠正。系统化整理:

先明确一致性的等级:

  • 强一致性:读写实时同步,性能代价大,通常需要分布式锁 + 事务,一般不用
  • 最终一致性:允许短时间窗口内不一致,最终收敛。绝大多数业务的选择

四种经典策略对比:

策略 流程 问题
先更新 DB,再更新缓存 写 DB → 写 Redis 并发下缓存写入顺序错乱,容易残留旧值;写多读少时浪费
先更新缓存,再更新 DB 写 Redis → 写 DB DB 失败时缓存脏了;同样有并发问题
先删缓存,再更新 DB(Cache Aside 变种) 删 Redis → 写 DB 删除后写 DB 前,另一个请求读缓存 miss → 从 DB 读旧值 → 写回缓存,长时间脏数据
先更新 DB,再删缓存(Cache Aside 标准做法) 写 DB → 删 Redis 极端情况下也可能不一致,但概率极低

推荐:Cache Aside Pattern(旁路缓存)

1
2
读:  先读 Redis → miss 则读 DB → 回填 Redis(带 TTL)→ 返回
写: 先更新 DB → 再删除 Redis(不是更新!)

**为什么是”删”而不是”更新”**:

  1. 更新可能存在并发覆盖(A 更新完 B 又更新,但 A 后写入)
  2. 有些缓存是多表 JOIN 计算出来的,更新代价高,删了让下次读时懒加载更划算
  3. 删除是幂等的,失败重试也安全

为什么”先删缓存 + 后写 DB”不安全:

1
2
3
4
5
6
7
时间线:
T1: 线程 A 删除缓存
T2: 线程 B 读缓存 miss
T3: 线程 B 从 DB 读到旧值
T4: 线程 B 把旧值写回缓存
T5: 线程 A 更新 DB
→ 缓存里是旧值,DB 里是新值,长期不一致

“先更新 DB + 后删缓存”的极端场景(发生概率极低):

1
2
3
4
5
6
T1: 线程 A 读缓存 miss
T2: 线程 A 从 DB 读到旧值
T3: 线程 B 更新 DB
T4: 线程 B 删除缓存
T5: 线程 A 把旧值写回缓存
→ 需要"A 的 DB 读比 B 的 DB 写还慢",实际很罕见

工程加固手段:

手段 说明
延迟双删 更新 DB → 删缓存 → 睡一小段时间(如 500ms)→ 再删一次,兜底上面的极端场景
订阅 binlog(Canal / Debezium) 通过 MySQL binlog 异步同步缓存,业务代码只写 DB,缓存由订阅者维护,解耦 + 顺序保证
消息队列 + 重试 删缓存失败时进 MQ 重试,保证最终成功
TTL 兜底 缓存都带 TTL,即使某次删除失败,最迟 TTL 到期也会自愈
分布式锁 强一致场景下用 Redisson 锁住 key,读写串行化,性能差

面试话术模板:

“一般做最终一致性,采用 Cache Aside 模式:读走缓存,miss 回填;写先更新 DB 再删缓存。极端场景下用延迟双删或订阅 binlog(Canal)来兜底。强一致场景才考虑加分布式锁,但会牺牲性能,一般业务不这么做。”

Q19. Kafka / RocketMQ 这类消息队列如何实现高性能?

面试时答了零拷贝、Epoll、分区、批处理、压缩,其他没答上。系统化整理:

Kafka 高性能的核心手段:

手段 说明
顺序写磁盘 消息追加到 partition 的 log 文件末尾,顺序写比随机写快 1000 倍以上(机械盘尤其明显,SSD 也受益)
零拷贝(sendfile) 消费者拉消息时,数据从 page cache 直接送到网卡,不经过用户态,减少 4 次拷贝到 2 次
Page Cache 依赖 OS 的页缓存做读写加速,不在 JVM 堆内维护消息,避免 GC
分区(Partition) 一个 Topic 拆成多个 Partition 分布到多个 Broker,并行读写,横向扩展吞吐
批量发送 Producer 攒批(batch.size + linger.ms)后一次发送,摊薄网络开销
压缩 Producer 侧压缩(Gzip / Snappy / LZ4 / Zstd),Broker 直接存压缩后的 batch,Consumer 解压。LZ4 / Zstd 是主流(压缩比高 + CPU 开销低)
稀疏索引 每个 segment 配一个 .index 文件,按 offset 稀疏索引,二分查找定位消息
Epoll + Reactor 网络层用 epoll(Linux)多路复用,少量线程处理大量连接
副本机制 Leader/Follower 副本,写入走 Leader,Follower 拉取同步,保证可靠性不影响吞吐

RocketMQ 的差异:

  • CommitLog 单一存储:所有 Topic 的消息都写到同一个 CommitLog,然后异步分发到 ConsumeQueue(索引)。好处是磁盘写入完全顺序化,坏处是读放大(需要按索引跳到 CommitLog 读)
  • 消息过滤下推:支持 Tag / SQL92 过滤,Broker 侧过滤后返回,减少网络传输
  • 事务消息 / 顺序消息 / 定时消息:内置支持,比 Kafka 更贴近业务

零拷贝的四种实现:

1
2
3
4
5
6
7
8
9
10
11
传统 read + write(4 次拷贝,2 次系统调用):
磁盘 → 内核缓冲 → 用户缓冲 → Socket 缓冲 → 网卡

mmap + write(3 次拷贝):
磁盘 → 内核缓冲(用户空间映射)→ Socket 缓冲 → 网卡

sendfile(2 次拷贝,Kafka 用):
磁盘 → 内核缓冲 → 网卡(DMA gather)

sendfile + DMA gather(真正的零 CPU 拷贝):
磁盘 → 内核缓冲 → 网卡(数据完全不经过 CPU)

Kafka 的架构关键点:

1
2
3
4
5
6
7
Producer → Broker Cluster (多 Partition 多副本) → Consumer Group
│
├── Topic-A-Partition-0 (Leader@Broker1, Follower@Broker2,3)
├── Topic-A-Partition-1 (Leader@Broker2, Follower@Broker1,3)
└── Topic-A-Partition-2 (Leader@Broker3, Follower@Broker1,2)
│
└── ZooKeeper / KRaft(元数据管理)
  • Producer 按 key 分区(默认 hash),保证同 key 消息顺序
  • Consumer Group 内 partition 均分,一个 partition 只能被 group 内一个 consumer 消费(决定了并行度上限)
  • Rebalance 是常见性能坑:Consumer 上下线触发全组重分配,短暂停顿

Q20. 用 Redis / Kafka / MySQL / Nacos 等组件设计一个高可用系统,各起什么作用?

这是开放性架构题,考察对常见组件的整体把握。按”入口 → 服务 → 数据 → 观测”四层展开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
                            ┌─────────────────┐
│ DNS / GSLB │ ← 全局流量调度、多机房容灾
└────────┬────────┘
│
┌────────▼────────┐
│ CDN / WAF │ ← 静态加速 + 安全防护
└────────┬────────┘
│
┌────────▼────────┐
│ LB (SLB/LVS) │ ← 入口负载均衡
└────────┬────────┘
│
┌────────────────────────────┼────────────────────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ K8s集群│ │ K8s集群│ │ K8s集群│ ← 多集群/多可用区
│ Zone-A│ │ Zone-B│ │ Zone-C│
└────┬───┘ └────┬───┘ └────┬───┘
│ Ingress → Service → Pod │
└────────────────────────────┼────────────────────────────┘
│
┌─────────────────────────────────┼─────────────────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Nacos │ │ Redis │ │ Kafka │
│注册/配置 │ │ 缓存 │ │ 消息队列 │
└─────────┘ └─────────┘ └─────────┘
│ │
▼ ▼
┌───────────┐ ┌────────────┐
│ MySQL │ │ OSS/NAS │
│ 主从/分片 │ │ 对象存储 │
└───────────┘ └────────────┘
│
▼
┌────────────────────────────────┐
│ Prometheus + Grafana + Loki │ ← 可观测
│ SkyWalking / Jaeger (Trace) │
└────────────────────────────────┘

各组件作用详解:

层次 组件 作用
入口层 DNS / GSLB 全局调度、跨机房切换
CDN 静态资源加速、边缘缓存
WAF 防 SQL 注入、XSS、CC 攻击
LB(LVS / SLB) 4/7 层负载均衡,收敛连接
应用层 K8s 容器编排、自愈、滚动更新、弹性伸缩
Ingress 集群入口路由(域名/路径)
Service / Pod 服务发现与流量分发
HPA 按指标自动扩缩容
中间件层 Nacos 服务注册发现 + 配置中心
Sentinel 限流熔断降级
Redis 缓存 + 分布式锁 + 计数
Kafka / RocketMQ 异步解耦 + 削峰填谷 + 日志聚合
数据层 MySQL 关系型数据主存
MongoDB 半结构化 / 灵活 Schema 数据
OSS / NAS 对象存储(图片、视频、备份)
Elasticsearch 全文检索 / 日志分析
观测层 Prometheus + Grafana 指标监控与可视化
Loki / EFK 日志聚合与查询
SkyWalking / Jaeger 分布式链路追踪
Alertmanager 告警路由与通知

高可用设计原则:

  1. 无单点:每一层都做冗余(多副本、多可用区、多集群)
  2. 无状态:应用无状态化,Session 放 Redis,才能自由扩缩容
  3. 故障隔离:舱壁模式(线程池隔离)、限流熔断、按业务分集群
  4. 降级预案:非核心功能可关闭(大促时的推荐、评价)
  5. 异步解耦:非核心链路走 MQ,避免同步阻塞
  6. 可观测:Metrics / Logging / Tracing 三件套齐全,故障可定位可复盘
  7. 演练验证:定期做故障演练(Chaos Engineering)、多活切换演练

回答技巧:面试官问这题不是要听你背组件清单,而是看你有没有系统性思维——能不能按流量走向把组件串起来,能不能讲清每个组件解决什么问题、不用会怎样。

四、大模型 SRE 与 AI Agent

Q21. 大模型服务常见的限流手段有哪些?如何应对客户突发流量?

这题是模型 SRE 岗位的核心考点。分限流维度和突发流量处置两部分讲。

限流维度(对客户可见的配额):

维度 说明 典型场景
TPM(Tokens Per Minute) 每分钟消耗的 token 总量 长文本应用的主要约束
RPM(Requests Per Minute) 每分钟请求数 短文本、高频调用场景
并发数(Concurrency) 同时在处理的请求数 保护后端推理引擎不被打爆
日/月配额 总量控制 商务合约约束
模型级配额 每个模型独立配额 稀缺资源(如 GPT-4 级别)单独管

保护性限流(对客户不可见,系统内部):

维度 说明
Burst 增速 单位时间内的增长速率(如每秒新增请求数),防止瞬时突刺
步长/爬坡 从 0 到峰值不允许一步到位,必须逐步爬升
排队时长 请求在队列中等待超过 X 秒直接拒绝
单连接 QPS 防止单客户端建大量连接绕过限制

这些是网关的自我保护,客户看不见但真实存在。任何一家大模型厂商(火山、百炼、OpenAI)都有类似机制。

客户突发流量的处置策略(按优先级):

① 首选:让客户做流量整形(Client-Side)

  • 平滑上量:不要瞬间从 100 QPS 拉到 10000 QPS,按分钟级逐步爬坡
  • 客户端限流:应用侧用令牌桶 / 漏桶自控
  • 网关侧:客户自建 API 网关做前置限流
  • 重试策略:指数退避 + 抖动,不要死循环重试放大流量

② 推荐升级产品形态

对于流量稳定且量大的客户,推公有 API 不如推:

产品形态 特点
PTU / TPM 预留 预留固定算力,独享,无 burst 限流
MU(Model Unit) 按模型单元计费,容量保障
独立部署 / 专属实例 完全隔离,客户自主控制

这类产品没有 burst 限流(因为容量本来就是独享的),是稳定大流量客户的首选。

③ 内部调整限流阈值(危险操作)

如果客户拒绝流量整形、也不用预留产品,前线又非常强硬,最后手段是调整该客户的 burst 限流配置:

  • 后台可以调增长量、步长、增长比例
  • 风险极大:曾经出现过调整后其他大客户突增流量把整个集群打爆的情况
  • 现在的做法:只调额度(TPM/RPM),不调 burst 配置,因为额度影响面可控,burst 影响面是整个池子

④ 兜底:切流 + 扩容

如果已经打挂了:

  • 按长度分桶把流量调度到其他部署
  • 扩容 GPU 实例(HPA + 手动补副本)
  • 极端情况下对该客户完全限流

回答技巧:这题面试官想听的不是”有哪些限流算法”,而是你对客户工况的理解——什么时候该推产品升级、什么时候该让客户改代码、什么时候自己扛。体现的是”技术 + 商务”的综合判断。

Q22. 你处理过最复杂/最难的故障是怎样的?整体流程是什么?

这是开放题 + 项目深挖,考察的是流程规范性和你在其中的角色。以云上重大故障为例讲流程:

故障生命周期:

1
2
1. 告警触发  →  2. 应急拉起  →  3. 定级  →  4. 止血/恢复  →  5. 复盘  →  6. 改进跟踪
(F0 预警) (调度员) (P1-P4) (5-30 分钟) (故障报告) (Action Item)

① 告警与拉起

  • F0 预警:所有告警最初都是 F0,由应急中心调度员(GOC)统一接手
  • 调度员根据告警内容拉起相关人员:
    • SRE:定位、止血
    • 产研(RD):根因分析
    • 应急接口人:跨团队协调
    • PR / 法务(对客故障):对外沟通口径
    • 销售 / 服务:客户侧安抚

② 故障定级

按事先定义的故障矩阵评估:

维度 举例
影响产品数 单个产品 / 多个产品 / 全站
API 可用率 < 99% → P4,< 80% → P3,< 20% → P2,接近 0 → P1
持续时长 5 分钟内 / 30 分钟内 / 超过 30 分钟
影响客户数 单个客户 / 头部客户 / 大量客户
业务损失 是否有资金损失、数据丢失

③ 止血与恢复(SRE 的三把斧)

以大模型故障为例:

手段 说明
切流 按长度分桶调度:某个模型的长文本请求突增打挂集群,把这部分流量切到其他部署
扩容 HPA 自动扩 + 手动补副本,把实例数拉上去
限流 保护性限流兜底,把异常客户限住,防止扩散

传统服务的三板斧是:切流、扩容、限流,本质一样。

④ 时间要求(内部标准)

  • 5 分钟定位:告警发生后 5 分钟内明确”是什么问题”
  • 10 分钟定界:明确”影响范围有多大、涉及哪些组件”
  • 30 分钟恢复:完成止血,业务恢复

⑤ 各角色分工

角色 职责
调度员(GOC) 拉群、通报、协调、时间线记录
SRE 监控看板、切流扩容限流、执行 SOP
产研 分析请求特征、定位根因、修复代码
对客团队 客户侧沟通、逃逸方案、补偿方案

⑥ 复盘与改进

  • 恢复后输出故障报告:时间线、根因、影响面、处理过程、改进项
  • 对客故障:还要出对客报告(客户侧可优化点,比如客户自己没做流量整形、没做熔断降级)
  • Action Item 跟踪闭环:每条改进项要有责任人、截止日期、验收标准

回答技巧:面试官问这题不是要听你讲一个具体故障多牛,而是看你有没有规范化处理故障的能力——能不能讲清角色分工、时间要求、SOP 步骤。有实战经验的人会自然带出”5 分钟定位、10 分钟定界、30 分钟恢复”这类内部标准,以及”切流/扩容/限流”三板斧这种行业共识。

Q23. AI 运维诊断 Agent 是怎么做的?踩过什么坑?

这题是差异化亮点,重点讲数据来源、Agent 架构、部署选型三块。

核心认知:AI 运维诊断的关键是”数据”,不是”模型”

如果人工都要排查很久(数据分散、看板不齐全),凭什么指望 AI 在没有数据的情况下帮你诊断?先把可观测做好,再谈 AI Agent。

前置条件:可观测三大支柱

支柱 数据 工具
Metrics 时序指标(QPS、延迟、错误率、资源使用) Prometheus / VictoriaMetrics
Logging 结构化日志 Loki / Elasticsearch / SLS
Tracing 分布式调用链 Jaeger / SkyWalking / OpenTelemetry

三者通过 TraceID 串联:告警带 TraceID → 反查日志 → 定位调用链 → 关联指标。

Agent 工作流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
告警触发(含 TraceID / 服务名 / 时间窗)
│
▼
Agent 接收
│
├──▶ 拉取该 TraceID 的完整调用链(Tracing)
├──▶ 拉取相关服务的最近日志(Logging)
├──▶ 拉取相关服务的指标(Metrics:CPU/内存/RT/错误率)
├──▶ 查询上下游依赖关系(服务拓扑)
│
▼
LLM 推理 + 归因分析
│
▼
输出:可能的根因 + 建议处置方案 + 相关证据

技术实现路径:

方案 A:Claude Code SDK / Cursor Agent 模式

  • 优势:现成的 ReAct 循环、工具调用协议、上下文管理
  • 劣势:沙箱部署难,生产网 / 客户侧不方便直接用;数据出域风险

方案 B:AgentScope / LangGraph / LangChain 自建

  • 优势:可控性强,能定制 ReAct 逻辑、工具集、记忆
  • 劣势:MCP 工具要自己封装(每个数据源都要写适配器);部署链路长

方案 C:MCP(Model Context Protocol)+ 现成 Agent

  • 优势:工具生态标准化,写一次 MCP Server 可以被任何 MCP Client 使用
  • 落地方式:把 Prometheus、Loki、Jaeger、K8s API 都封装成 MCP Server,Agent 通过 MCP 协议调用

生产落地的坑:

坑 说明
数据源集成难 每个监控系统都有自己的查询语法,工具封装成本高
模型选型 生产网数据不能出域 → 用私有化模型(Qwen、Llama 自部署);开发环境可以用云上模型
权限与审计 Agent 的操作要能审计,特别是执行类工具(重启 Pod、扩容)
幻觉与误导 LLM 可能编造根因,必须让 Agent 强制引用证据(哪个指标、哪条日志)
成本 一次诊断可能消耗几万 token,高频告警下成本失控
收敛时间 Agent 多轮 ReAct 可能耗时几分钟,紧急故障场景等不起

关键设计:AI Agent 只做辅助决策,不做自动执行。

  • 只读工具(查指标、查日志):Agent 自由调用
  • 写工具(重启、扩容、切流):Agent 输出建议 → 人工确认 → 另一个执行系统去做

AI 与执行系统之间必须做隔离:Agent 只能从预定义的 SOP 库中选择步骤,SOP 由另一个程序按顺序执行,Agent 没办法直接调用底层 API。

Q24. LLM 有哪些场景不适合使用?

这题面试官问得非常好——**”更重要的是它什么时候不能用,你用错了代价是很惨重的”**。

核心原因:LLM 输出本质不稳定

即使 temperature=0、top_p=1,同一个 prompt 在不同时间、不同模型上输出也可能不同。这是推理机制决定的(自回归采样 + 浮点精度 + KV Cache 实现差异),不是工程问题。

不适合的场景:

场景 为什么不适合 替代方案
强结构化输出且下游强依赖 JSON Schema 约束下模型仍可能输出不合法 JSON,下游解析崩溃 用约束解码(xgrammar / outlines)+ 校验兜底;或用传统规则引擎
确定性计算(数学、逻辑) LLM 不是计算器,会算错 调用 Python / SQL / 计算器工具
精确数据查询 幻觉问题,会编造不存在的记录 RAG + 强引用 + 数据库查询
高危运维操作(故障注入、删库、切流) 输出不确定 → 结果不可预期 → 背锅 只能做辅助,人工必须确认;用预定义 SOP 而不是让 AI 自由发挥
核心业务代码全权生成 出了故障责任归属不清 AI 辅助编写,人工 Code Review;关键路径必须人审
实时性要求极高 推理延迟几百 ms 到几秒 传统规则引擎、缓存
成本敏感的高频调用 每次几万 token,成本失控 用小模型 / 蒸馏模型 / 传统方法
法律 / 医疗 / 金融决策 责任问题 + 幻觉风险 只能做辅助分析,最终决策必须人工

JSON Schema 约束输出的坑(真实故障案例):

  • 客户用 JSON Schema 约束模型输出,下游业务强依赖字段
  • 模型偶尔输出不合法 JSON(多字段、少字段、类型错误)
  • 下游解析失败 → 业务异常
  • 更糟的情况:JSON Schema 过于复杂导致推理引擎本身出问题(编译慢、内存暴涨、GPU 卡死)

应对方式:

  1. 约束解码(Constrained Decoding):xgrammar、outlines、guidance 等库在解码阶段强制符合语法
  2. 校验兜底:输出后用 JSON Schema 校验,失败重试或降级
  3. 简化 Schema:避免深度嵌套、复杂正则、动态模式
  4. 分桶缓存:相同 Schema 复用编译结果,避免重复编译

面试话术:

“LLM 的核心特点是输出不确定,所以任何对确定性有强要求的场景都要慎用。比如让模型输出严格 JSON 且下游强依赖,即使加了 Schema 约束也可能出问题;再比如让 AI 自动做故障注入这种高危操作,出了事谁背锅?我们的原则是:AI 做辅助分析和推荐,人工做最终决策和执行。核心代码可以让 AI 写 80%,但 Code Review 必须回归到人。”

加分回答:主动提”约束解码“和”JSON Schema 编译缓存“这类工程细节,能体现出你不只是知道”LLM 不稳定”,还知道具体怎么在工程上兜底。

反问

面试最后的反问环节,两场都问了对方团队的一些情况,作为参考:

基础面(0914):

# 问题 面试官答复要点
1 目前应急流程和基建情况? 各组件监控告警 + 定期演练 + SOP 沉淀成 skills
2 AI 在运维中的应用程度? 知识库召回、故障定位(多数据源分析)、SOP 挑选与执行
3 AI 自动化操作怎么做安全隔离? Agent 只能选 SOP,SOP 由独立程序按步骤执行,Agent 无法直接操作
4 有没有遇到过 AI 相关的重大故障? 有:AI 反复重写同一个 bug、误删分支历史、参数达到上限
5 团队用什么云? 内部为主,公有云用集团子公司,流程偏传统,AI 提单未成体系

模型 SRE 面:

# 问题 面试官答复要点
1 团队的角色定位?是 SRE 吗? 是模型 SRE
2 内部运维平台能做哪些操作? 切流、扩容、新建部署,不涉及底层修改
3 团队规模? 模型 SRE 团队规模不小
4 遇到过最大的问题是什么? 资源问题——卡不够、卡型不匹配、突增流量打爆集群
5 火山模型”从不出问题”是真的吗? 不好说,但稳定性工作大家都做了很多;限流是共同手段
6 长时间的重大故障? 影响时间超过 30 分钟的比较少

反问的价值:反问不是走过场,是双向评估——你能通过反问判断这个团队的成熟度(有没有 SOP、有没有演练、有没有 AI 落地)、技术栈(自建 vs 云上)、风险点(手动切流 vs 自动切流)。这些信息反过来决定了你入职后的工作体验和成长空间。

AI总结

两场面试下来最大的感受是:日常做的时候都能做,但让自己系统讲一遍就磕巴。

具体表现在:

  • 概念混淆:把 Namespace 的”隔离”讲成 CGroup 的能力;把 Pending 的排查方向说成探针问题(探针影响的是 Ready)
  • 细节遗忘:三次握手说不出 SYN/ACK 标志位,拥塞控制说不出 CUBIC/BBR,归并排序讲不清合并过程
  • 表达松散:Cache Aside 讲成”先删缓存再写 DB”,被面试官纠正后才反应过来

这些东西不是不会,是没有系统整理过。日常运维更多是”遇到问题 → 搜 → 解决”的即时模式,缺少把知识点串成体系的输出。

后续准备方向:

  1. 基础八股回炉:TCP/IP、操作系统、数据结构与算法这些”面试必考但日常少用”的东西要定期复习
  2. K8s 深入原理:不能只停留在”用过”,要理解 QoS、驱逐、调度器打分、CGroup v2 的实现细节
  3. 系统化输出:每个知识点写一篇博客,用输出倒逼输入
  4. AI + 运维的落地经验:这是差异化优势,要把项目细节、踩坑、选型权衡讲透

基础不牢,地动山摇。持续学习。

CATALOG
  1. 1. 前言
  2. 2. 复盘
    1. 2.1. 一、网络与算法基础
      1. 2.1.1. Q1. TCP 三次握手的过程是怎样的?为什么不能是两次或四次?
      2. 2.1.2. Q2. TCP 拥塞控制有哪些算法?大致原理是什么?
      3. 2.1.3. Q3. 简单说下快排和归并排序的思路
      4. 2.1.4. Q4. 你写代码吗?Python 熟不熟?
    2. 2.2. 二、容器与 K8s
      1. 2.2.1. Q5. 容器和虚拟机(VMware)有什么区别?
      2. 2.2.2. Q6. CGroup 和 Namespace 分别起什么作用?
      3. 2.2.3. Q7. K8s 中 Deployment、Pod、Service 三者是怎么关联的?
      4. 2.2.4. Q8. 讲一下 K8s 的 QoS 等级?驱逐顺序是怎样的?
      5. 2.2.5. Q9. 如果节点上只剩 Guaranteed Pod 且内存不足,还会被驱逐吗?
      6. 2.2.6. Q10. 什么是节点污点(Taint)?除了标记 NotReady 还有什么用?
      7. 2.2.7. Q11. 如何排查一个处于 Pending 状态的 Pod?
      8. 2.2.8. Q12. K8s 有哪几种探针?分别用来做什么?
      9. 2.2.9. Q13. 探针配置不合理会导致什么问题?
      10. 2.2.10. Q14. HPA 是怎么工作的?大模型场景下有什么变化?
    3. 2.3. 三、存储与中间件
      1. 2.3.1. Q15. MongoDB 和 MySQL 的区别?各自的高可用和扩容方案是什么?
      2. 2.3.2. Q16. Redis 和 InfluxDB 分别是什么?各自的应用场景?
      3. 2.3.3. Q17. 缓存穿透、击穿、雪崩是什么?怎么规避?
      4. 2.3.4. Q18. Redis 缓存和数据库如何保证一致性?
      5. 2.3.5. Q19. Kafka / RocketMQ 这类消息队列如何实现高性能?
      6. 2.3.6. Q20. 用 Redis / Kafka / MySQL / Nacos 等组件设计一个高可用系统,各起什么作用?
    4. 2.4. 四、大模型 SRE 与 AI Agent
      1. 2.4.1. Q21. 大模型服务常见的限流手段有哪些?如何应对客户突发流量?
      2. 2.4.2. Q22. 你处理过最复杂/最难的故障是怎样的?整体流程是什么?
      3. 2.4.3. Q23. AI 运维诊断 Agent 是怎么做的?踩过什么坑?
      4. 2.4.4. Q24. LLM 有哪些场景不适合使用?
  3. 3. 反问
  4. 4. AI总结