【面经合集】当 deepseek 遇上阿里的面试|Java后端|01
🌟【友情提示】本篇面经来自粉丝投稿+智能润色,点击进入 -> 🔗互联网面经大全 围观25届校招修罗场!!每个技术细节都经过脱敏处理,请勿对号入座~
🌈 面试官:
咱们先从数据结构开始吧,B树、B+树的优劣和适用场景能聊聊吗?听说B-树不太常见?
💬 小基:
好嘞!B树和B+树都是多路平衡树,但设计目标不同。
B树的优势是查询可能在非叶子节点就命中(比如范围查询时找到中间值),不用每次都下探到叶子节点,适合频繁随机读写的场景,比如文件系统索引。但它的缺点是范围查询效率低,因为需要中序遍历,而且节点同时存数据和索引,导致单个节点存储的键值更少,树的高度可能更高
B+树的叶子节点用链表串联,范围查询直接扫链表就行(比如查5-10,找到头尾直接遍历),而且非叶子节点只存索引,能容纳更多键,树更矮,减少磁盘IO次数。所以数据库(比如MySQL)和大型存储系统基本都用B+树。至于B-树,其实就是B树的另一种称呼,没有特殊区别~
🌈 面试官:
Redis的优缺点呢?听说大Key会阻塞?
💬 小基:
Redis优点很突出:内存读写速度极快(10万+/秒),支持丰富数据结构(String、Hash、List等),还有持久化(RDB快照/AOF日志)和集群高可用。但缺点也很明显:
- 内存贵,数据量大了成本高;
- 持久化有风险——RDB可能丢最后一次快照后的数据,AOF文件大了恢复慢;
- 大Key(比如一个Hash存了百万字段)会阻塞线程,甚至拖垮集群,得拆分成小Key或用分片
🌈 面试官:
缓存不一致怎么解决?比如数据库更新了但缓存还是旧数据。
💬 小基:
常见方案是双删延迟:
- 先删缓存 → 2. 更新数据库 → 3. 延迟几百毫秒再删一次缓存(防其他线程脏读)。
或者用订阅数据库Binlog的工具(比如Canal),数据库一改就自动删缓存。极端场景可以加分布式锁,保证更新时强制查库,但会影响并发性能
🌈 面试官:
消息队列如何避免消息丢失?
💬 小基:
得分环节保障:
- 生产者开Confirm模式,等Broker确认收到消息;
- Broker持久化消息到磁盘(比如Kafka的副本机制);
- 消费者手动ACK,处理完再通知Broker,避免自动提交导致数据丢失。
如果Broker没收到ACK重发消息,消费者要做幂等——比如用唯一ID(雪花算法、Redis自增ID)去重。
🌈 面试官:
手撕一个线程安全的阻塞队列?
💬 小基:
用Python的
threading.Condition
实现:
import threading
class BlockingQueue:
def __init__(self, capacity):
self.capacity = capacity
self.queue = []
self.lock = threading.Lock()
self.not_full = threading.Condition(self.lock)
self.not_empty = threading.Condition(self.lock)
def put(self, item):
with self.not_full:
while len(self.queue) >= self.capacity:
self.not_full.wait() # 队列满时等待
self.queue.append(item)
self.not_empty.notify() # 通知消费者有数据了
def take(self):
with self.not_empty:
while len(self.queue) == 0:
self.not_empty.wait() # 队列空时等待
item = self.queue.pop(0)
self.not_full.notify() # 通知生产者有空位了
return item
注释:
Condition
锁保证线程安全;wait()
和notify()
控制阻塞和唤醒;- 队列满时
put
阻塞,空时take
阻塞。
🌈 面试官:
最后聊聊Docker的优缺点?
💬 小基:
优点:
- 环境隔离:开发、测试、生产环境一致;
- 轻量:秒级启动,资源占用小;
- 易扩展:配合K8s快速扩容。
缺点:
- 网络配置复杂(比如跨主机通信);
- 数据持久化需要挂载卷;
- 安全性问题(容器逃逸漏洞)
总结:面试官全程点头,尤其夸代码注释清晰~ 问题虽然广但没深挖特别偏的,关键是逻辑清晰+结合实际场景!
#面经##java##deepseek#本专栏收集了互联网上的面试经验贴