简历提问
# 自我介绍
面试官好,我是来自 XX 大学 软件工程 专业的26届本科毕业生 西螈,我今天面试的岗位是 Java后端开发, 我开发了两个项目,分别是快易点和校园美食圈,
快易点项目是一个定制的项目,主要是为商家提供一个点餐系统,商家可以管理菜品,用户可以浏览并点餐,项目中主要使用JWT令牌+拦截器和ThreadLocal来实现用户的登录校验,还有使用Redis将热点商品进行缓存,通过RabbitMQ的一个死信队列插件实现订单的超时自动取消
而校园美食圈是一个类似于大众点评的项目,用户可以查询附近的商户以及其他用户对这些商户的评价笔记,这个项目主要是通过Redis实现的,通过Redis+Token和拦截器的方案来实现用户的登录校验,项目中对高并发的秒杀优惠券部分,使用Redis分布式锁来实现一人一单,由于锁的粒度太大,我使用Lua+redis来实现抢单并使用RabbitMQ异步实现数据库库存扣减和订单生成的操作
非常荣幸能获得今天这个交流的机会,我希望能沟通愉快
# 技术深度与项目实践
校园美食圈 - 缓存实践:
你提到使用 Redis 缓存高频访问信息,降低数据库压力约30%。请问你是如何确定哪些数据属于“高频访问”且适合缓存的? (考察缓存策略的设计)
项目的核心场景和读多写少的信息:推荐商户列表、秒杀优惠券等、
描述一下你在解决缓存穿透时,“缓存空数据”的具体实现逻辑?动态TTL机制又是如何设计的? (考察对穿透、雪崩实际解决手段的理解)
关于缓存击穿,你提到用“逻辑过期”解决了问题。请对比一下“逻辑过期”和另一种常用的“互斥锁”方案,各自的优缺点是什么?为什么在项目中选择了逻辑过期? (考察对解决方案的权衡)
“接口响应时间降低约30%”——这个数据是如何得出的?是否做了压力测试?使用了什么工具进行压测?
校园美食圈 - 秒杀/分布式锁:
- 使用 Redis + Lua 脚本实现抢单业务时,Lua脚本具体封装了哪些操作?为什么要用Lua脚本而不是单独执行这些Redis命令? (考察Lua的作用)
- Redisson分布式锁实现“一人一单”。锁的粒度是如何设计的?(例如:锁的Key是什么?是基于用户ID、活动ID还是组合?) (考察锁的精细度设计)
- 在高并发下用CAS(乐观锁)进行库存控制时,可能遇到什么问题?(如自旋次数过多)。你是如何优化或者规避这个问题的? (考察对乐观锁局限性的认识)
- 用RabbitMQ异步扣减数据库库存和生成订单。请说明这个异步流程的具体步骤,并描述是如何保证消息最终不丢失的? (考察消息可靠性)
- 用户领券下单后,如何保证缓存(如库存缓存、活动信息缓存)与数据库的数据一致性?
校园美食圈 - 其他功能:
- 使用Redis ZSet实现点赞排行榜。ZSet的score和value分别存储的是什么?如何进行分页查询? (考察对ZSet特性的理解)
- 使用Set集合实现共同关注。假设用户A关注了用户B,用户B关注了用户C,用户A想快速找到与用户C的共同关注者,如何使用Redis命令高效完成? (考察对Set操作的应用)
快易点 - 通用技术:
- 你用AOP+自定义注解实现公共字段自动填充。请简述一下这个实现过程,自定义注解是如何定义和使用的,AOP切点织入在哪个时机? (考察AOP实际应用)
- 使用ThreadLocal存储用户信息确实方便,但要注意什么风险?特别是涉及到线程池复用的情况下?如何规避? (考察ThreadLocal最佳实践)
- 快易点项目中也提到使用Spring Cache优化代码。请对比一下原生Redis操作和使用Spring Cache的优劣?你在哪种场景下优先选择哪个? (考察框架选型思考)
# 原理与理解
- JUC & JVM:
- ThreadLocal的底层原理是什么?为什么它会导致内存泄漏,你又是如何避免的?(结合你的项目经验)
- 简述线程池的核心参数(corePoolSize, maxPoolSize, workQueue, keepAliveTime, handler)及其作用。如果给你一个核心数10,最大数20,队列大小100的线程池,突然来了200个任务,线程池的执行流程是怎样的?
- CAS操作的本质是什么?它依赖CPU的什么指令? (考察底层原理)
- 在项目中实际使用过哪些线程池?如何配置参数?有做过调优吗?
- 如果系统出现频繁Full GC,你会如何着手排查?可能的原因有哪些?(从JVM内存结构角度思考)
- Spring:
- Spring Boot自动装配的核心原理是什么?(提示:@EnableAutoConfiguration, spring.factories)
- Spring中Bean的生命周期是怎样的?(关键步骤如:实例化、属性填充、初始化、销毁等)
- IOC(控制反转)解决了什么问题?与DI(依赖注入)是什么关系?
- 在项目中是如何利用AOP的?举一个项目中的具体切面例子。
- MySQL:
- InnoDB的索引为什么选择B+树而不是B树或者Hash?
- 如何理解“回表”?写一个可能造成回表的SQL例子。覆盖索引如何避免回表?
- 简述MVCC(多版本并发控制)是如何在RC(读已提交)和RR(可重复读)级别解决读写冲突的?
- 什么情况下,即使你建立了索引,索引也可能失效?
- Redis:
- Redis 持久化方式 RDB 和 AOF 的区别?如何选择?
- 简述Redis主从复制的流程(全量复制/部分复制)。
- Redis哨兵(Sentinel)的核心功能是什么?如何实现主节点自动故障转移?选举新主节点的规则?
- 你在项目中使用Redis的哪种数据结构最多?分别解决什么场景?
- Redis是单线程模型,为什么性能还很高?它真的所有操作都是单线程吗?
- 消息队列(RabbitMQ):
- RabbitMQ如何保证消息不丢失?请从Producer、Broker、Consumer三个方面解释一下。
- RabbitMQ如何保证消息不被重复消费?(即幂等性问题)项目里是如何处理的?
- 死信交换机(DLX)的作用是什么?你在快易点项目中用它处理订单超时,描述一下消息是如何路由到死信队列的?
# 总结与发展
项目难点:
在你做过的项目中(尤其是校园美食圈),遇到的最大的技术挑战是什么?你是如何定位、分析并最终解决的?
在秒杀优化券时使用直接使用线程池异步处理方式导致事务失效,不过后面使用RabbitMQ就
你觉得你的“校园美食圈”项目在应对更大流量(比如用户量暴增10倍)时,哪个环节最可能成为瓶颈?如何优化?
学习与发展:
- 从你接触到的技术中(比如Redis、分布式锁、消息队列),你对哪个方向最感兴趣?为什么?接下来想深入学习的领域是什么?
- 你在学习新技术时,通常采用什么方法?(文档、源码、视频、实践?)
# 反问
我们项目大概