25年11月它思科技 Java开发 二面
#JAVA##JAVA面经##JAVA内推#
1. 项目中你负责的模块如何保证接口的幂等性?
回答思路
- 核心锚定:幂等性核心是「同一请求多次执行结果一致」,需按请求类型+业务场景选择方案,核心逻辑是「唯一标识+状态校验+防重拦截」;
- 分层拆解方案(按场景适配):
- 读接口:天然幂等,无需额外处理;
- 写接口(如订单创建、支付回调):
- 方案1:唯一请求ID(幂等号)+ Redis防重锁
- 流程:前端/网关生成唯一requestId,接口接收后先通过
SET requestId 1 NX EX 300加锁,成功则执行业务,失败则返回重复请求;- 适用场景:短链接、支付回调等一次性请求;
- 项目落地:订单创建接口,requestId关联用户ID+订单业务号,Redis锁有效期覆盖业务最大处理时长;
- 核心:NX保证只有一次执行,EX防止死锁;
- 写接口(如库存扣减):
- 方案2:业务唯一键+数据库唯一索引
- 流程:以「用户ID+商品ID+活动ID」为唯一键创建数据库唯一索引,重复插入时触发主键冲突,捕获异常并返回“操作已执行”;
- 适用场景:有明确业务唯一标识的场景;
- 状态机控制:
- 流程:针对有状态流转的业务(如订单:待支付→已支付→已完成),执行业务前校验当前状态,仅允许合法状态流转(如已支付订单不允许重复扣减库存);
- 核心结论:核心是「唯一标识拦截重复请求 + 业务状态校验防止重复处理」,按场景选择Redis防重锁/数据库唯一索引/状态机。
标准答案
我负责的订单模块按场景分层保证幂等:① 支付回调接口:用网关生成的唯一requestId作为幂等号,接口层先通过Redis
SET requestId 1 NX EX 300加防重锁,成功则执行业务,失败则返回重复请求;② 订单创建接口:以「用户ID+商品ID+活动ID」为业务唯一键,创建数据库唯一索引,重复插入触发主键冲突时,捕获异常并返回“订单已创建”;③ 库存扣减接口:增加状态机校验,仅当订单处于“待支付”状态时允许扣减,已支付/已取消状态直接拒绝。核心逻辑是「唯一标识拦截重复请求 + 业务状态校验防止重复处理」,确保同一请求多次执行结果一致。
2. 高并发场景下,如何解决数据库读写性能瓶颈?
回答思路
- 核心锚定:数据库瓶颈核心是「读多写少/写多读少」,解决方案围绕「读写分离+缓存+分库分表+SQL优化」,按“先缓存→再分离→最后分库分表”的优先级落地;
- 分层拆解方案:
- 读性能优化(核心:减轻数据库读压力):
- 缓存层:热点数据(如商品详情、用户信息)接入Redis,设置合理过期时间+缓存预热+缓存击穿/穿透/雪崩防护;
- 读写分离:主库写、从库读,通过中间件(MyCat/Sharding-JDBC)实现读写路由,从库可水平扩容;
- 只读实例:针对超高频读场景,部署多个只读实例,分摊读压力;
- 写性能优化(核心:减少写冲突+提升写效率):
- 分库分表:按用户ID/订单ID哈希分片,拆分大表为小表,减少单表数据量和锁竞争;
- 批量写入:将高频小批量写入(如日志、埋点)改为批量提交,减少事务次数和IO;
- 异步写入:非核心数据(如订单日志)通过MQ异步写入,降低接口响应时间;
- 基础优化(兜底):
- SQL优化:避免大表全扫、优化JOIN、控制返回字段;
- 数据库参数调优:增大InnoDB Buffer Pool、优化日志刷盘策略;
- 核心结论:先通过缓存扛读压力,再读写分离扩展读能力,最后分库分表解决写瓶颈,配合基础优化兜底。
标准答案
高并发下数据库读写瓶颈按“先轻量后重度”解决:① 读瓶颈:热点数据接入Redis缓存(设置过期时间+布隆过滤器防穿透),主从分离(主库写、多从库读),通过MyCat实现读写路由;② 写瓶颈:按用户ID哈希分库分表拆分大表,高频小写入改为批量提交,非核心数据通过MQ异步写入;③ 基础优化:优化慢SQL(避免全表扫描、控制JOIN表数),调大InnoDB Buffer Pool提升缓存命中率。核心逻辑是“缓存扛读、分库分表扛写、读写分离扩容量”,优先用轻量方案(缓存),再用架构方案(分库分表)。
3. JDK 动态代理与 CGLIB 动态代理的底层实现差异是什么?
回答思路
- 核心锚定:差异核心是「代理对象创建方式+目标类依赖」,JDK基于接口,CGLIB基于继承;
- 分层拆解差异:
维度 JDK 动态代理 CGLIB 动态代理 底层原理 反射机制,生成实现目标接口的代理类 ASM字节码框架,生成目标类的子类 目标类要求 必须实现接口 无需实现接口,不能代理final类/方法 代理对象创建 Proxy.newProxyInstance() 创建代理实例 Enhancer.create() 创建子类实例 方法调用 通过InvocationHandler.invoke() 拦截 通过MethodInterceptor.intercept() 拦截 性能 反射调用,创建快、执行稍慢 字节码生成,创建慢、执行稍快 - 核心结论:JDK代理依赖接口,基于反射;CGLIB代理基于继承,基于ASM字节码,两者拦截方式和性能特性不同。
标准答案
JDK动态代理与CGLIB的核心差异:① 底层原理:JDK基于反射,生成实现目标接口的代理类;CGLIB基于ASM字节码框架,生成目标类的子类;② 目标类要求:JDK要求目标类必须实现接口,CGL
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏
