Java小厂后端面经(转Go方向)
《面试题目》
- 明确说明需要转 Go,能不能接受?
- 自我介绍。
- 项目介绍。
- 对项目进行拷打。
- 说明三级缓存架构设计,回答的是缓存穿透、击穿、雪崩。
- Redis 中缓存了哪些数据,用的什么数据结构,为什么用这个?
- 秒杀系统的流程是什么?
- 下单信息怎么返回给用户?
- 使用了消息队列,独立的线程获取消息后实现了什么?
- 刚下完单,订单信息还在消息队列中未写入数据库时,怎么通过订单 ID 查询订单信息?
- 生成订单到写 MySQL 的过程中一致性和性能怎么取舍?
- 对 MySQL 的理解,追问为什么主键使用 int,说一下最左前缀法则,解释为什么
like %xxx会产生索引失效。 - 使用过多线程吗?
- 简单说一下 CAS 机制。
《参考解析》
1. 三级缓存与穿透/击穿/雪崩
三级缓存通常指本地缓存(Caffeine)+ 分布式缓存(Redis)+ 数据库,逐层兜底。缓存穿透(查询不存在的数据)用布隆过滤器或缓存空值防御;缓存击穿(单个热点 key 过期瞬间高并发)用互斥锁重建或逻辑过期;缓存雪崩(大量 key 同时过期)用过期时间加随机抖动 + 多级缓存分散压力。
2. 秒杀系统流程与一致性取舍
典型流程:请求先到网关限流 → Redis 预扣库存(Lua 脚本保证原子性)→ 库存够则异步发 MQ 下单消息 → 消费者落库生成订单 → 返回用户排队中/已下单。下单瞬间数据库未必已写入,前端轮询订单状态接口,后端可先用 Redis 记录「订单已受理」状态供查询,DB 写入完成后再更新为最终状态,本质是用最终一致性换取秒杀峰值下的高吞吐。
3. MySQL 主键用 int 与最左前缀法则
主键建议用自增 int/bigint 而非 UUID,因为 InnoDB 聚簇索引按主键物理排序存储,自增主键保证顺序插入、减少页分裂,UUID 随机写入会导致频繁页分裂、索引膨胀。最左前缀法则:联合索引 (a,b,c) 只有从最左列 a 开始连续匹配的查询条件才能用上索引,跳过 a 直接查 b 则索引失效。like '%xxx'(前置通配符)无法利用 B+ 树索引的有序性做范围扫描,因此索引失效;like 'xxx%'(后置通配符)仍可命中索引。
4. CAS 机制
CAS(Compare And Swap)是一种无锁并发原语,包含内存值、预期值、新值三个参数,仅当内存值等于预期值时才更新为新值,否则失败重试(自旋)。Java 中 AtomicInteger 等原子类基于 CAS + Unsafe 类的硬件级 CMPXCHG 指令实现,避免了 synchronized 的线程阻塞开销,但存在 ABA 问题(可用版本号/AtomicStampedReference 解决)和高并发下自旋空转的 CPU 开销问题。