互联网大厂Java面试实录:从JVM调优到微服务架构的实战演练

场景介绍

某互联网大厂电商业务线面试现场,面试官老张(资深架构师)与候选人谢飞机(自称"全栈开发"的水货程序员)的对话实录。


第一轮:基础技术考察

老张:你好,请简单介绍一下你自己。

谢飞机:您好!我叫谢飞机,毕业三年,主要做Java开发,对Spring Boot、MySQL这些都很熟悉,平时喜欢研究新技术,像什么Kafka、Redis、Docker都有接触过...

老张:好的,那我们开始吧。第一个问题,在电商大促场景下,如果JVM堆内存突然飙升,你会如何排查?

谢飞机:嗯...这个嘛,我记得可以用jstat命令看看GC情况,还有jmap可以dump内存,然后MAT分析工具看大对象...应该是这样吧。

老张:思路是对的。那具体说说G1垃圾收集器和CMS有什么区别呢?

谢飞机:呃...G1好像是新一代的收集器,CMS是旧版的?它们都用来处理老年代...具体参数不太记得了。

老张:(微笑)至少方向对了。那你说说,电商订单系统里,为什么常用ThreadLocal存储用户信息?

谢飞机:这个我知道!ThreadLocal每个线程独立存储空间,避免线程安全问题,就像存用户ID一样很方便...

老张:不错!继续。如果一个订单创建接口响应时间从200ms变成2s,你的排查思路是什么?

谢飞机:先看日志有没有异常,然后查数据库慢查询,再看网络延迟...可能还要检查Redis连接池?

老张:很好,逻辑清晰。那DB和Redis数据不一致怎么处理?

谢飞机:这个...可以用延时双删策略?或者消息队列保证最终一致性?记不太清具体怎么实现了...

老张:(点头)有概念就行。好,我们进入下一轮。


第二轮:微服务与架构设计

老张:现在电商系统通常采用微服务架构,你理解Spring Cloud的核心组件有哪些?

谢飞机:Eureka做注册中心,Feign做远程调用,Hystrix熔断,Gateway做网关...还有配置中心Config。

老张:很好!那如果服务A调用服务B出现雪崩效应,你怎么解决?

谢飞机:用熔断降级,比如Resilience4j或者Sentinel,设置超时时间和失败次数阈值...

老张:不错。那分布式事务怎么处理?比如下单扣减库存和积分的场景。

谢飞机:TCC模式?或者Seata的AT模式?反正就是保证多个服务的数据一致性...

老张:(挑眉)具体说说Seata AT模式的原理。

谢飞机:呃...好像是有全局锁和回滚日志?提交前做undo_log记录,失败了就回滚...

老张:大体正确。那消息队列Kafka在电商中有什么应用场景?

谢飞机:订单创建后发消息通知库存系统,还有用户行为数据采集,解耦各个服务...

老张:对的。那如何保证消息不丢失?

谢飞机:开启ACK机制,持久化消息,消费者手动确认...

老张:很好。最后一个,微服务链路追踪怎么做?

谢飞机:SkyWalking或者Zipkin,加Trace ID,把请求头传递下去...

老张:回答得不错。进入最后一轮。


第三轮:深度场景题

老张:假设我们要做一个秒杀活动,高并发场景,你如何设计整个流程?

谢飞机:先在Redis预扣库存,然后用MQ异步下单,限流防刷,数据库最后落盘...

老张:具体说说Redis预扣库存的实现细节。

谢飞机:用Lua脚本保证原子性,key的设计要唯一,过期时间设置合理...

老张:那如果Redis挂了怎么办?

谢飞机:集群部署?主从备份?或者本地缓存兜底?

老张:(笑)这问题有点深啊。再问你一个,支付系统的幂等性怎么保证?

谢飞机:唯一流水号,数据库唯一索引,或者Redis去重键...

老张:很好。最后问个复杂的,如何设计一个支持千万级用户的推荐系统?

谢飞机:这个...可能要结合大数据平台,Flink做实时计算,Elasticsearch做检索,机器学习模型推荐...

老张:(露出赞赏的笑容)思路是对的,但细节还需要深入研究。好了,今天的面试就到这里。

谢飞机:谢谢面试官!

老张:回去等通知吧,我们会在一周内给你反馈。


面试答案解析

第一轮答案详解

1. JVM堆内存飙升排查

排查步骤:

# 查看进程PID
jps -l

# 监控GC情况
jstat -gcutil <pid> 1000

# 导出堆 dump
jmap -dump:format=b,file=heap.hprof <pid>

# 分析工具:VisualVM、Eclipse MAT、JProfiler

G1 vs CMS对比: | 特性 | G1 | CMS | |------|-----|-----| | 新生代+老年代划分 | Region分区 | 连续空间 | | GC停顿时间 | 可预测 | 不可控 | | 适用场景 | 大堆内存 | 低延迟需求 | | 标记算法 | 可达性分析 | 增量标记 |

2. ThreadLocal的使用场景
// 用户上下文封装
public class UserContext {
    private static final ThreadLocal<UserInfo> USER_INFO = new ThreadLocal<>();
    
    public static void setUserInfo(UserInfo user) {
        USER_INFO.set(user);
    }
    
    public static UserInfo getUserInfo() {
        return USER_INFO.get();
    }
    
    public static void clear() {
        USER_INFO.remove();
    }
}

**注意:**必须在finally块中clear(),防止内存泄漏。

3. DB和Redis数据不一致解决方案

方案一:Cache Aside Pattern

@Transactional
public void updateOrder(Order order) {
    // 1. 更新数据库
    orderMapper.update(order);
    // 2. 删除缓存
    redisTemplate.delete("order:" + order.getId());
}

方案二:延时双删

  • 先删缓存
  • 更新数据库
  • 休眠N毫秒
  • 再次删除缓存

方案三: Canal监听Binlog异步更新


第二轮答案详解

1. Spring Cloud核心组件

| 组件 | 作用 | 替代方案 | |------|------|----------| | Eureka/Nacos | 服务注册发现 | Consul、Zookeeper | | Feign/OpenFeign | 声明式HTTP客户端 | RestTemplate、Dubbo | | Hystrix/Sentinel | 熔断降级 | Resilience4j | | Gateway/Zuul | API网关 | Kong、Apigee | | Config/Bus | 配置管理 | Apollo、Nacos Config |

2. Seata AT模式原理

执行流程:

  1. 获取全局锁(Global Lock)
  2. 执行本地事务,记录Before Image和After Image
  3. 提交本地事务
  4. 异步提交全局事务(Undo Log)
  5. 若失败则根据Undo Log回滚

关键表结构:

CREATE TABLE undo_log (
    id BIGINT NOT NULL AUTO_INCREMENT,
    branch_id BIGINT NOT NULL,
    xid VARCHAR(128) NOT NULL,
    context VARCHAR(128) NOT NULL,
    rollback_info LONGBLOB NOT NULL,
    log_status INT NOT NULL,
    log_created DATETIME NOT NULL,
    log_modified DATETIME NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY ux_undo_log (xid, branch_id)
);
3. Kafka消息可靠性保证
# Producer配置
acks: all  # 所有副本确认
retries: 3  # 重试次数
batch.size: 65536
linger.ms: 10

# Consumer配置
enable.auto.commit: false  # 手动提交offset
max.poll.interval.ms: 300000

第三轮答案详解

1. 秒杀系统设计

完整流程图:

用户请求 → 限流网关(Nginx/Lua) → Redis预扣库存(Lua) 
→ MQ异步下单 → 消费者处理 → 数据库落库 → 返回结果

Redis库存扣减代码:

String script = "if redis.call('get', KEYS[1]) > ARGV[1] then " +
                 "return redis.call('decr', KEYS[1]) else return 0 end";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), 
                                     Collections.singletonList(key), count);

防刷措施:

  • Nginx层限流:limit_req_zone
  • 验证码校验
  • 用户黑名单
  • IP频率限制
2. 支付系统幂等性设计

方案一:唯一业务流水号

CREATE TABLE payment_order (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    pay_no VARCHAR(64) UNIQUE NOT NULL COMMENT '支付流水号',
    amount DECIMAL(10,2) NOT NULL,
    status TINYINT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

方案二:状态机控制

public enum PaymentStatus {
    CREATED(0), PAID(1), REFUNDED(2), CLOSED(3);
    // 状态转换规则
    Map<Pair<PaymentStatus, PaymentStatus>, Boolean> TRANSITION_RULES = new HashMap<>();
}

方案三:Redis去重

String key = "pay:lock:" + payNo;
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS);
if (success) {
    try {
        // 执行业务逻辑
    } finally {
        redisTemplate.delete(key);
    }
}
3. 千万级推荐系统架构

技术选型: | 模块 | 技术栈 | 说明 | |------|--------|------| | 数据采集 | Flume/Kafka | 用户行为日志收集 | | 实时计算 | Flink | 实时特征工程 | | 离线计算 | Spark/Hive | T+1批量训练 | | 模型服务 | TensorFlow Serving | 模型推理 | | 召回引擎 | Elasticsearch | 向量相似度搜索 | | 排序引擎 | XGBoost/LightGBM | 精排模型 | | 缓存 | Redis Cluster | 热点数据缓存 | | 存储 | Cassandra/Doris | 海量数据存储 |

推荐流程:

用户请求 → 召回层(多路召回) → 粗排 → 精排 → 重排 → 返回结果
          ↓                      ↓        ↓        ↓
     协同过滤              特征拼接   深度学习   多样性调整
     内容推荐              交叉特征   模型推理  业务规则

总结

本次面试覆盖了以下技术要点:

Java基础

  • JVM内存模型与GC算法
  • ThreadLocal生命周期管理
  • 并发编程最佳实践

中间件

  • Redis数据结构与持久化
  • Kafka消息可靠投递
  • MySQL索引优化与事务隔离

微服务

  • Spring Cloud组件选型
  • 分布式事务解决方案
  • 服务治理与监控

架构设计

  • 高并发秒杀方案
  • 幂等性保障策略
  • 大规模推荐系统架构

运维监控

  • ELK日志收集
  • Prometheus+Grafana监控
  • SkyWalking链路追踪

建议求职者:

  1. 基础知识要扎实,不能只背八股文
  2. 要有实际项目经验,能说出遇到的问题和解决方案
  3. 关注技术原理,理解底层实现
  4. 培养系统性思维,能从业务角度思考技术方案

祝各位求职顺利!🚀

更多推荐