美云智数 Java软件开发 一面 面经
1. 自我介绍
您好,我叫XXX,目前是XX大学计算机专业的大三/大四学生。我主要的技术方向是Java后端开发,熟悉Spring全家桶、MySQL、Redis等主流技术栈。
在校期间我系统学习了数据结构、算法、操作系统、计算机网络等基础课程,打下了扎实的理论基础。同时我也注重实践,做过几个项目,包括电商系统、在线教育平台等,积累了一定的开发经验。
最近在XX公司实习,主要负责后端接口开发和系统优化工作。通过实习我深入了解了企业级项目的开发流程,学会了如何与团队协作,如何解决实际业务问题。
我对技术充满热情,平时会关注技术社区,学习新技术。我的学习能力和适应能力比较强,希望能加入贵公司,在实际项目中继续提升自己。
2. MySQL事务的隔离级别,分别解决什么问题?
四种隔离级别:
读未提交是最低的隔离级别,一个事务可以读到另一个事务未提交的数据。这会导致脏读问题,就是读到了可能会回滚的数据。这个级别基本不使用,因为数据不可靠。
读已提交只能读到已提交的数据,解决了脏读问题。但会出现不可重复读,就是同一个事务内多次读取同一条数据,结果不一样,因为其他事务修改并提交了数据。Oracle默认使用这个级别。
可重复读保证同一个事务内多次读取结果一致,解决了不可重复读问题。MySQL默认使用这个级别。但理论上会出现幻读,就是同一个事务内多次查询,记录数不一样,因为其他事务插入或删除了数据。不过MySQL通过MVCC和间隙锁在很大程度上解决了幻读。
串行化是最高的隔离级别,事务串行执行,完全避免了脏读、不可重复读、幻读。但性能最差,因为事务要排队执行。只在对数据一致性要求极高的场景使用。
隔离级别的实现:
MySQL主要通过MVCC多版本并发控制实现隔离。每行记录有隐藏的事务ID和回滚指针,形成版本链。事务开始时创建ReadView,根据可见性规则判断能看到哪个版本的数据。读已提交每次查询都生成新的ReadView,可重复读只在事务开始时生成一次ReadView。
对于写操作,使用锁机制。行锁保证并发修改的安全性,间隙锁防止幻读。
3. 在项目中遇到的最大问题是什么?怎么发现和解决的?
问题背景:
我在做电商项目时,遇到过一个并发问题。在秒杀场景下,出现了库存超卖的情况。本来库存只有100件商品,但实际卖出了120件,导致后面的订单无法发货,用户投诉。
问题发现:
最开始是运营人员发现库存数据不对,反馈给我们。我查看了数据库,发现库存确实是负数。然后查看日志,发现在秒杀开始的那一刻,有大量并发请求同时进来。
问题分析:
我分析了代码逻辑,发现问题出在扣减库存的流程上。当时的实现是先查询库存,判断是否充足,然后扣减库存。这个过程不是原子操作,在高并发场景下会出现竞态条件。
比如两个请求同时查询库存都是1,都判断可以购买,然后都去扣减库存,结果库存变成了-1。这就是典型的并发问题。
解决方案:
我尝试了几种方案。第一种是使用数据库的悲观锁,在查询时加for update锁住记录。但这种方式性能很差,因为所有请求都要排队。
第二种是使用乐观锁,给库存表加版本号字段,更新时判断版本号是否一致。这种方式性能好一些,但在高并发下失败率很高,用户体验不好。
最终我采用了Redis预扣库存的方案。秒杀开始前把库存数量加载到Redis,使用Lua脚本保证扣减库存的原子性。扣减成功后异步创建订单,定时任务同步数据到数据库。这样既保证了原子性,又有很好的性能。
优化效果:
优化后系统可以支持每秒上万的并发请求,库存数据完全准确,没有再出现超卖问题。接口响应时间从几秒降到几十毫秒。
经验总结:
这次经历让我深刻理解了并发编程的重要性。在高并发场景下,要特别注意原子性问题。要善用缓存和异步处理提升性能。遇到问题要系统分析,尝试多种方案,选择最合适的。
4. Spring Cloud的核心组件有哪些?分别的作用是什么?
服务注册与发现:
Eureka或Nacos作为注册中心,服务启动时注册到注册中心,其他服务可以从注册中心发现服务地址。注册中心会定期检查服务健康状态,自动剔除不健康的实例。这样实现了服务的动态管理,不需要硬编码服务地址。
配置管理:
Spring Cloud Config或Nacos Config提供集中化的配置管理。所有服务的配置都存储在配置中心,支持不同环境的配置隔离。配置修改后可以动态刷新,不需要重启服务。这样避免了配置分散难以管理的问题。
服务调用:
OpenFeign提供声明式的HTTP客户端,只需要定义接口和注解,就可以像调用本地方法一样调用远程服务。它集成了Ribbon负载均衡,自动在多个服务实例间分配请求。
网关:
Spring Cloud Gateway作为统一入口,负责路由转发、负载均衡、限流、鉴权等功能。所有外部请求先到网关,网关根据路由规则转发到具体服务。这样可以统一处理跨域、安全等问题。
熔断降级:
Sentinel或Hystrix提供熔断降级功能。当下游服务出现问题时,快速失败,避免调用方长时间等待。支持多种熔断策略,可以设置降级方法返回默认值。
链路追踪:
Sleuth配合Zipkin实现分布式链路追踪。记录请求在各个服务间的调用链路,包括耗时、状态等信息。可以快速定位性能瓶颈和故障点。
这些组件配合使用,构建了完整的微服务架构,解决了服务治理、配置管理、容错等问题。
5. Redis的数据结构及应用场景
String字符串:
String是最基础的类型,可以存储字符串、数字、二进制数据,最大512MB。常用于缓存对象、计数器、分布式锁。比如缓存用户信息用set和get命令,做计数器用incr命令原子性自增,实现分布式锁用setnx命令。
List列表:
List是双向链表,支持从两端push和pop。常用于消息队列、最新列表、关注列表。做消息队列用lpush生产消息,brpop阻塞消费。做最新动态用lpush添加,lrange获取最新N条。
Hash哈希:
Hash适合存储对象,field-value结构。常用于存储用户信息、购物车。比如购物车用hset设置商品数量,hincrby增减数量,hgetall获取整个购物车。相比String存储JSON,Hash可以单独修改某个字段,更灵活。
Set集合:
Set是无序不重复集合,支持交并差运算。常用于标签、共同关注、去重。比如用户标签用sadd添加,s
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经,带你练透java圣经