字节跳动 抖音电商 Java后端 日常实习面经(三面 + HR)
- 轮次
- 三面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
一面(约 1 小时)
- 自我介绍;你做过的几个项目里,哪个最有挑战?为什么?
- RAG 智能知识库主要解决什么问题?具体应用场景是什么?(是给模拟面试使用,还是独立知识库功能?学习小组具体怎么使用?)
- RAG 知识库和大模型是怎么连接的?完整请求链路是什么?(用户问题从哪里进入?用户是在和 Agent 对话,还是直接和大模型对话?你的系统到底属于 Agent、Workflow 还是 Skill?)
- RAG 的整体检索流程是什么?
- 决策机制具体是怎么设计的?
- 简历上的优化是怎么做的?
- 优化的 P50 / P95 数据是怎么测出来的?
- 平时使用过哪些大模型?最近主要使用哪个模型?学校对使用这些 AI 工具是什么态度?
- 优惠券管理系统主要做了什么?核心业务是什么?是不是优惠券秒杀场景?用户抢的券本身是否有价格?
- 优惠券秒杀完整链路是怎么实现的?Redis Lua 如何校验库存和用户领取次数?Redis 如何预扣库存?用户券如何缓存和过期?
- 商家创建 CouponTemplate 之后,C 端用户怎么知道有哪些优惠券可以领取?
- Redis 中优惠券列表和优惠券模板具体怎么组织?
- 优惠券查询出现热点 Key 怎么处理?某个热门商家被大量用户查询怎么办?某张秒杀券一百万用户同时访问怎么办?
- Redis Lua 预扣成功,但是 RocketMQ 消息发送失败怎么办?
- 这个优惠券项目是真实业务项目还是学习项目?有没有实际用户使用?
- 场景编程题:知识库文档多路结果合并。
- 智力题:两根不均匀燃烧、每根烧完需要 60 分钟的绳子,怎么计时 45 分钟?
- 反问:团队具体负责什么业务?
二面(leader,约 1 小时)
- 自我介绍。
- 项目是如何规划的?为什么要做这些项目?想要学习什么?
- 讲一个以前完全不了解的技术点,如何从零开始学习之后掌握的?
- 除了 RAG,大模型 / Agent 相关的技术有学习过哪些?agent 具体学习了什么?
- skill 发现机制是如何实现的?
- 自己学习过程中写过什么 skill 吗?code 使用过程中是否沉淀过一些 skill、是否用过其他人的 skill?
- skill 不适合干什么?skill 出现了之后为什么还是需要 MCP?skill 也能够调用脚本,skill 和 MCP、tool 的根本区别是什么?
- tool 和 skill 你在实际开发过程中使用过吗?有写过相关 demo 吗?
- Agent 三种范式你有自己写过相关 demo 吗?
- AI 相关知识更新很快,你觉得当前哪些做法能较好应对这种快速更新?
- harness 概念你知道吗?
- codex 过程中你认为你使用的是哪种 engineering 模式?测试用例执行过程中是否遇到过问题?
- Spec Coding 是否了解?
- 优惠券管理系统是个 demo 吗?你说为了学习高并发相关内容,具体是什么链路呢?
- 秒杀一般会对系统带来什么问题?
- 热点 key 问题如何解决?
- Java 中反射概念知道吗?
- IoC 是什么?解释下。IoC 有什么好处?我不用 IoC 不也能复用单例吗?IoC 本身和 Spring 是强绑定的吗?
- 目前实习计划?
- 算法题:LC 打家劫舍。
- 多线程代码阅读(银行扣减防止超卖):哪些代码导致了这个扣减的问题?怎么改?synchronized 加在哪里?除了 synchronized 还有什么实现方式吗?
- 假设代码中可以使用 MySQL、Redis,你会怎么实现?可以只用 MySQL 实现吗?
- 反问技术栈、面试轮数。
三面(约 33 分钟)
- 自我介绍。
- 目前是大三的话,学校学习了什么课程?
- 说说你对操作系统的理解,我希望是体系化的介绍,代表你对整个课程的理解。
- 给你一个 url 链接你会想到什么?
- 什么是 DNS 污染?
- 项目经历里面你做了什么?是领券功能还是抢券功能?库存扣减你是怎么做的?
- 通过项目实践,你收获了什么?
- Leetcode 下一个排列。
- 当前你的实习计划是什么样的?
- 反问:组里整体氛围是什么样的?结果什么时候会出?实习生的要求以及建议?
HR 面
- 自我介绍。
- 确认基本信息:目前大几、预计什么时候毕业?
- 接下来一年的学业安排是什么样的?还有哪些课程?一周大概有多少课?后续有没有毕业论文、毕设等安排?
- 如果实习 base 不在学校所在地,课程怎么安排?学校是否支持长期实习?是否可以请长假?下学期是否也可以继续请假实习?
- 下一段实习怎么规划?大概什么时候可以开始实习?希望实习多久?
- 为什么计划实习六个月以上?六个月之后是准备回学校做其他事情,还是可以继续延长?最长大概可以实习多久?
- 找实习找了多长时间了?
- 目前面试过哪些公司 / 岗位?哪些已经走到比较靠后的流程?
- 为什么拒掉已经拿到的其他机会?
- 选择要不要面一个岗位、以及拿到 Offer 后要不要接,最看重哪些因素?如果选最重要的三个因素,怎么排序?
- 你所说的「有挑战的业务」具体是什么样的?
- 两个项目里,你觉得哪个更有挑战?项目背景是什么?是课程项目、个人尝试还是其他项目?遇到了什么难点?最后是怎么解决的?
- 这个项目是你自己一个人独立闭环完成的吗?
- 如果让你重新做一次这个项目,会在哪些地方做调整或改进?怎么让开发过程更高效?怎么让最终效果更好?
- 在学校期间,老师或同学对你的高频评价是什么?如果总结成 1~3 个关键词,会是什么?
- 你对下一段实习有什么期待?如果实习六个月甚至更久,希望最终获得什么样的成长和成果?
- 关于博客 / 技术文章:做博客大概多久了?最开始为什么想做博客?坚持写博客过程中有哪些思考和收获?
- 反问:从 HR 的角度对实习生有什么建议或者要求?HR 面之后的整体流程是什么样的?结果大概什么时候出来?Base 地怎么考虑?
《参考解析》
- 秒杀完整链路:整条链路的目的是把流量挡在数据库前面。请求进来先在网关或应用层做限流和防刷(同一用户、同一设备的频次),然后用 Redis 做库存预扣。预扣必须用 Lua 脚本,因为它把「读库存 → 判断是否充足 → 判断用户是否已领 → 扣减 → 记录领取」这几步合成一次原子执行,Redis 单线程执行脚本期间不会被打断,天然免疫并发。扣减成功之后不要同步写订单库,而是发一条 MQ 消息异步落库、异步发券,接口立刻返回「排队中」,前端轮询或推送结果。这样数据库承受的是削峰之后的写入量。
- Lua 预扣成功但 MQ 发送失败怎么办:这是这道题的真正考点——本地操作已经生效、下游投递失败了。要分两种情况看。如果在事务里,可以用本地消息表:扣减和「写一条待发送消息」放在同一个本地事务里,再由定时任务扫表补发,靠消息表的唯一索引保证不重复发。如果用 RocketMQ,可以用事务消息,先发半消息、执行本地事务、再提交或回滚。如果只是单机无事务,最省事的是「先写消息表再扣减」或者扣减后立刻记录一条补偿记录,总之原则是不允许出现「Redis 已经扣了但没有任何地方记得要补」的状态。兜底还要有对账任务,按券的发放流水和订单表比对,把差异捞出来人工或自动修。
- 热点 key 怎么处理:先确认热点的粒度。如果是单张秒杀券被百万用户同时访问,那把库存拆成 N 份分片(比如 100 份),请求按用户 ID 哈希落到某个分片扣减,各分片独立执行 Lua,最后汇总;这是「库存分桶」。如果是商家维度的读热点,就在本地缓存(Caffeine)加一层,或者给 key 加随机后缀把读请求打散到多个副本上再聚合。另外要注意热 key 的探测本身——不是等它出问题才知道,而是靠客户端埋点统计 key 的 QPS,超过阈值就用
--hotkeys或 MONITOR 采样确认,然后把这个 key 推进本地缓存。 - 银行扣减超卖的代码怎么改:那道题的核心是「检查—执行」不是一个原子操作,
if (!paid(orderId))和markPaid(orderId)之间可能被其他线程插入,也可能被同一条线程的另一次调用重入。改法分几层。单机用synchronized的话,锁要加在稳定的共享对象上,加在方法上锁的是this,如果 service 是多例、或者有多个实例,就完全锁不住;更稳的是按 orderId 分段加锁(ConcurrentHashMap+ 每 key 一把锁,或striped lock)。分布式场景要用 Redis 的SET NX PX或者数据库乐观锁(带 version 或状态字段的update ... where status = ?),并且把「查状态 → 改状态」收进一条带条件的 SQL 里,用影响行数判断是否抢到。除此之外还要考虑幂等:同一笔支付请求重试时不能重复扣钱,所以要有业务唯一键。 - 只用 MySQL 能不能实现:能,而且不少真实系统就是这么做的。用
update coupon set stock = stock - 1 where id = ? and stock > 0这一条语句,靠 InnoDB 的行锁保证原子性,影响行数为 1 就是扣减成功。它的优点是实现简单、不用维护 Redis 一致性,缺点是高并发下所有请求争同一行锁,会串行排队,QPS 上不去(单行热点更新的上限大概就是几千)。所以选型看量级:几千 QPS 以内 MySQL 足够;到了几万、几十万就得引入 Redis 预扣把流量卸掉这一层。 - skill、MCP 和 tool 的区别:这三者经常被混着问,区别在抽象层次和加载方式。tool 是最小执行单元,一个函数加一份 JSON Schema,模型决定什么时候调用它。MCP 是协议,解决的是「工具怎么接进来」——把外部系统(数据库、文件系统、第三方 API)按统一协议暴露成一组工具,好处是接入和模型解耦、可以复用。skill 更偏「领域知识和流程的封装」,它描述的是「这类任务该按什么步骤做、要注意什么」,往往内含多个 tool 调用、也可能带脚本和参考资料,通常在需要时才按描述被加载进来,所以能省上下文。一句话概括:tool 是动作,MCP 是动作的接口标准,skill 是把一串动作加判断条件打包成的作业指导书。
- 两根绳子计时 45 分钟:第一步,同时点燃第一根绳子的两头和第二根绳子的一头;第一根烧完时正好过去 30 分钟,此时第二根还剩 30 分钟的「单头燃烧量」。第二步,立刻把第二根的另一头也点上,它剩下的部分两头同时烧,15 分钟烧完。总计 45 分钟。关键理解是「不均匀」不影响结论:两头同时烧一定是用时的一半,因为两团火无论在哪里相遇,消耗掉的总长度都是整根。
- 联合索引 (a, b) 和 (b, a) 的差异:最左前缀原则决定了两者能支持的查询不同。
where a = 1能用 (a, b),用不了 (b, a);where a = 1 and b = 2两者都能用,差别在数据分布不好时 (a, b) 可能先按 a 定位到很大一片再过滤 b,而 (b, a) 反过来。真正会出现「同样的条件却选了另一个索引」的情况,往往是优化器根据统计信息估的行数不同,或者某个索引是覆盖索引(要查的列都在索引里就不用回表,成本天然更低)。count(*)和count(具体列)的区别是前者不判断 NULL、能走最小的二级索引,后者要读出那一列再判断非空,所以一般count(*)更快;而count(*)配a = 1 and b like '%name'这种,b上的前导通配符会让它没法走索引,只剩a能用。 - P50 / P95 这类指标怎么测出来:压测工具(JMeter、wrk、k6)跑出带分位的报告,或者生产上从埋点日志里按时间窗口聚合。关键是说清楚三件事:测的是什么环境(本地 / 预发 / 线上灰度)、样本量和持续时间、以及分位数是从什么粒度统计的(单机还是全链路)。如果简历上写了「优化后 P95 从 800ms 降到 200ms」,要能补上「用 200 并发压 10 分钟、取后 5 分钟的稳定值」这种细节——考官问这一句,考的就是这个优化是不是真的做过。
- RAG 的完整链路和「决策机制」:链路本身是「文档解析与切分 → 向量化入库 → 查询改写 → 召回(向量 + 关键词混合)→ 重排 → 拼 prompt → 生成」。被问「决策机制」时,通常是指召回之后怎么决定给模型哪些内容:常见做法是设分数阈值过滤低相关内容、按来源去重并限制同一文档的片段数量、再按 token 预算截断。真正能加分的回答是补上「我为什么这么定」——比如阈值太松会把噪声喂进去导致答非所问,太紧则召回率掉下来,所以用一批标注问题扫了一遍阈值取 F1 最高的点。混合检索的收益也要能说出量化的提升,不然就只是「我用了两种检索」。