百度后端一面总结
概览
- 面试流程特点:技术面 + 技术面 + HR 面(推测)
- 整体难度/风格:重基础(网络、数据库、并发、JVM),项目追问深,手撕代码
面经列表
面经 1 | 后端开发工程师 | 2026-07-29
来源:本人亲历(视频面试) 时间:2026-07-29 职位:后端开发工程师 轮次:一面(技术面) 结果:未知 ⚠️(面试官评价基础不牢、表达能力不佳)
面试问题与解答
Q1:HTTP 四次挥手的过程?
A:TCP 连接终止的四次挥手:
- FIN:客户端发送 FIN,表示不再发送数据(仍可以接收)
- ACK:服务端回复 ACK,确认收到 FIN
- FIN:服务端发送 FIN,表示也不再发送数据
- ACK:客户端回复 ACK,进入 TIME_WAIT 状态(2MSL 后关闭)
💡 TIME_WAIT 作用:① 确保最后一个 ACK 被对方收到 ② 防止旧连接的延迟报文干扰新连接
Q2:HTTPS 如何保障安全性?
A:HTTPS = HTTP + SSL/TLS,通过三层机制保障:
- 加密:TLS 握手协商对称密钥,后续通信用对称加密,防止窃听
- 完整性:MAC(消息认证码)/ 数字签名,防止篡改
- 身份认证:服务端证书(X.509)由 CA 签发,客户端验证证书链,防止中间人攻击
Q3:TLS 握手协议的过程?
A:TLS 1.2 握手(简化):
- ClientHello:客户端发送支持的加密套件列表 + 随机数(Client Random)
- ServerHello:服务端选择加密套件 + 发送证书 + 随机数(Server Random)
- 密钥交换:客户端验证证书 → 生成 Pre-Master Secret → 用服务端公钥加密发送
- 双方生成会话密钥:Client Random + Server Random + Pre-Master Secret → 对称密钥
- Finished:双方用会话密钥加密发送 Finished 消息,验证握手成功
TLS 1.3 简化为 1-RTT(甚至 0-RTT),废弃了 RSA 密钥交换,改用 ECDHE
Q4:线程池的七大参数?
A:
| 参数 | 说明 |
|---|---|
corePoolSize | 核心线程数,即使空闲也保留 |
maximumPoolSize | 最大线程数 |
keepAliveTime | 非核心线程空闲存活时间 |
unit | 存活时间单位 |
workQueue | 任务等待队列(如 LinkedBlockingQueue、SynchronousQueue) |
threadFactory | 线程工厂,自定义线程命名等 |
rejectedExecutionHandler | 拒绝策略 |
Q5:任务进入线程池的交付过程?
A:
- 当前运行线程数 <
corePoolSize:创建新线程执行任务 - 当前运行线程数 ≥
corePoolSize:任务进入workQueue workQueue已满且运行线程数 <maximumPoolSize:创建非核心线程- 运行线程数 ≥
maximumPoolSize且队列已满:执行拒绝策略
Q6:线程池拒绝策略有哪些?
A:
| 策略 | 行为 |
|---|---|
AbortPolicy(默认) | 抛出 RejectedExecutionException |
CallerRunsPolicy | 由调用线程(提交任务的线程)自己执行 |
DiscardPolicy | 静默丢弃任务 |
DiscardOldestPolicy | 丢弃队列中最老的任务,然后重试提交 |
Q7:MyISAM 和 InnoDB 的对比?
A:
| 维度 | MyISAM | InnoDB |
|---|---|---|
| 事务 | 不支持 | ✅ 支持 ACID |
| 行锁/表锁 | 表锁 | 行锁 + 表锁 |
| 外键 | 不支持 | ✅ 支持 |
| 崩溃恢复 | 不支持 | ✅ Redo Log |
| 索引 | 非聚簇索引 | 聚簇索引(主键索引即数据) |
| 适用场景 | 读多写少、日志、统计 | 高并发、需要事务 |
Q8:幻读和脏读的区别?
A:
- 脏读(Dirty Read):事务 A 读取了事务 B 未提交的修改,B 回滚后 A 读到的数据无效
- 幻读(Phantom Read):事务 A 执行两次相同条件的查询,第二次查询到了事务 B 新插入的行(行数变了)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ❌ | ❌ | ❌ |
| READ COMMITTED | ✅ | ❌ | ❌ |
| REPEATABLE READ | ✅ | ✅ | ❌(MySQL 解决) |
| SERIALIZABLE | ✅ | ✅ | ✅ |
Q9:InnoDB 如何解决幻读和脏读?
A:
- 脏读:通过 MVCC(多版本并发控制),读操作读取快照版本,不读未提交数据
- 幻读:
- 快照读(
SELECT):MVCC + 一致性读,读取事务开始时的快照,避免幻读 - 当前读(
SELECT ... FOR UPDATE):Gap Lock(间隙锁)+ Next-Key Lock,锁定范围防止插入
- 快照读(
MVCC 实现:每行记录隐藏两个字段
DB_TRX_ID(最近修改事务 ID)和DB_ROLL_PTR(回滚指针),通过 Read View 判断可见性
Q10:Redis 除了缓存还能做什么?
A:
- 分布式锁:
SET key value NX EX 10(RedLock 方案) - 限流:滑动窗口计数器、令牌桶
- 消息队列:List 做队列 / Pub-Sub / Stream
- 会话存储:用户登录态、购物车
- 排行榜/计数器:
ZADD、INCR - 布隆过滤器:RedisBloom 模块
- GEO 地理位置:附近的人
- 分布式 ID 生成:
INCR原子自增
Q11:Redis 内存满了再插入数据会发生什么?
A:取决于 maxmemory-policy 配置:
| 策略 | 行为 |
|---|---|
noeviction(默认) | 拒绝写入,返回错误 |
allkeys-lru | 淘汰最近最少使用的 key |
allkeys-lfu | 淘汰使用频率最低的 key |
volatile-lru | 淘汰设置了过期时间的 key 中最近最少使用的 |
volatile-ttl | 淘汰即将过期的 key |
allkeys-random | 随机淘汰 |
生产环境推荐
allkeys-lru或volatile-lru
Q12:Java 21 的新特性?
A:
- 虚拟线程(Virtual Threads):轻量级线程,由 JVM 调度,海量并发不再 OOM
- 序列集合(Sequenced Collections):
SequencedCollection、SequencedMap,统一有序集合接口 - 分代 ZGC:降低延迟,支持纳秒级停顿
- Record 模式匹配:
instanceof+ Record 解构 StringTemplate(预览):字符串模板,类似 JS 模板字符串
Q13/Q14:虚拟线程是什么?如何调度?
A:
- 虚拟线程是 JVM 在用户空间实现的轻量级线程,一个 OS 线程可承载成千上万个虚拟线程
- 创建:
Thread.startVirtualThread(() -> {...})或Executors.newVirtualThreadPerTaskExecutor() - 调度:
- 虚拟线程由 JVM 的 ForkJoinPool(默认载体线程池)调度
- 当虚拟线程执行阻塞操作(I/O、sleep)时,自动让出载体线程,不阻塞 OS 线程
- 阻塞结束后,由调度器重新分配到可用的载体线程继续执行
- 无需手动管理线程池,适合高并发 I/O 密集型场景
Q15:介绍一下你的失物招领项目?
A:(原文未提供详细解答,需自行准备项目介绍话术)
💡 复盘建议:项目介绍应包含:背景(为什么做)→ 架构(技术选型)→ 难点(遇到什么问题)→ 解决(怎么解决的)→ 成果(数据指标)。控制在 2-3 分钟。
Q16:Java Spring 框架和 PHP 项目相比有什么优势?
A:
| 维度 | Java Spring | PHP |
|---|---|---|
| 企业级生态 | 完整(Spring Boot/Cloud/Security) | 轻量,适合快速开发 |
| 并发性能 | JVM 优化、线程池、虚拟线程,高并发强 | 多进程模型,高并发需借助 Swoole |
| 类型安全 | 强类型语言,编译期检查 | 弱类型,运行时才暴露问题 |
| 微服务 | Spring Cloud 完整解决方案 | 需借助第三方框架 |
| 运维 | 监控完善(Actuator、Micrometer) | 相对简单 |
Q17:Spring 的定时任务是怎么实现的?
A:Spring 提供三种定时任务方式:
| 方式 | 注解/API | 说明 |
|---|---|---|
| 基于注解 | @Scheduled + @EnableScheduling | 最简单,支持 cron/fixedRate/fixedDelay |
| 基于接口 | SchedulingConfigurer | 可动态配置任务,支持从数据库读取 cron 表达式 |
| 线程池调度 | ThreadPoolTaskScheduler | 底层实际执行者,管理线程池和任务队列 |
@Scheduled 的执行原理:
@EnableScheduling导入SchedulingConfiguration,注册ScheduledAnnotationBeanPostProcessor- 该 BeanPostProcessor 解析
@Scheduled注解,将任务封装为Runnable - 任务提交到
TaskScheduler(默认ThreadPoolTaskScheduler,线程池大小为 1) - 内部使用
ScheduledExecutorService.scheduleAtFixedRate()或.scheduleWithFixedDelay()执行
⚠️ 默认单线程,如果任务执行时间超过间隔,会堆积。生产环境建议自定义线程池:
@Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); return scheduler; }
Q18:操作系统底层是怎么做定时任务的?
A:操作系统通过 时钟中断 + 定时器数据结构 实现:
1. 硬件层面:时钟中断(Timer Interrupt)
- 由可编程间隔定时器(PIT/HPET/Local APIC Timer)定期触发中断
- Linux 默认 1000Hz(每 1ms 一次),每次中断更新
jiffies计数器
2. 内核层面:定时器管理
- Linux:使用 红黑树(rbtree) 或 时间轮(timer wheel) 管理定时器
- 高版本采用
hrtimer(高精度定时器),红黑树按到期时间排序 - 每次时钟中断检查红黑树中最小节点,到期则触发回调
- 高版本采用
- 时间复杂度:插入/删除 O(log N),获取最近到期 O(1)
3. 用户态到内核态的链路
用户进程调用 sleep()/setitimer()
↓ 系统调用
内核注册 hrtimer 到红黑树
↓ 时钟中断
检查到期定时器 → 唤醒进程/发送信号4. Java 的关联
Thread.sleep()→nanosleep()系统调用 → 内核 hrtimerScheduledThreadPoolExecutor→ 内部DelayedWorkQueue(小顶堆)→ 依赖LockSupport.parkNanos()→ 内核定时器
Q19:如何解决多图片并发上传导致的 OOM?
A:
- 流式处理:不一次性读入内存,用
InputStream边读边传 / 边写磁盘 - 限制单文件大小:前端 + 后端双重校验
- 限制并发数:线程池 / 信号量控制同时上传的数量
- 分片上传:大文件切分,断点续传
- 直接转存对象存储:不经过应用服务器内存,前端直传 OSS/S3
- 压缩/缩略图:上传时生成多尺寸,减少内存峰值
Q20:失物招领用户上传表单的流程?
A:(原文未提供详细解答,需自行梳理项目流程)
A:(原文未提供详细解答,需自行梳理项目流程)
💡 复盘建议:画图描述清晰的数据流:用户填写表单 → 前端校验 → 图片上传(直传 OSS)→ 后端接收 JSON → 参数校验 → 落库 → 异步通知匹配 → 返回结果
Q21:手撕合并数组列表
A:合并 K 个有序数组为一个有序数组。经典做法:
- 最小堆:维护 K 个指针,每次取最小值入结果,时间 O(N log K)
- 分治:两两合并,时间 O(N log K)
// 最小堆解法
PriorityQueue<int[]> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
// int[] = {value, arrayIndex, elementIndex}面试官评价与反馈
数据库基础不太牢固 — 对 MVCC、Gap Lock 的理解不够深入,建议系统学习《MySQL 45 讲》
项目介绍表达能力不佳 — 缺乏结构化表达,建议用 STAR 法则准备项目介绍
面试官观点:Agent 时代后端开发招聘仍然重基础,算法、网络、数据库、并发是核心考察点
经验教训
- 数据库是硬伤:MVCC、锁机制、隔离级别必须能深入讲清楚原理,不能停留在表面概念
- 项目介绍要练:准备一个 2 分钟版本 + 一个 5 分钟详细版本,用 STAR 法则(Situation-Task-Action-Result)
- 手撕代码不能断:合并 K 个有序数组是经典题,要熟练写出最小堆和分治两种写法
- 行业趋势:即使 AI Agent 兴起,后端开发的核心竞争力仍然是扎实的基础 + 工程能力