首页 > 25年10月乐精灵 JAVA开发 面经
头像
JAVA大厂圣经
发布于 03-08 20:52 广东
+ 关注

25年10月乐精灵 JAVA开发 面经

#JAVA##JAVA面经##JAVA内推#

先简单聊聊自己吧~学校、专业,怎么和编程结缘的?

“我是XX大学计算机科学与技术专业大四学生。高中因信息学竞赛接触C++,大学用Java写第一个‘课程表查询系统’时被其严谨的面向对象设计和‘一次编写,到处运行’的特性吸引。选择Java开发,源于三点:一是企业级应用对稳定性的高要求(如金融、电商),二是Spring生态对工程化的极致追求(从Boot到Cloud),三是在校园项目中用Java解决真实问题的成就感——比如用线程池优化判题服务,让同学提交代码后秒级反馈。我享受用代码构建可靠系统的踏实感,也渴望在贵司这样的平台深耕后端技术。”

挑一个你最有感触的项目:你负责哪块?当时遇到啥小难题?怎么搞定的?

“校园OJ系统中,我负责判题核心模块

  • 难题:高并发下判题服务CPU飙升(同学用死循环耗尽资源)
  • 解决
    1️⃣ 硬隔离:Docker容器限制--cpus=0.5 --memory=128m
    2️⃣ 软监控:Shell脚本定时检测进程CPU,超阈值自动kill
    3️⃣ 流程加固:RabbitMQ消费前校验代码特征(如检测while(true)
  • 收获:安全与稳定性必须前置设计。这件事让我养成‘上线前必压测+监控覆盖’的习惯。”

抽奖小场景:如何让每次抽奖概率尽量一致?

“核心思路:避免分次独立抽样
推荐方案:后台一次性随机生成2个不重复中奖者(如Collections.shuffle(users).subList(0,2)),前端分两次‘公布结果’。

  • 优势
    • 每人中奖概率严格=2/10
    • 无概率偏差(避免1/10→1/9的波动)
    • 业务可解释(‘系统已抽完,分步揭晓’)
    ⚠️ 若强制分两次抽:需用‘有放回抽样+去重重抽’,但实现复杂且体验割裂。工程上,用技术手段保障公平性,而非迁就业务表述。”

项目里用RabbitMQ主要是为了解决啥问题?

“三大价值:
🔹 削峰:秒杀请求入队,判题服务匀速消费(峰值QPS 500→稳定100)
🔹 解耦:订单创建后,异步触发短信通知、积分更新,避免主流程阻塞
🔹 异步:用户提交代码后立即返回‘排队中’,判题结果通过WebSocket推送
项目实例:OJ系统中,判题服务宕机时,消息积压在队列,恢复后自动续处理,保障用户体验。”

消息怎么保证不丢?

“三端可靠性设计:
🔹 生产者:Confirm模式 + 异步监听ack/nack → nack时存DB + 定时补偿重发
🔹 Broker:队列/交换机/消息均持久化(durable=true
🔹 消费者:手动ACK(处理成功后ack) + 幂等设计(业务ID去重) + 死信队列(DLQ)兜底
项目验证:压测10万消息,丢失率=0。关键认知:可靠性是组合拳,单点保障必有漏洞。”

中奖后半小时内领奖,超时自动失效——怎么实现?

“三方案对比:

方案 实现 适用场景
延迟队列 RabbitMQ TTL + 死信队列 高可靠、需精确触发
Redis ZSet score=过期时间戳,定时扫描 轻量、高并发
定时任务 每分钟查DB超时记录 简单业务
项目选择:用Redis ZSet(见下题),因判题超时场景需高频扫描,ZSet范围查询效率O(logN)。”

不用MQ,只用Redis(ZSet)+MySQL实现半小时失效?

“四步闭环:
1️⃣ 中奖时

  • MySQL存中奖记录(status=UNCLAIMED)
  • Redis ZSet:ZADD award_expire <timestamp+1800> award_id
    2️⃣ 后台线程:每秒执行ZRANGEBYSCORE award_expire 0 <current_timestamp>
    3️⃣ 处理失效
  • 遍历返回的award_id,更新MySQL status=EXPIRED
  • ZREM award_expire award_id
    4️⃣ 领奖时:先查ZSet是否存在,存在则删除并更新DB
    优势:避免数据库轮询压力,Redis原子操作保障一致性。”

MySQL事务的ACID,结合项目说说哪一点你特别注意过?

一致性(Consistency) 是生命线。

  • 场景:二手平台‘下单扣库存’
  • 保障
    @Transactional(rollbackFor = Exception.class)
    public void createOrder(Order order) {
        // 1. 扣库存(乐观锁:version校验)
        boolean success = productMapper.decrStock(order.getProductId(), order.getCount());
        if (!success) throw new BusinessException("库存不足");
        // 2. 创建订单
        orderMapper.insert(order);
        // 3. 发送MQ(异步,不阻塞事务)
        rabbitTemplate.convertAndSend("order.queue", order);
    }
    
  • 反思:曾因漏加rollbackFor,业务异常未回滚导致超卖。从此将‘事务边界+回滚规则’纳入Code Review Checklist。”

InnoDB怎么保证“原子性”?

“靠 Undo Log

  • 事务修改数据前,将旧值写入Undo Log(存于共享表空间)
  • 回滚时:根据Undo Log逆向操作(如INSERT→DELETE)
  • 关键细节:Undo Log本身也受Redo Log保护,崩溃恢复时先重做Redo,再用Undo回滚未提交事务
    认知:原子性不是‘魔法’,而是日志驱动的精密工程。”

事务隔离级别?项目用哪个?为啥?

项目用 REPEATABLE_READ(InnoDB默认)

  • 原因
    ✅ 避免不可重复读(如订单查询时价格突变)
    ✅ InnoDB通过间隙锁(Gap Lock) 解决幻读(范围查询加锁)
    ✅ 平衡性能与一致性(SERIALIZABLE锁表,性能差)
  • 避坑:明确告知前端‘列表页数据可能非实时’,关键操作(如支付)加版本号校验。”

“幻读”问题?InnoDB在RR级别下怎么防?

幻读定义:同一事务内,两次范围查询,第二次查到第一次不存在的记录(如SELECT * WHERE id>10,中间被插入id=11)。
InnoDB解决方案

  • 间隙锁(Gap Lock):锁住记录之间的‘间隙’(如id=10和id=20之间),阻止插入
  • 临键锁(Next-Key Lock):记录锁 + 间隙锁组合
    注意:RR级别下,当前读(如SELECT ... FOR UPDATE)会加间隙锁防幻读;快照读(普通SELECT)靠MVCC避免幻读感知。”

MVCC能用大白话说说吗?

“像‘时光机’:
1️⃣ 每行数据有隐藏字段:trx_id(创建事务ID)、roll_pointer(指向Undo Log旧版本)
2️⃣ 事务开始时生成Read View(记录当前活跃事务ID列表)
3️⃣ 查询时:

  • 只返回trx_id < Read View最小值 且 未删除的版本
  • 若当前版本不可见,沿roll_pointer找历史版本
    效果:读操作无锁,不同事务‘看到’各自时间点的数据快照
    项目价值:OJ系统中,学生查提交记录时,即使判题服务正在更新状态,也能看到稳定历史结果。”

索引在项目里怎么用的?像书的目录有啥好处?

项目实例

  • orders(user_id, create_time)建联合索引
  • 好处
    快速定位:查‘用户近7天订单’,避免全表扫描(10万行→扫15行)
    避免回表:查询字段全在索引中(覆盖索引)
    排序优化ORDER BY create_time直接走索引
    比喻:没有索引=翻整本字典找‘苹果’;有索引=直接翻‘苹’字部,效率天壤之别。”

聚簇索引是啥?和普通索引区别?

"| 特性 | 聚簇索引(主键索引) | 普通索引(二级索引) |
|------|---------------------|---------------------|
| 叶子节点 | 存整行数据 | 存主键值 |
| 查询 | 一次IO(无需回表) | 两次IO(回表查聚簇索引) |
| 数量 | 每表仅1个 | 可多个 |
项目认知:设计表时,主键选自增ID(避免页分裂),业务查询用二级索引+覆盖索引优化。”

为啥MySQL爱

更多模拟面试

全部评论

(0) 回帖
加载中...
话题 回帖

近期热帖

热门推荐