资讯动态

Java大厂面试实录:Spring Boot、Redis缓存、Kafka消息队列、微服务与JVM实战问答

发布时间:2026/8/15 12:22:59 来源:尧图企业网站定制
Java大厂面试实录Spring Boot、Redis缓存、Kafka消息队列、微服务与JVM实战问答周五下午西二旗某互联网大厂会议室。面试官老张面无表情地翻着简历对面坐着一位穿着格子衫、头发略显凌乱的年轻人谢飞机。他搓了搓手露出一个自信的笑容。面试官老张谢飞机是吧看了你的项目有电商秒杀经验那咱们直接聊技术。谢飞机好嘞面试官您放心我平时最爱研究技术了尤其喜欢研究Spring Boot用它就像坐飞机一样顺滑面试官老张行那我们开始第一轮。你先说说Java 8和Java 11有哪些主要区别谢飞机这个简单Java 8有Lambda表达式和Stream流还有新的日期时间API。Java 11的话……嗯是长期支持版本还新增了字符串的repeat()、isBlank()这些方法还直接支持运行单文件Java源码。我记得Java 9开始就有模块化了Java 11把一些旧的东西删了比如Java EE模块。面试官老张不错基本功还可以。那Spring Boot的自动配置是怎么实现的谢飞机这个也懂Spring Boot启动时会加载META-INF/spring.factories里的自动配置类通过EnableAutoConfiguration注解导入再配合一堆Conditional条件注解比如ConditionalOnClass、ConditionalOnMissingBean只有当classpath里有对应的类和配置时才会生效。真的我对这个可熟了。面试官老张好。那MyBatis里#{}和${}有什么不同谢飞机#{}是预编译会生成?占位符防止SQL注入${}就是直接拼接字符串有SQL注入风险。我平时都写#{}安全面试官老张那如果电商后台需要一个动态排序功能用户传入排序字段名比如price或sales你会怎么写谢飞机我……我就直接ORDER BY ${sortField}因为排序字段没法用占位符啊。不过这个字段我只允许传一个白名单比如先校验是不是price或者sales不是就默认用id……呃这样应该没问题吧面试官老张嗯知道做白名单还算有救。我们再聊点数据库运行的场景。假设一个订单表数据量很大查询某个用户的订单列表很慢你会怎么排查谢飞机加索引先看看where条件给用户ID加索引。如果还慢就explain看看执行计划是不是没走索引是不是全表扫描了。嗯还可以分库分表不过分库分表我没实际做过……但我看过好多文章面试官老张行基础还行。我们进入第二轮聊聊电商秒杀。你项目里用过Redis吧说说Redis常用数据结构。谢飞机用过有String、Hash、List、Set、ZSet还有Bitmap、HyperLogLog、Geo这些高级的。秒杀我一般用String做计数器用Hash存商品信息用ZSet做排行榜。面试官老张那缓存穿透、击穿、雪崩分别是什么怎么解决谢飞机缓存穿透是查询一个不存在的key每次都会打到数据库。解决方法是缓存空值或者用布隆过滤器。缓存击穿是一个热点key失效大量请求同时打到数据库解决方法是加互斥锁或者把热点key的过期时间设长一点。缓存雪崩是很多key同时过期解决方法是给过期时间加随机值别让他们一起过期。面试官老张背得挺熟。那秒杀场景下你怎么防止商品超卖谢飞机我用分布式锁哦不我……我当时在单机上用synchronized锁住库存扣减方法但在分布式下肯定不行我就用Redis的decr命令扣减库存这样是原子的不会超卖。然后……如果库存不够了就返回失败。面试官老张如果Redis本身是单点Redis挂了怎么办谢飞机啊……挂了那就……就数据库扛不行缓存雪崩了。可以搞Redis集群哨兵模式或者用RedLock但RedLock也有争论……嗯我很纠结。面试官老张行了。那Kafka如何保证消息不丢失谢飞机消息不丢失……生产者端要acksall消费者端要手动提交offset不要自动提交。还有Broker要配置副本数大于1副本同步……大概是这样吧。我只用过自动提交手动提交我了解一点。反正丢消息别找我我发消息都是靠玄学。面试官老张好第三轮我们聊微服务和JVM。先说说JVM内存划分。谢飞机JVM内存分为堆、方法区、虚拟机栈、本地方法栈、程序计数器。堆里面又分新生代和老年代新生代有Eden和两个Survivor区。方法区在Java 8之后变成了元空间用的是本地内存。一般我排查内存问题就jstat、jmap、jvisualvm反正一通操作就完事了。面试官老张嗯。Spring Cloud微服务之间调用你用什么谢飞机用OpenFeign声明式HTTP客户端写个接口加个注解就能调用。配合Nacos或者Eureka做服务发现用Ribbon做负载均衡。虽然现在Ribbon有点老了但Spring Cloud LoadBalancer也可以。面试官老张那如果订单服务调用库存服务库存服务突然挂了你怎么处理谢飞机我……我用try-catch包起来捕获异常后返回一个当前库存服务繁忙然后给用户提示请稍后再试。如果请求特别多我还可以重试几次用Retryable但这样可能会更糟……嗯应该用Resilience4j做服务降级和熔断比如走个降级方法快速失败别让调用方一直等。面试官老张那你懂双亲委派模型吗谢飞机懂的防止重复加载也保证核心类不被篡改。加载一个类时先让父加载器加载父加载器加载不了才由子加载器加载。比如我们自定义一个java.lang.String也加载不了因为启动类加载器已经把它加载了。面试官老张最后一个问题如果数据库查询慢除了加索引还能怎么优化谢飞机还能……优化SQL写法避免select *使用覆盖索引小表驱动大表。如果是分页查询深分页很慢的话可以用子查询或者游标。还可以加缓存比如把热点数据放到Redis里。嗯还有读写分离主库写从库读。总之方法很多具体问题具体分析吧。面试官老张好的你这三轮表现还可以但也有不少模糊的地儿。回去等通知吧我们有结果会告知你。谢飞机好嘞那我回去等您好消息了希望贵厂能给我这架小飞机一个跑道面试官老张……门在那边。详细答案解析让小白也能读懂第一轮Java基础与Spring/MyBatis1. Java 8 和 Java 11 主要区别Java 8 是2014年发布的里程碑版本带来了Lambda表达式、Stream API、Optional、新的日期时间APILocalDateTime、接口默认方法和静态方法。Java 11 是2018年发布的LTS长期支持版本基于Java 10。主要变化新增String方法isBlank()、strip()、repeat()、lines()。本地变量类型推断varJava 10引入Java 11继续支持。支持直接运行.java源文件java Test.java无需先编译。将Java EE模块如JAXB、JAX-WS从JDK中移除。增加了HTTP Client标准API替代HttpURLConnection。2. Spring Boot自动配置原理Spring Boot的核心是EnableAutoConfiguration注解。它会通过SpringFactoriesLoader加载META-INF/spring.factories文件新版是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中声明的所有自动配置类。每个自动配置类通常带有条件注解ConditionalOnClassclasspath中是否存在某个类。ConditionalOnMissingBean容器中是否还没有某个Bean。ConditionalOnProperty配置文件中是否设置了指定属性。例如RedisAutoConfiguration当classpath中有RedisOperations类时它会创建RedisTemplate和StringRedisTemplate。我们可以通过自定义配置类或application.properties来覆盖这些默认行为。3. MyBatis中#{}和${}的区别#{}预编译占位符MyBatis会将其解析成?然后使用PreparedStatement设置参数防止SQL注入性能也更好。${}字符串直接拼接不会使用预编译存在SQL注入风险。在实际开发中能用#{}就不用${}。但如果需要动态指定表名、列名如排序字段由于SQL语法不允许占位符出现在这些位置此时必须用${}。正确做法是对用户输入做严格白名单校验只允许预定义的字段名。例如String safeField price.equals(sortField) ? price : id; // 再用 ${safeField} 拼接4. 大表查询慢的排查思路先用EXPLAIN查看SQL执行计划重点关注type是否全表扫描、key实际使用的索引、rows扫描行数。确认WHERE条件、ORDER BY、JOIN关联字段上的索引是否合理。避免SELECT *只查询需要的列使用覆盖索引减少回表。深分页问题LIMIT 100000, 20会扫描十万行。可以改用游标分页或子查询WHERE id (SELECT id FROM orders WHERE ... ORDER BY id LIMIT 1 OFFSET 100000) ORDER BY id LIMIT 20;如果单表数据量过大考虑分库分表如按用户ID取模、读写分离、引入Redis缓存热点数据。第二轮缓存与消息队列1. Redis常用数据结构String最基础可用于计数器、缓存、分布式锁。Hash存储对象如商品详情可单独修改一个字段。List消息队列简单、最新列表。Set去重、共同好友、抽奖。ZSet有序集合排行榜、按分数排序。Bitmap签到、布隆过滤器底层。HyperLogLogUV统计。Geo地理位置计算。2. 缓存穿透、击穿、雪崩缓存穿透查询一个缓存和数据库都不存在的数据如恶意请求ID-1每次都打到数据库。解决缓存空值即使查不到也缓存一个空对象设置短期过期时间。使用布隆过滤器将存在的ID提前放入过滤掉不存在的请求。前端增加参数校验非法参数直接拒绝。缓存击穿某个热点key过期瞬间大量并发请求同时打到数据库。解决使用互斥锁分布式锁只允许一个请求去数据库加载其他请求等待或返回旧值。热点数据设置不失效后台异步更新。缓存雪崩大量key在同一个时刻过期导致所有请求都打到数据库。解决过期时间加上随机数避免同时过期。使用Redis集群提高可用性。服务端做熔断降级。3. 秒杀防超卖不能简单用synchronized在分布式环境下锁不生效。常用方案RedisDECR原子操作每个用户抢购前先DECR一个库存key如果返回值大于等于0则抢购成功否则失败。原子操作天然防止并发超卖。使用数据库UPDATE stock SET num num - 1 WHERE num 0返回受影响行数判断是否成功。这是乐观锁模式避免应用层加锁。更严格的做法是利用Redis预扣减异步发送消息到MQ由MQ消费者最终落地数据库保证最终一致性。4. Kafka保证消息不丢失消息不丢失需要从三个环节解决生产者设置acksall表示分区副本都收到消息才确认。同时设置retries重试次数大于0。Broker设置replication.factor副本因子大于1且min.insync.replicas大于1保证至少一个副本同步。消费者使用手动提交offsetenable.auto.commitfalse。处理完业务逻辑后再提交offset避免消息还没处理就提交导致程序崩溃时丢消息。注意Kafka保证的是“至少一次”投递可能重复消费因此消费端需要处理幂等性如使用唯一业务ID去重。第三轮微服务与JVM1. JVM内存划分程序计数器当前线程执行的字节码行号指示器。虚拟机栈每个方法调用对应一个栈帧存储局部变量、操作数栈、方法返回地址可能抛出StackOverflowError。本地方法栈为native方法服务。堆对象实例和数组。分新生代Eden/S0/S1和老年代。GC主要关注堆。方法区/元空间存储类信息、常量、静态变量。Java 8之前是永久代堆内Java 8改为元空间本地内存避免永久代溢出。排查工具jps查看进程jstat查看GCjmap导出堆jvisualvm可视化分析。2. Spring Cloud服务调用常用组件服务注册中心Nacos/Eureka/Consul、远程调用OpenFeign、负载均衡Spring Cloud LoadBalancer、熔断降级Resilience4j/Sentinel、网关Gateway/Zuul。OpenFeign用法定义接口添加FeignClient(nameinventory-service)方法上使用GetMapping(/reduce)注入后直接调用。Feign内置Ribbon负载均衡新版使用Spring Cloud LoadBalancer。3. 服务调用失败的处理不能只用try-catch因为如果下游服务已经过载大量请求等待超时会导致线程池耗尽进而拖垮调用方。应该使用超时配置HTTP连接和读取超时设置较短。熔断器使用Resilience4j连续失败率达到阈值时开启熔断后续请求快速失败走降级方法。降级定义fallback方法返回兜底数据比如返回一个“默认库存不足”的提示。限流对入口进行限流防止大流量冲击。异步/消息队列对于非实时操作可以发消息到MQ异步处理解耦调用方和下游。4. 双亲委派模型类加载器从上到下分为启动类加载器Bootstrap、扩展类加载器Platform/Extension、应用类加载器Application。当一个类需要被加载时首先会交给父类加载器加载父类加载器无法加载时才由子类加载器尝试加载。好处避免核心类如java.lang.String被重复加载。防止核心类被篡改保证Java类库的安全性。反例如果自己写一个java.lang.String类由于父类加载器已经加载过真正的String自定义类永远无法被加载或者抛出SecurityException。5. SQL优化思路扩展除了加索引还有以下手段覆盖索引查询列包含在索引中避免回表。例如CREATE INDEX idx_user_id ON orders(user_id, status)查询SELECT status FROM orders WHERE user_id?。减少关联查询拆分成多次简单查询或者冗余字段。使用分页优化避免大Limit使用WHERE条件定位游标。索引失效场景注意对列进行函数运算、隐式类型转换、LIKE %xxx、OR连接非索引列等都可能导致索引失效。读写分离主库负责写从库负责读配合MySQL主从复制。数据库连接池使用HikariCP并合理配置最大连接数。避免大事务及时提交减少锁冲突。分库分表数据量千万级以上时按业务维度分片。需要解决分布式ID、跨库查询、分布式事务等问题。缓存将热点数据放到Redis降低数据库读压力。以上便是这场“严肃与搞笑”交织的技术面试全记录。谢飞机虽然有些知识点回答得含糊但整体方向没跑偏。对于广大同学来说不仅要把基础概念背熟更要在真实业务场景中总结解决方案做到“知其然也知其所以然”。面试官那句“回去等通知”虽然让人焦虑但只要持续补全技术栈下一场机会一定会稳稳落地。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价