大厂Java面试实录:从电商秒杀到微服务架构的深度拷问
大厂Java面试实录:从电商秒杀到微服务架构的深度拷问
面试场景设定
面试官:资深技术专家,严肃认真,注重技术深度 谢飞机:自称有3年Java开发经验,实际技术掌握参差不齐 业务场景:电商平台秒杀系统设计与实现
第一轮:基础架构与缓存设计
面试官:谢飞机你好,我们公司正在构建一个高并发的电商秒杀系统。首先,请你谈谈在这种场景下,你会如何设计系统的整体架构?
谢飞机:啊,秒杀系统啊!这个我知道,就是很多人同时抢购嘛!我会用Spring Boot搭建,然后加个Redis缓存商品库存,再搞个消息队列处理订单...(略显紧张)
面试官:嗯,思路基本正确。那么具体来说,你会如何使用Redis来防止超卖问题?
谢飞机:这个...用Redis的decrement命令减库存呗,减到0就不能买了!
面试官:不错,知道用原子操作。那如果Redis集群中某个节点宕机了,库存数据不一致怎么办?
谢飞机:呃...这个...应该不会吧?Redis不是有主从复制吗?(开始含糊)
面试官:好,我们继续。除了Redis,你还会用哪些技术来保证系统的高可用性?
谢飞机:可以用Nginx做负载均衡,还有...Spring Cloud的网关?
第二轮:微服务与分布式事务
面试官:刚才你提到了Spring Cloud,假设我们将秒杀系统拆分为商品服务、订单服务和支付服务三个微服务。在用户下单时,需要同时扣减库存、创建订单和预扣款,这三个操作如何保证一致性?
谢飞机:这个简单啊!用数据库事务不就行了?
面试官:但这是跨服务的分布式场景,每个服务都有自己的数据库。
谢飞机:那...那就用消息队列?先发消息,慢慢处理?(不确定地说)
面试官:具体说说你的方案。如果使用消息队列,消息丢失了怎么办?消费者重复消费怎么办?
谢飞机:消息丢失...可以持久化?重复消费...加个幂等性?(声音越来越小)
面试官:好,我们看个具体场景。用户秒杀成功后,订单服务创建订单成功,但支付服务调用第三方支付接口超时了,这时候该怎么处理?
谢飞机:这个...可以让用户重新支付?或者用定时任务检查?(明显底气不足)
第三轮:性能优化与监控
面试官:假设我们的秒杀活动预计有100万用户参与,峰值QPS可能达到10万。在这样的压力下,你会如何优化系统性能?
谢飞机:10万QPS!这么多!可以用Redis集群,还有...CDN加速静态资源?
面试官:具体到代码层面,你如何优化一个商品详情查询接口?假设这个接口需要查询商品基本信息、库存、价格和用户评价。
谢飞机:可以用缓存啊!把商品信息都缓存起来!
面试官:如果商品信息经常变动呢?比如价格调整、库存变化。
谢飞机:那就...设置短一点的缓存时间?或者用消息通知更新缓存?(开始瞎猜)
面试官:最后,系统上线后,你如何监控系统的运行状态?比如如何发现接口响应变慢、如何定位内存泄漏问题?
谢飞机:可以看日志...还有用那个...Prometheus?我们公司好像用过。(勉强回答)
面试官:好的,今天的面试就到这里。你的基础概念掌握得还可以,但在分布式系统设计和问题解决方面还需要加强。我们会在一周内通知你面试结果。
谢飞机:好的好的,谢谢面试官!(如释重负地离开)
技术深度解析与代码案例
1. Redis防止超卖的完整方案
@Service
public class SeckillService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 使用Lua脚本保证原子性,防止超卖
*/
public boolean seckillWithLua(Long productId, Long userId) {
String luaScript = """
local stockKey = KEYS[1] -- 库存key
local orderKey = KEYS[2] -- 订单key
local productId = ARGV[1]
local userId = ARGV[2]
-- 检查库存
local stock = redis.call('get', stockKey)
if not stock or tonumber(stock) <= 0 then
return 0
end
-- 检查是否重复购买
local bought = redis.call('sismember', orderKey, userId)
if bought == 1 then
return -1
end
-- 扣减库存
redis.call('decr', stockKey)
-- 记录购买用户
redis.call('sadd', orderKey, userId)
return 1
""";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
List<String> keys = Arrays.asList(
"seckill:stock:" + productId,
"seckill:order:" + productId
);
Long result = redisTemplate.execute(script, keys, productId.toString(), userId.toString());
if (result == 1) {
// 秒杀成功,异步创建订单
sendOrderMessage(productId, userId);
return true;
}
return false;
}
/**
* Redis集群数据一致性保障
* 使用Redisson的RReadWriteLock实现分布式锁
*/
public boolean updateStockWithLock(Long productId, Integer quantity) {
RReadWriteLock lock = redissonClient.getReadWriteLock("lock:stock:" + productId);
RLock writeLock = lock.writeLock();
try {
writeLock.lock(10, TimeUnit.SECONDS);
// 更新库存逻辑
return updateStockInDB(productId, quantity);
} finally {
writeLock.unlock();
}
}
}
2. 分布式事务解决方案:Seata AT模式
@Service
public class SeckillOrderService {
@GlobalTransactional(timeoutMills = 300000, name = "seckill-order-tx")
public boolean createSeckillOrder(OrderDTO orderDTO) {
// 1. 扣减库存(商品服务)
boolean reduceResult = productService.reduceStock(orderDTO.getProductId(), orderDTO.getQuantity());
if (!reduceResult) {
throw new RuntimeException("库存不足");
}
// 2. 创建订单(订单服务)
Order order = orderService.createOrder(orderDTO);
// 3. 预扣款(支付服务)
boolean payResult = paymentService.preDeduct(order.getOrderNo(), order.getTotalAmount());
if (!payResult) {
throw new RuntimeException("预扣款失败");
}
return true;
}
}
// 消息队列补偿方案
@Component
public class OrderCompensateConsumer {
@RabbitListener(queues = "order.compensate.queue")
public void handleCompensate(OrderMessage message) {
try {
// 检查订单状态
OrderStatus status = orderService.getOrderStatus(message.getOrderNo());
if (status == OrderStatus.CREATED) {
// 订单已创建但未支付,检查支付状态
PaymentStatus paymentStatus = paymentService.queryPaymentStatus(message.getOrderNo());
if (paymentStatus == PaymentStatus.FAILED) {
// 支付失败,回滚库存
productService.rollbackStock(message.getProductId(), message.getQuantity());
// 取消订单
orderService.cancelOrder(message.getOrderNo());
}
}
} catch (Exception e) {
// 记录日志,重新入队
log.error("补偿处理失败: {}", message.getOrderNo(), e);
throw new AmqpRejectAndDontRequeueException(e);
}
}
}
3. 高性能接口优化策略
@Service
public class ProductQueryService {
// 多级缓存设计
@Cacheable(value = "product", key = "#productId",
cacheManager = "caffeineCacheManager")
public ProductDetailDTO getProductDetail(Long productId) {
// 1. 先查本地缓存(Caffeine)
// 2. 本地缓存未命中,查Redis
// 3. Redis未命中,查数据库
return queryFromDB(productId);
}
// 并发优化:使用CompletableFuture并行查询
public ProductDetailDTO getProductDetailParallel(Long productId) {
CompletableFuture<ProductBaseInfo> baseInfoFuture = CompletableFuture
.supplyAsync(() -> productService.getBaseInfo(productId));
CompletableFuture<ProductStock> stockFuture = CompletableFuture
.supplyAsync(() -> stockService.getStock(productId));
CompletableFuture<ProductPrice> priceFuture = CompletableFuture
.supplyAsync(() -> priceService.getPrice(productId));
CompletableFuture<List<ProductReview>> reviewFuture = CompletableFuture
.supplyAsync(() -> reviewService.getReviews(productId));
try {
CompletableFuture.allOf(baseInfoFuture, stockFuture, priceFuture, reviewFuture).join();
return ProductDetailDTO.builder()
.baseInfo(baseInfoFuture.get())
.stock(stockFuture.get())
.price(priceFuture.get())
.reviews(reviewFuture.get())
.build();
} catch (Exception e) {
throw new RuntimeException("查询失败", e);
}
}
}
// 缓存更新策略:发布订阅模式
@Component
public class ProductCacheUpdateListener {
@EventListener
public void handleProductUpdate(ProductUpdateEvent event) {
// 删除本地缓存
caffeineCacheManager.getCache("product").evict(event.getProductId());
// 删除Redis缓存
redisTemplate.delete("product:" + event.getProductId());
// 发送消息通知其他服务更新缓存
rabbitTemplate.convertAndSend("product.cache.update", event);
}
}
4. 系统监控与告警配置
# application-monitor.yml
management:
endpoints:
web:
exposure:
include: "health,metrics,prometheus"
metrics:
export:
prometheus:
enabled: true
distribution:
percentiles-histogram:
http.server.requests: true
# 自定义指标
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "seckill-service")
.commonTags("region", "cn-east-1");
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
// 关键业务指标监控
@Component
public class BusinessMetrics {
private final Counter seckillSuccessCounter;
private final Counter seckillFailCounter;
private final Timer seckillTimer;
public BusinessMetrics(MeterRegistry registry) {
seckillSuccessCounter = Counter.builder("seckill.success.total")
.description("秒杀成功总数")
.register(registry);
seckillFailCounter = Counter.builder("seckill.fail.total")
.description("秒杀失败总数")
.register(registry);
seckillTimer = Timer.builder("seckill.process.time")
.description("秒杀处理时间")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
}
public void recordSeckill(boolean success, long duration) {
if (success) {
seckillSuccessCounter.increment();
} else {
seckillFailCounter.increment();
}
seckillTimer.record(duration, TimeUnit.MILLISECONDS);
}
}
业务场景与技术要点总结
1. 电商秒杀系统核心挑战
- 瞬时高并发:百万级用户同时请求
- 数据一致性:库存不能超卖
- 系统可用性:99.99%的高可用要求
- 响应速度:毫秒级响应时间
2. 技术架构演进
第一代:单体应用+数据库
- 问题:数据库成为瓶颈,容易宕机
第二代:缓存+队列
- 改进:Redis缓存库存,消息队列异步处理
- 问题:分布式事务复杂
第三代:微服务+云原生
- Spring Cloud微服务架构
- Kubernetes容器编排
- Service Mesh服务网格
3. 关键技术点深度解析
3.1 Redis高级用法
- Lua脚本:保证多个Redis命令的原子性
- 分布式锁:Redisson实现,解决集群数据一致性问题
- 数据结构选择:String存库存,Set存已购买用户,Hash存商品详情
3.2 分布式事务方案对比
| 方案 | 优点 | 缺点 | 适用场景 | |------|------|------|----------| | 2PC | 强一致性 | 性能差,阻塞严重 | 银行核心系统 | | TCC | 性能较好 | 业务侵入性强 | 电商订单 | | Saga | 异步,性能好 | 实现复杂 | 长流程业务 | | 消息队列 | 解耦,可扩展 | 最终一致性 | 秒杀、通知类 |
3.3 性能优化层次
- 前端优化:静态资源CDN、页面静态化
- 网关层:限流、熔断、降级
- 服务层:线程池优化、连接池配置
- 缓存层:多级缓存、缓存预热
- 数据库:读写分离、分库分表
4. 监控体系构建
四个黄金指标:
- 延迟:接口响应时间
- 流量:QPS、并发数
- 错误:错误率、异常数
- 饱和度:CPU、内存、磁盘使用率
监控工具栈:
- 指标收集:Prometheus + Micrometer
- 日志收集:ELK Stack
- 链路追踪:SkyWalking + Jaeger
- 可视化:Grafana
- 告警:AlertManager + 企业微信
学习建议
对于想要进入大厂的Java开发者,建议:
- 基础扎实:深入理解JVM、并发编程、数据结构
- 框架精通:Spring全家桶原理要懂,不能只会用
- 分布式实践:至少亲手搭建过微服务项目
- 性能优化:掌握常用的性能分析工具
- 业务理解:技术要为业务服务,理解业务场景
记住:大厂面试不仅考察你会什么,更考察你思考问题的方式和深度。从谢飞机的表现可以看出,知道概念和真正理解原理之间还有很大差距。持续学习,深入思考,才能在技术道路上走得更远。
更多推荐

所有评论(0)