面灵AI→

度小满 AI 全栈一面(约53分钟)

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 请做一个自我介绍。
  2. 做过的项目中,哪个功能最有挑战?遇到的痛点是什么,你是如何解决的?请详细展开。
  3. 你主要做后端和 Agent,项目中是否涉及前端工作?
  4. 从浏览器输入 URL 到页面展示,中间发生了什么?
  5. 为什么 TCP 建立连接需要三次握手,而不是两次?
  6. 502 和 504 通常分别表示什么问题?
  7. 301 和 302 有什么区别?
  8. 进程和线程有什么区别?浏览器为什么采用多进程架构?
  9. 你是否使用过 Go?介绍一下 Go 的 GMP 调度原理。
  10. MySQL 为什么使用 B+ 树索引,而不是哈希表、二叉树或 B 树?
  11. 哪些 SQL 写法会导致索引失效?
  12. 项目中使用过什么缓存?Redis 为什么快?
  13. 缓存雪崩、击穿和穿透分别是什么,应该如何处理?
  14. flex: 1 是哪几个属性的缩写?
  15. 什么是跨域,为什么会有跨域,有哪些解决方案?
  16. 了解哪些前端框架?
  17. 什么是防抖和节流?
  18. 平时如何使用 AI 辅助开发?请举一个印象较深的场景。
  19. AI 给出的方案会直接采纳多少?有没有踩坑经历?
  20. 长期使用后,你认为 AI 不擅长哪些部分?
  21. 如果给一个项目增加 AI 问答功能,前后端分别要注意什么?请从体验、安全和工程角度说明。
  22. LeetCode 93:复原 IP 地址。先说明思路,再编码并运行通过。

《参考解析》

从输入 URL 到页面展示:按「网络 → 解析 → 渲染」三段讲最清晰。网络段:浏览器先查自身缓存与 Service Worker,再做 DNS 解析(浏览器 DNS 缓存 → 系统 hosts → 本地 DNS 服务器,逐级递归到权威服务器),拿到 IP 后与服务器建立 TCP 连接(HTTPS 还要做 TLS 握手,协商版本与密码套件、验证证书、生成会话密钥)。如果是 HTTP/2 或 HTTP/3,连接复用与握手过程会有差异。请求段:组装请求行、请求头(Host、Cookie、Accept、UA 等)发出,服务端可能经过 CDN、网关、负载均衡到应用,应用查缓存和数据库后返回响应;浏览器按 Cache-Control、ETag 决定是否复用。渲染段:HTML 边下载边被 HTML 解析器解析成 DOM,遇到 CSS 阻塞渲染(构建 CSSOM),遇到同步 script 阻塞解析(可用 defer/async 缓解);DOM 与 CSSOM 合成渲染树,接着布局(Layout)算几何位置、绘制(Paint)生成绘制指令、合成(Composite)分层后交给 GPU 上屏。回答时能点出「哪些环节会阻塞首屏」以及可以优化什么(DNS 预解析、预连接、CDN、关键 CSS 内联、懒加载),比背流程更能体现工程理解。

为什么 TCP 握手要三次而不是两次:核心是双方都要确认「自己的发送能力和对方的接收能力都正常」,并且要同步初始序列号。两次握手时,客户端发 SYN、服务端回 SYN+ACK 后服务端就认为连接建立,但服务端无法确认客户端是否收到了自己的 SYN+ACK,也无法确认客户端的序列号范围已被对方接受。如果网络中存在延迟到达的旧 SYN(历史连接请求),服务端会为这个早已失效的请求建立一条连接并分配资源,而客户端根本不认这条连接,于是资源被白白占用;第三次握手(客户端回 ACK)就是对「这条连接确实是我现在要的」的最终确认。换个角度说,第三次握手让服务端在收到客户端确认后才进入 ESTABLISHED,把「浪费的资源」推迟到确认之后。顺带要能说明四次挥手为什么是四次(TCP 全双工,被动方收到 FIN 后可能还有数据要发,ACK 与 FIN 不能合并)。

MySQL 为什么选 B+ 树,以及索引失效的写法:排除法的逻辑最清楚。哈希索引等值查询 O(1),但不支持范围和排序,无法做 order by/between/前缀匹配,且哈希冲突与扩容代价高。二叉树(含二叉搜索树)在自增主键下会退化成链表,树高过大导致磁盘 IO 次数不可控,每个节点只存一个键也无法利用磁盘块的预读。B 树每个节点同时存键和数据,导致单节点能放的键变少、树更高,且范围查询需要中序回溯到不同层级。B+ 树的性质刚好对上磁盘存储:非叶子节点只存键,扇出大(一个 16KB 页能放上千个键),三层左右就能支撑千万级数据;数据全部在叶子节点且用双向链表连接,范围扫描和排序只要顺序遍历叶子;查询路径长度稳定,IO 次数可预估。索引失效的常见写法包括:在索引列上做函数或运算(where date(create_time) = '2026-09-01'、where id + 1 = 5)、隐式类型转换(字符串列传数字、或字符集不一致的 join)、以 % 开头的 like、or 连接的条件中有一侧无索引、联合索引不满足最左前缀(跳过第一列、或中间列用了范围条件后面的列失效)、使用 !=/not in/is not null 导致优化器判定选择性差而放弃索引、以及排序字段与索引顺序不一致产生 Using filesort。要说清一件事:失效往往是优化器基于代价估算的选择,不是语法错误,用 EXPLAIN 看 type、key、rows、filtered 才能确诊。

缓存雪崩、击穿、穿透的处理:三者要区分清楚。雪崩是大量 key 在同一时刻失效或缓存集群整体不可用,处理方式是过期时间加随机抖动、做多级缓存(本地 + Redis)、对缓存层做高可用(集群/哨兵)、并给回源链路加限流与降级,必要时用「逻辑过期 + 异步重建」让旧值继续提供读服务。击穿是单个热点 key 过期瞬间大量并发请求同时回源,处理方式是用互斥锁或 singleflight 保证只有一个请求去重建、其余等待后重试读缓存,或者干脆不给热点 key 设过期时间、靠后台任务更新。穿透是查询根本不存在的数据,请求每次都落到数据库(常见于恶意扫 ID),处理方式是布隆过滤器提前拦截、缓存空值并设置较短过期时间、以及在接入层做参数校验和限流。Redis 快的原因也要能答:纯内存操作、单线程模型避免锁竞争与上下文切换(6.0 起网络 IO 多线程但命令执行仍是单线程)、IO 多路复用(epoll)支撑高并发连接、高效的基础数据结构(跳表、压缩列表、渐进式 rehash 的哈希表)、以及自定义的内存分配器减少碎片。

Go 的 GMP 调度原理:G 是 goroutine(用户态协程,初始栈 2KB,按需增长),M 是操作系统线程(machine),P 是处理器上下文(processor,数量默认等于 GOMAXPROCS,通常为 CPU 核数),P 持有本地可运行队列、内存分配缓存(mcache)等资源。M 必须绑定一个 P 才能执行 G,这就把「并发度」限制在 P 的数量上,避免了线程爆炸。调度流程上,每个 P 有一个容量 256 的本地运行队列,新创建的 G 优先放进本地队列(runnext 槽优先执行),本地队列满了就把一半 G 迁移到全局队列;M 执行 G 时若发生系统调用阻塞,会释放 P 交给其他 M 使用,阻塞结束后该 G 进入全局队列等待重新绑定 P。当 M 的本地队列为空时,会先看全局队列,再去其他 P 的队列「偷」一半 G,这就是 work-stealing,用来做负载均衡。此外还有基于信号的抢占式调度(Go 1.14 起):早期只能在函数调用点协作式让出,长循环会饿死其他 G,现在通过 SIGURG 在安全点抢占。答题时把「P 决定并发度、本地队列 + work stealing、syscall 让出 P、抢占式调度」这四点说清就够扎实了。

LeetCode 93 复原 IP 地址怎么写怎么讲:思路是回溯加剪枝,把字符串切成四段,每段是 0255 的合法十进制数且不能有前导零("0" 本身合法,"01" 不合法)。递归参数用当前下标 start 和已切段数 seg,终止条件是 seg == 4 && start == n 时收集结果;每一层枚举段长 13,越界就停,遇到首位是 '0' 且长度大于 1 直接剪枝,数值大于 255 直接剪枝。剩下的段数还有优化空间:若剩余字符数大于剩余段数的 3 倍(或小于剩余段数)可提前返回。核心递归骨架大概是:

def backtrack(start, seg, path):
    if seg == 4:
        if start == n:
            res.append(".".join(path))
        return
    for end in range(start, min(start + 3, n)):
        if end > start and s[start] == '0':
            break
        part = s[start:end + 1]
        if int(part) > 255:
            break
        path.append(part)
        backtrack(end + 1, seg + 1, path)
        path.pop()

复杂度上界是 O(3⁴ × n)(段长组合远小于此,实际剪枝后很快),空间是递归深度 4 加结果集。讲的时候要主动说清「前导零」「等于 0 的段」这两条边界,并手动跑 "25525511135"、"0000"、"101023" 三个用例。