面灵AI→

南京某小厂 Java 后端实习一面:Lua 秒杀、本地消息表与 RAG 链路

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

《面试题目》

  1. 你这个社区生活服务平台是个人项目吗?
  2. 先不用讲业务功能,直接说这个后端项目分了哪些模块?
  3. 简历里写用 Lua 脚本实现秒杀,为什么不直接用 MySQL 扣库存?
  4. 如果库存只有 1,两个用户同时请求你的 Lua 脚本,怎么保证只有一个人成功?
  5. 简历写了「条件更新扣减库存」,这个条件更新的 SQL 怎么写?
  6. 订单变更同事务写入本地消息表,为什么要多建一张消息表?
  7. 项目里的 Spring AI 加 RAG,用户提问之后,从收到问题到生成回答,完整经过哪些步骤?
  8. 压测把平均响应时间降下来了,具体改了哪部分代码?为什么能提升?

《参考解析》

为什么用 Lua 而不是 MySQL 扣库存:秒杀的特点是瞬时高并发集中在一个商品上,用 MySQL 扣库存会让所有请求在同一行上排队拿行锁,连接池很快被打满、响应时间雪崩。把扣减放进 Redis,靠单线程执行保证原子性,Lua 脚本把「读库存、判断、扣减」合成一次执行,数据库只承接扣减成功的少量请求。代价是要处理 Redis 与数据库的最终一致(预扣加异步落库、失败回滚)。

库存只剩 1 时怎么保证只成一单:Lua 脚本在 Redis 里原子执行,脚本内先判断库存大于 0 再减,把结果返回给客户端,只有拿到成功返回的请求算抢到。关键是不能在脚本外用 GET 再 DECR。多实例部署时扣减结果要与订单号绑定。

条件更新 SQL:update stock set count = count - 1 where id = ? and count > 0,再看影响行数是否为 1,为 0 就说明已经扣完。这一句把判断和更新合成一个原子操作,靠行锁加 where 条件,比先查再改安全。

本地消息表为什么单独建一张:业务数据和消息在同一个本地事务里写,事务提交就代表消息一定落库,再由定时任务异步投递并重试,从而避免「业务成功、消息丢了」和「消息发了、业务回滚了」两种不一致。单独建表是为了不动业务表结构、能独立重试和补偿,也方便查积压和人工介入。

RAG 链路与压测优化:问题进来先做改写和意图判断,走向量与关键词两路召回,融合后重排取 top-k,拼进 prompt 连同历史对话一起交给模型,流式返回并附引用;工程上要处理切块策略、embedding 模型版本、结果缓存和超时降级。压测优化则要说清瓶颈原来在哪(慢查询、锁竞争、序列化、连接池大小)、改了哪段代码、指标从多少降到多少,只给「优化了接口」这种结论会立刻被追问到答不上来。