美团 AI应用开发 一面

这个是4.30的面经 我一直没来得及整理

1. 自我介绍,包括个人背景、经历,以及 AI 大模型如何融入学习和实践

2. 使用 AI Coding 工具时遇到过什么问题,怎么解决

答案:

AI Coding 最大的问题不是不会写代码,而是它容易写出“看起来能跑但不符合项目约束”的代码。比如它会新增不必要的抽象层、改动无关文件、吞异常、遗漏权限校验、把敏感字段打进日志,或者生成一个和团队代码风格完全不同的实现。

我的做法是先给它明确上下文和边界,不让它自由发挥。比如只允许修改指定文件,不允许改接口签名,不允许新增依赖,不允许修改数据库 schema。生成代码后,我会先看 diff,再跑测试,再重点 review 异常、并发、权限、事务和日志脱敏。

AI Coding 约束:
1. 只允许修改 order_reply_service.go 和对应测试文件
2. 不允许修改 Controller 入参和返回结构
3. 不允许新增第三方依赖
4. 所有错误必须返回业务错误码
5. 日志中不得出现手机号、邮箱、token、完整地址
6. 必须补充失败路径和边界条件测试

如果 AI 生成的代码过于啰嗦,我会让它先输出设计,再要求它按现有项目模式重写,而不是接受它自己创造的一套架构。

3. 简要说明 OSI 七层模型每一层的作用

答案:

OSI 七层模型从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。实际开发中不一定每天都直接使用七层模型,但排查网络问题时非常有用。

物理层负责比特流传输,比如网线、光纤、无线信号;数据链路层负责同一链路内的数据帧传输,比如 MAC 地址、交换机;网络层负责跨网络寻址和路由,比如 IP、路由器;传输层负责端到端传输,比如 TCP、UDP;会话层负责会话建立和管理;表示层负责数据格式、加密、压缩;应用层就是 HTTP、DNS、SMTP、WebSocket 这些面向应用的协议。

后端排查接口超时时,可能涉及多个层次。比如 DNS 解析慢属于应用层附近,TCP 握手失败属于传输层,路由不可达属于网络层,TLS 握手失败又涉及表示层和应用层。

应用层:HTTP / DNS / WebSocket
表示层:TLS / 编码 / 压缩
会话层:会话保持
传输层:TCP / UDP
网络层:IP / 路由
数据链路层:MAC / 交换机
物理层:网线 / 光纤 / 无线

4. 数组和链表的区别,各自优缺点是什么

答案:

数组是一段连续内存,支持通过下标随机访问,时间复杂度是 O(1)。缺点是插入和删除时可能需要移动大量元素,尤其是在中间位置操作,复杂度是 O(n)。链表不要求连续内存,插入和删除节点只需要修改指针,适合频繁插删,但查找某个位置需要从头遍历,复杂度是 O(n)

在工程里,如果数据规模固定、读多写少、需要频繁随机访问,一般选数组或切片。如果数据需要频繁在中间插入删除,可以考虑链表。不过实际业务中链表用得没有数组多,因为链表缓存局部性差,CPU cache 不友好。

// 数组随机访问快
int[] arr = {1, 2, 3, 4};
int x = arr[2];

// 链表插入删除灵活,但查找慢
class Node {
    int val;
    Node next;
}

5. 动态变化的数据集合,如果频繁插入删除并且还要遍历访问,会选数组还是链表

答案:

不能只看“频繁插入删除”就选链表,还要看插入删除发生在哪里。如果是在尾部追加,数组或动态数组更合适,因为扩容是摊销成本,遍历也更快。如果是在中间频繁插入删除,并且已经能快速拿到节点位置,链表才更有优势。

如果既要频繁插入删除,又要大量遍历,很多时候会选择数组加延迟删除、跳表、红黑树、哈希表加链表这样的组合结构。比如 LRU 缓存就用 HashMap 加双向链表,HashMap 解决快速定位,双向链表解决顺序维护。

class LruNode {
    String key;
    String value;
    LruNode prev;
    LruNode next;
}

Map<String, LruNode> index = new HashMap<>();

如果只是普通业务列表,数据量不大,优先考虑可读性和维护成本,不会为了理论复杂度强行使用链表。

6. 解释一下垃圾回收机制,GC 在 Java 中的作用是什么

答案:

Java GC 的作用是自动回收不再被引用的对象,避免程序员手动释放内存。它主要管理堆内存,对象创建后通常分配在堆上,当对象从 GC Roots 不可达时,就可以被回收。GC Roots 包括线程栈里的局部变量、静态变量、JNI 引用、同步锁持有对象等。

Java GC 的基本过程可以理解为:先判断对象是否存活,再回收不可达对象,必要时进行内存整理。现代 JVM 通常采用分代思想,把堆分为新生代和老年代。大多数对象朝生夕死,所以新生代 GC 会更频繁;存活时间较长的对象会晋升到老年代。

public class GCDemo {
    public static void main(String[] args) {
        byte[] data = new byte[1024 * 1024];
        data = null;
        System.gc();
    }
}

System.gc() 只是建议 JVM 执行 GC,不保证立即执行。线上一般不建议业务代码主动调用它。

7. Java 垃圾回收算法有哪些,分别适合什么场景

答案:

常见算法有标记-清除、复制、标记-整理和分代收集。标记-清除先标记存活对象,再清除不可达对象,缺点是会产生内存碎片。复制算法把内存分成两块,每次只使用一块,回收时把存活对象复制到另一块,适合对象存活率低的新生代。标记-整理会把存活对象向一端移动,适合老年代,避免碎片问题。分代收集则是根据对象生命周期组合使用不同算法。

新生代通常用复制算法,因为大多数对象很快死亡,复制成本低。老年代对象存活率高,如果也用复制算法,成本太高,所以更多使用标记-清除或标记-整理。G1、ZGC、Shenandoah 这些收集器又在此基础上做了并发、分区和低停顿优化。

新生代:对象死亡率高,适合复制算法
老年代:对象存活率高,适合标记-整理或分区回收
低延迟服务:更关注 STW 时间
批处理任务:更关注吞吐量

8. 频繁 Full GC 如何排查和优化

答案:

频繁 Full GC 先要确认原因,是老年代空间不足、元空间不足、大对象分配、内存泄漏、晋升失败,还是代码里主动调用了 System.gc()。排查时会看 GC 日志、堆内存曲线、对象分布、线程栈和 dump 文件。

常用命令包括 jstat 看 GC 频率,jmap 导出堆,jcmd 查看 JVM 信息,MAT 分析对象引用链。如果发现某类对象数量持续增长且无法回收,就要看是否被静态集合、本地缓存、线程变量、连接池或队列引用。

jstat -gcutil <pid> 1000
jcmd <pid> GC.heap_info
jmap -dump:format=b,file=heap.hprof <pid>

优化方式要看原因。如果是缓存无限增长,要加容量和过期;如果是大对象频繁创建,要复用或分块;如果是堆太小,要调整 JVM 参数;如果是老年代对象晋升太快,要减少对象生命周期或调整新生代比例。

-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError

9. SQL 慢查询怎么定位和优化

答案:

慢查询不能只看 SQL 本身,要结合执行计划、数据量、索引、返回行数、锁等待和业务调用频率。第一步一般是开启慢查询日志,找出高耗时、高频率、高扫描行数的 SQL。第二步用 EXPLAIN 看执行计划,重点看 typekeyrowsfilteredExtra

如果 type=ALL,说明全表扫描;如果 ExtraUsing filesortUsing temporary,说明排序或分组可能有问题;如果 rows 很大但返回很少,说明过滤条件没有很好利用索引。优化方式包括增加合适的联合索引、改写 SQL、减少回表、避免函数包裹索引列、用汇总表替代实时聚合。

EXPLAIN
SELECT order_id, user_id, amount
FROM order_detail
WHERE user_city = 'Shanghai'
  AND create_time >= '2026-04-01'
ORDER BY amount DESC
LIMIT 50;

优化不是简单加索引,加错索引会增加写入成本,也可能被优化器放弃。

10. EXPLAIN 中 type=ALL 是什么含义,出现后一定要优化吗

答案:

type=ALL 表示全表扫描,是访问类型里比较差的一种。但出现 ALL 不一定绝对有问题。如果表很小,几十行或几百行,全表扫描可能比走索引更快。如果查询本身要返回表里大部分数据,走索引再回表也未必划算。

需要优化的是大表、高频、扫描大量无效数据的 ALL。比如千万级订单表只查某个城市最近一天的数据,却全表扫描,这就必须优化。可以通过联合索引、分区、汇总表、条件改写来解决。

CREATE INDEX idx_city_time_amount
ON order_detail(user_city, create_time, amount);

如果查询只需要索引里的字段,可以进一步设计覆盖索引,减少回表:

SELECT user_city, create_time, amount
FROM order_detail
WHERE user_city = ?
  AND create_time >= ?
ORDER BY amount DESC
LIMIT 50;

11. 联合索引字段顺序如何确定,为什么不是随便排列

答案:

联合索引字段顺序要结合查询条件、字段选择性、排序、分组和范围查询来确定。一般等值条件放前面,范围条件放后面,排序字段尽量放在能利用索引顺序的位置。字段顺序不同,索引效果可能完全不一样。

比如查询条件是 user_city = ? AND create_time >= ? ORDER BY amount DESC,如果大多数查询都按城市过滤,再按时间范围查,就可以考虑 (user_city, create_time, amount)。但如果业务更多是查某个时间范围内所有城市的订单,那 create_time 可能应该放更前。

CREATE INDEX idx_city_time_amount
ON order_detail(user_city, create_time, amount);

不能只看字段名,要看真实 SQL 模式。如果 user_city 区分度很低,比如只有几个城市,而 create_time 能过滤掉大量数据,就要结合执行计划和统计信息判断。

12. 设计一个缓存系统提升数据库查询效率,要考虑哪些内容

答案:

缓存系统要先确定缓存什么数据、缓存粒度、过期时间、更新策略和一致性要求。不是所有数据都适合缓存,适合缓存的一般是读多写少、计算成本高、允许短暂不一致的数据。强一致的资金、库存、状态流转不能随便用普通缓存兜底。

常见设计是先查本地缓存,再查 Redis,最后查数据库。Redis 没命中时回源数据库并写缓存。为了防止缓存穿透,可以缓存空值或使用布隆过滤器;为了防止缓存击穿,可以加互斥锁;为了防止缓存雪崩,可以给过期时间加随机抖动。

public OrderSummary getOrderSummary(String orderId) {
    String key = "order:summary:" + orderId;

    OrderSummary cached = redis.get(key, OrderSummary.class);
    if (cached != null) {
        return cached;
    }

    OrderSummary db = orderMapper.selectSummary(orderId);
    if (db == null) {
        redis.set(key, OrderSummary.empty(), Duration.ofSeconds(30));
        return null;
    }

    redis.set(key, db, Duration.ofMinutes(10).plusSeconds(ThreadLocalRandom.current().nextInt(60)));
    return db;
}

13. 更新数据库后删除缓存,在高并发下可能有什么问题,怎么优化

答案:

更新数据库后删除缓存是常见方案,但在高并发下可能出现旧值回写。比如线程 A 更新数据库并删除缓存,线程 B 在 A 提交前读到旧数据库值,然后把旧值写回缓存。这样数据库已经是新值,但缓存里又变成旧值。

可以用延迟双删、版本号、binlog 订阅或消息队列异步删除来缓解。更严谨的做法是缓存数据带版本,写缓存时比较版本,旧版本不能覆盖新版本。对于一致性要求很高的数据,不能只依赖缓存策略,要从业务上避免缓存参与关键判断。

@Transactional
public void updateOrderStatus(String orderId, String status) {
    orderMapper.updateStatus(orderId, status);
    redis.delete("order:summary:" + orderId);

    mq.send("cache.delete.delay", orde

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

AI-Agent面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务