高频web后端工程师面试题及答案_第1页
高频web后端工程师面试题及答案_第2页
高频web后端工程师面试题及答案_第3页
高频web后端工程师面试题及答案_第4页
高频web后端工程师面试题及答案_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

高频web后端工程师面试题及答案Java中如何解决循环依赖?Spring通过三级缓存机制处理循环依赖。一级缓存singletonObjects存储已完成初始化的Bean实例;二级缓存earlySingletonObjects存储提前暴露的未初始化Bean(用于解决AOP代理问题);三级缓存singletonFactories存储ObjectFactory工厂,用于提供早期Bean引用。当A依赖B、B依赖A时,创建A的流程为:实例化A→将A的ObjectFactory存入三级缓存→执行属性注入时发现需要B→触发B的创建流程→B实例化后存入三级缓存→B属性注入时需要A→从A的三级缓存获取ObjectFactory提供早期A实例(可能是代理对象)→将早期A存入二级缓存并移除三级缓存→B完成初始化后存入一级缓存→回到A的属性注入,从一级缓存获取B→A完成初始化后存入一级缓存,同时移除二级缓存中的早期引用。需注意构造器注入无法解决循环依赖,因为构造器调用发生在实例化阶段,此时三级缓存尚未提供;而@Lazy注解通过延迟加载将依赖包装为代理对象,可绕过实例化阶段的直接依赖。MySQL中覆盖索引和回表的区别是什么?覆盖索引指查询所需的所有字段都包含在索引中,无需回表查询主键对应的行数据。例如,若有联合索引(name,age),执行selectname,agefromuserwherename='张三'时,索引本身已包含name和age,直接通过索引获取数据。回表则是查询字段不在索引中,需先通过索引找到主键,再根据主键到聚簇索引(主键索引)中查找完整行数据。例如,索引(name)查询selectfromuserwherename='张三',需先通过name索引找到主键id,再用id到聚簇索引获取其他字段。覆盖索引能显著减少I/O消耗,优化查询性能,设计索引时应尽量让常用查询字段包含在索引中。Redis分布式锁的实现需要注意哪些问题?核心要点包括:1.锁的原子性:加锁需使用setkeyvalueNXPXmilliseconds原子操作,避免setnx和expire分开执行导致锁无法释放;2.锁的过期时间:需根据业务执行时间设置合理过期时间,过短可能导致业务未完成锁已释放,过长可能导致死锁;3.锁的续期:若业务执行时间超过过期时间,需通过看门狗(如Redisson的自动续期机制)在锁过期前自动延长有效期;4.锁的释放:释放时需验证锁的持有者(通过唯一value标识),避免误删其他客户端的锁,可使用Lua脚本保证原子性(ifredis.call('get',KEYS[1])==ARGV[1]thenreturnredis.call('del',KEYS[1])end);5.可重入性:支持同一线程多次加锁,需记录加锁次数(如Redisson的可重入锁通过hash结构存储锁计数);6.集群环境下的一致性:使用Redlock算法(向多个独立Redis实例加锁,多数成功则认为加锁成功)可提升集群环境的可靠性,但需权衡性能和一致性。Kafka如何保证消息不丢失?需从生产者、Broker、消费者三端处理。生产者端:设置acks=all(所有ISR副本确认),开启重试(retries>0),使用幂等性(enable.idempotence=true)避免重复发送;Broker端:设置min.insync.replicas≥2(至少2个同步副本),确保消息写入多数副本后才确认;保留消息(log.retention.hours足够大),避免日志过早删除;消费者端:关闭自动提交(mit=false),手动提交offset需在消息处理完成后执行,避免处理未完成时提交offset导致消息丢失;处理消息时做好异常捕获,确保失败时能重试或记录到死信队列。分布式事务的TCC模式具体如何实现?TCC(Try-Confirm-Cancel)分为三个阶段:1.Try阶段:预留资源,检查业务参数合法性,冻结相关资源(如订单服务预扣库存、支付服务预占额度);2.Confirm阶段:正式提交资源,将Try阶段预留的资源确认使用(如库存扣减、额度扣除);3.Cancel阶段:回滚资源,释放Try阶段预留的资源(如库存解冻、额度恢复)。实现时需注意:Try需幂等(多次调用结果一致),Confirm/Cancel需可重试(网络波动可能导致重复调用),且需记录事务日志(用于故障时的事务恢复)。例如,电商下单场景:订单服务Try预提供订单(状态为“待确认”),库存服务Try预扣库存(标记为“已锁定”),支付服务Try预占账户余额(标记为“已冻结”);所有Try成功后执行Confirm,订单状态改为“已支付”,库存正式扣减,余额正式扣除;若任一Try失败,则执行Cancel,订单取消,库存解锁,余额解冻。JVM内存模型中各区域的作用及常见异常有哪些?JVM内存分为五部分:1.程序计数器:记录当前线程执行的字节码行号,线程私有,无内存溢出问题;2.虚拟机栈:存储方法调用的栈帧(局部变量表、操作数栈、动态链接、返回地址),线程私有,若线程请求的栈深度超过限制会抛出StackOverflowError;若栈扩展失败(动态扩展场景)会抛出OutOfMemoryError;3.本地方法栈:与虚拟机栈类似,用于本地方法调用,异常类型相同;4.堆:存储对象实例和数组,线程共享,是GC的主要区域,若对象过多无法分配内存会抛出OutOfMemoryError:Javaheapspace;5.方法区(元空间,JDK8后):存储类信息、常量、静态变量、即时编译代码等,线程共享,若类信息过多(如动态提供大量Class)会抛出OutOfMemoryError:Metaspace。MySQL的间隙锁(GapLock)在什么场景下会触发?如何避免?间隙锁是InnoDB在可重复读隔离级别下为解决幻读问题引入的锁,锁定索引记录之间的间隙(包括索引前、后间隙),防止其他事务插入新记录。触发场景:当使用范围查询(如whereidbetween10and20)且查询字段是索引字段时,若记录不存在或存在但需锁定范围,InnoDB会对该范围内的间隙加锁。例如,表中有id=5、id=25的记录,执行updateusersetstatus=1whereidbetween10and20,会锁定(5,25)的间隙,阻止其他事务插入id=15的记录。间隙锁可能导致死锁(如两个事务同时锁定同一间隙并尝试插入),可通过以下方式避免:1.将隔离级别改为读提交(RC),InnoDB在RC下仅使用记录锁(RowLock);2.优化查询条件,避免范围锁(如明确查询存在的记录);3.使用覆盖索引,减少锁的范围;4.对业务允许的场景,添加字段让范围查询变为等值查询(如增加状态字段限制)。Redis的持久化机制RDB和AOF的区别及选择策略?RDB(快照持久化)通过定时(如save9001)或手动(save/bgsave)提供二进制快照文件(dump.rdb),记录某一时刻的内存数据。优点:文件紧凑,恢复速度快;缺点:可能丢失最后一次快照后的所有数据(取决于快照间隔)。AOF(日志持久化)记录所有写操作命令(appendonly.aof),通过重写(bgrewriteaof)压缩日志。优点:数据丢失少(可配置每秒同步、每次写同步);缺点:文件较大,恢复速度慢。选择策略:1.对数据一致性要求不高的场景(如缓存),可仅用RDB;2.对数据一致性要求高的场景(如会话存储),建议RDB+AOF结合(AOF保证实时性,RDB加速恢复);3.AOF配置推荐appendfsync=everysec(每秒同步,兼顾性能和安全),RDB配置save6010000(减少快照频率,降低CPU消耗)。微服务中如何实现服务限流?常见限流算法有令牌桶、漏桶、滑动窗口。1.令牌桶:以固定速率向桶中添加令牌(如100个/秒),请求需获取令牌才能通过,支持突发流量(桶容量限制最大突发量);2.漏桶:请求进入漏桶后以固定速率流出(如100个/秒),超过容量的请求被拒绝,适合平滑流量;3.滑动窗口:将时间划分为多个窗口(如1分钟划分为6个10秒窗口),统计窗口内的请求数,超过阈值则拒绝,精度高于固定窗口。实现工具:Sentinel(支持QPS、线程数限流,提供热点参数限流)、GuavaRateLimiter(令牌桶实现)、Nginx(通过limit_req模块实现漏桶限流)。例如,电商大促时对商品详情接口设置QPS=5000,使用Sentinel的@SentinelResource注解标记接口,配置限流规则,超出部分返回“系统繁忙”提示。HTTP2相比HTTP1.1有哪些改进?1.二进制分帧:HTTP1.1基于文本,HTTP2将消息拆分为二进制帧(HeaderFrame、DataFrame),更高效解析;2.多路复用:通过流(Stream)标识(31位整数)在一个TCP连接上并发处理多个请求/响应,解决HTTP1.1的队头阻塞问题(一个请求阻塞不影响其他请求);3.头部压缩:使用HPACK算法压缩请求头,减少重复字段(如Cookie)的传输体积;4.服务器推送:服务器可主动向客户端推送关联资源(如HTML请求时推送CSS、JS),减少客户端请求次数;5.流量控制:每个流可设置发送窗口大小,防止接收方处理能力不足导致的阻塞。例如,访问一个包含10张图片的页面,HTTP1.1需建立多个TCP连接或顺序加载(受限于keep-alive的并发数),而HTTP2通过一个连接并行传输所有图片的DataFrame,显著提升加载速度。MyBatis的一级缓存和二级缓存有何区别?如何配置?一级缓存(本地缓存)是SqlSession级别的缓存,同一SqlSession内执行相同查询会直接从缓存获取,无需访问数据库。失效场景:SqlSession关闭、执行增删改操作、手动调用clearCache()。二级缓存(全局缓存)是Mapper级别的缓存,多个SqlSession共享(需配置),基于namespace隔离。开启二级缓存需:1.在mybatis-config.xml中设置<settingname="cacheEnabled"value="true"/>;2.在Mapper接口或XML中添加@CacheNamespace注解(注解方式)或<cache/>标签(XML方式);3.实体类需实现Serializable接口。注意:二级缓存默认存储查询结果,若表数据频繁更新,需设置缓存刷新策略(如flushInterval)或关闭二级缓存,避免脏读。如何设计高并发场景下的接口幂等性?幂等性指多次调用同一接口产生的效果与一次调用相同。实现方案:1.全局唯一ID:请求时提供唯一ID(如UUID、雪花ID),服务端通过数据库唯一索引或Redis记录已处理的ID,重复请求直接返回结果;2.状态机:通过状态字段控制操作(如订单状态“未支付→支付中→已支付”,重复支付请求检查状态为“已支付”则直接返回);3.token机制:客户端先获取token(如调用/genToken接口),请求时携带token,服务端验证token有效性并删除(防止重复使用),可通过Redis存储token(设置过期时间);4.数据库乐观锁:通过版本号(version)或时间戳(update_time)实现,更新时检查版本号是否匹配(updatetablesetcount=count-1,version=version+1whereid=1andversion=oldVersion);5.防重表:请求前插入防重表(唯一索引为请求ID),插入成功则处理业务,失败则认为重复请求。例如,支付接口使用全局唯一ID+Redis记录,服务端接收到请求后检查ID是否存在,存在则返回已处理结果,不存在则处理并记录ID。ZooKeeper在分布式系统中的常见应用场景有哪些?1.服务注册与发现:服务启动时在ZooKeeper的/services节点下创建临时子节点(如/provider/:8080),注册中心监听该节点获取可用服务列表;服务下线时临时节点自动删除,实现动态发现;2.分布式锁:通过创建临时顺序节点(/lock/seq-),获取最小节点的客户端获得锁,其他客户端监听前一节点的删除事件实现公平锁;3.配置中心:将配置存储在ZooKeeper的/conf节点,客户端监听节点变化,配置更新时自动拉取;4.集群管理:通过监听子节点变化(如/servers)感知集群节点上下线,实现负载均衡;5.分布式协调:解决脑裂问题(如双主系统中通过ZooKeeper选举主节点,只有主节点可写)。需注意ZooKeeper的CP特性(强一致性,分区时可能不可用),适合对一致性要求高的场景。如何优化慢SQL?步骤如下:1.定位慢SQL:通过MySQL的慢查询日志(slow_query_log)或监控工具(如PerconaToolkit的pt-query-digest)获取执行时间长的SQL;2.分析执行计划:使用explain命令查看SQL的执行计划,关注type(最好为const,最差为ALL)、key(使用的索引)、rows(扫描的行数)、Extra(是否Usingfilesort/Usingtemporary);3.优化索引:添加缺失的索引(如对where/orderby/groupby字段创建索引),避免索引失效(如避免在索引字段使用函数、类型转换);4.优化查询逻辑:减少select(使用覆盖索引),拆分复杂查询(如将多表join拆分为单表查询+内存组装),避免子查询(改用join或临时表);5.分页优化:大数据量分页(如limit100000,10)时,使用覆盖索引+子查询(selectfromuserwhereid>(selectidfromuserlimit99990,1)limit10)避免全表扫描;6.数据库层面优化:调整innodb_buffer_pool_size(增大缓存池),启用查询缓存(MySQL8.0已废弃),升级硬件(如SSD提升I/O速度)。Java线程池的核心参数有哪些?如何合理配置?核心参数:1.corePoolSize:核心线程数,线程池长期保留的线程数;2.maximumPoolSize:最大线程数,线程池允许的最大线程数;3.keepAliveTime:非核心线程的空闲存活时间;4.unit:时间单位;5.workQueue:任务队列(ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等);6.threadFactory:线程工厂(用于设置线程名称、优先级);7.handler:拒绝策略(AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy)。配置策略:1.CPU密集型任务(如计算、加密):corePoolSize=CPU核心数+1(避免线程切换开销);2.I/O密集型任务(如数据库查询、网络请求):corePoolSize=CPU核心数×2(I/O等待时线程空闲,需更多线程利用CPU);3.任务队列:短耗时任务用SynchronousQueue(无缓冲,直接提交给线程),长耗时任务用LinkedBlockingQueue(避免任务堆积);4.拒绝策略:关键业务用AbortPolicy(抛出异常提醒),非关键业务用DiscardOldestPolicy(丢弃旧任务)。例如,处理用户订单的线程池,corePoolSize=8(4核CPU×2),maximumPoolSize=16,workQueue=LinkedBlockingQueue(1000),handler=CallerRunsPolicy(让调用线程处理,降低提交速度)。Redis的过期键删除策略和内存淘汰机制有何区别?过期键删除策略处理已过期的键,内存淘汰机制处理内存不足时的键删除。1.过期键删除策略:定时删除:设置定时器在键过期时立即删除(CPU消耗大);惰性删除:访问键时检查是否过期,过期则删除(内存占用高);定期删除:每隔一段时间扫描部分键,删除过期键(Redis采用惰性删除+定期删除结合)。2.内存淘汰机制:当内存达到maxmemory时,按策略删除键,Redis支持的策略有:noeviction(拒绝写操作);allkeys-lru(删除最近最少使用的键);allkeys-random(随机删除键);volatile-lru(仅删除设置过期时间的最近最少使用键);volatile-random(仅删除设置过期时间的随机键);volatile-ttl(仅删除设置过期时间且剩余时间最短的键)。生产环境推荐allkeys-lru(适用于缓存场景)或volatile-lru(适用于混合存储场景)。如何设计一个高可用的分布式系统?关键措施包括:1.冗余部署:核心服务多实例部署(如Nginx负载均衡、K8s的ReplicaSet),避免单点故障;2.故障检测:通过心跳机制(如ZooKeeper的临时节点、Eureka的心跳续约)或健康检查(如SpringBootActuator的/health接口)检测节点状态,自动隔离故障节点;3.自动恢复:使用K8s的自愈机制(Pod失败时自动重启)、Hystrix的熔断(服务不可用时快速失败,一段时间后尝试恢复);4.数据一致性:通过分布式事务(TCC、事务补偿)、最终一致性(消息队列异步同步)保证数据一致;5.流量治理:通过限流(Sentinel)、降级(关闭非核心功能)、负载均衡(Nginx、LVS)应对流量突增;6.异地多活:关键服务部署在多个机房(如阿里云的多可用区),通过DNS解析或GSLB(全局负载均衡)将流量导向健康机房,避免区域性故障。SpringAOP的实现原理是什么?如何处理循环依赖中的AOP代理?SpringAOP基于动态代理实现,有两种方式:1.JDK动态代理:为实现接口的类提供代理(Proxy.newProxyInstance),通过InvocationHandler拦截方法调用;2.CGLIB代理:为未实现接口的类提供代理(通过ASM修改字节码),通过MethodInterceptor拦截方法调用。AOP处理循环依赖时,三级缓存中的ObjectFactory会提前提供代理对象(若需要AOP),并存入二级缓存earlySingletonObjects。例如,A依赖B,A需要被代理,创建A时实例化后提供ObjectFactory(返回A的代理),B创建时获取A的代理(从三级缓存的工厂提供),A初始化完成后,最终的代理对象会替换二级缓存中的早期代理(实际为同一对象,因代理是单例),避免循环依赖导致的代理不一致问题。MySQL的主从复制是如何实现的?常见的复制延迟问题如何解决?主从复制通过二进制日志(Binlog)实现,步骤:1.主库将写操作记录到Binlog(开启binlog,binlog_format=ROW/STATEMENT/MIXED);2.从库的I/O线程连接主库,请求Binlog更新,主库的BinlogDump线程发送Binlog事件;3.从库将接收的Binlog事件写入中继日志(RelayLog);4.从库的SQL线程读取RelayLog,执行其中的事件,同步主库数据。复制延迟常见原因:主库写压力大(Binlog提供快于从库执行)、从库硬件性能差(CPU/磁盘慢)、大事务(从库需执行长时间的事务)。解决方法:1.优化主库SQL(减少大事务,拆分复杂操作);2.升级从库硬件(使用SSD、增加CPU核心);3.并行复制(MySQL5.7+的writeset并行复制,按库/表/行并行执行RelayLog事件);4.读写分离时,对实时性要求高的读操作直接访问主库;5.调整从库参数(如innodb_flush_log_at_trx_commit=2,减少磁盘I/O)。如何实现接口的熔断和降级?熔断(CircuitBreaker)指当服务失败率超过阈值时,自动拒绝后续请求,一段时间后尝试恢复;降级(Degradation)指在系统压力大时,关闭非核心功能,保证核心功能可用。实现工具:Hystrix(已停止维护)、Sentinel、Resilience4J。以Sentinel为例:1.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论