2026 Java后端高频面试宝典

文章目录

一、Java 基础(必问 100%)

1. 重载和重写的区别(必考)

重载(Overload)

  • 作用范围:在同一个类内部
  • 规则:方法名相同,参数列表(个数/类型/顺序)不同,返回值类型不作约束
  • 绑定时机:编译期静态绑定,属于静态多态
  • 业务场景:工具类提供多参数查询方法,例如根据ID查用户、ID+姓名查用户

重写(Override)

  • 作用范围:父子类继承关系中
  • 规则:方法名、参数列表、返回值(协变)完全一致,不能缩小父类方法访问权限;private/static/final修饰的父方法无法重写
  • 绑定时机:运行期动态绑定,属于动态多态
  • 业务场景:普通订单、会员订单各自重写支付pay()方法,实现不同扣款逻辑

2. == 和 equals 的区别

  1. 基本数据类型:== 直接对比存储的数值
  2. 引用数据类型:== 对比堆内存对象地址
  3. Object原生equals()等价于==;String、Integer等包装类重写equals,改为对比对象存储内容

面试场景题:new String("a") == new String("a") → false;new String("a").equals(new String("a")) → true

3. ArrayList 和 LinkedList 区别 & 业务选型

  • ArrayList:底层动态数组,随机下标查询速度快,中间位置增删元素需要移位效率低,默认初始容量10,扩容1.5倍
  • LinkedList:底层双向链表,首尾节点增删O(1),随机下标查找需要遍历链表效率低,无扩容机制

选型口诀:多查少改用ArrayList(商品列表查询),频繁首尾插入删除用LinkedList(临时浏览记录队列)

4. 三种保证ArrayList线程安全的方案

  1. Vector:方法全部加synchronized,老旧API,全局锁并发性能差,项目不推荐
  2. Collections.synchronizedList:对原有List做包装,全方法加同步锁,并发效率一般
  3. CopyOnWriteArrayList:生产首选,写时复制新数组、读操作无锁,适合读多写少的业务场景(配置缓存列表)

5. String / StringBuffer / StringBuilder 三者区别

  • String:底层final字符数组,字符串拼接会频繁创建新对象,少量字符串拼接场景使用
  • StringBuffer:方法加synchronized同步锁,多线程安全,并发拼接性能偏低,多线程字符串拼接
  • StringBuilder:无同步锁,执行效率最高,单线程大批量拼接首选(日志内容组装)

6. 深拷贝与浅拷贝

  • 浅拷贝:仅拷贝对象引用地址,新旧对象共用子对象数据,修改其中一方会互相影响
  • 深拷贝:递归完整创建所有层级新对象,内存完全隔离,修改互不干扰

场景:接口返回订单VO对象,防止修改VO污染原数据库实体,使用深拷贝。

7. Cookie 和 Session 区别

分类CookieSession
存储位置客户端浏览器服务端内存
容量单条最大3KB,仅支持字符串无固定限制,可存任意对象
安全性明文存储可被篡改,不安全服务端保存,安全性高

业务规范:登录账号密码等敏感信息存Session;记住登录、个性化偏好配置存入Cookie;浏览器禁用Cookie时可通过URL拼接sessionId。

8. HashMap 高频面试

8.1 JDK7与JDK8底层数据结构

  • JDK1.7:数组+单向链表
  • JDK1.8:数组+链表+红黑树
  • 树化规则:链表长度≥8 并且 数组容量≥64时链表转为红黑树;链表长度≤6红黑树退回链表

8.2 初始化容量16、扩容2倍、负载因子0.75原因

  1. 容量2的幂次:(数组长度-1)&hash等价取模运算,效率远高于%;扩容后元素只落在原下标/原下标+旧容量,减少数据迁移
  2. 负载因子0.75:平衡存储空间占用与哈希冲突概率,过小浪费内存、过大链表过长查询变慢

8.3 JDK7多线程扩容死循环,JDK8解决原理

JDK7采用头插法,多线程扩容时链表倒置形成环形链表,get()无限循环死锁;JDK8改为尾插法,保留原有链表顺序,彻底规避死循环。

9. ConcurrentHashMap 底层原理

  • JDK7:分段锁Segment,16个分段各自加ReentrantLock,锁粒度大、内存开销高
  • JDK8:取消分段,采用CAS + synchronized锁住数组桶头节点,锁粒度更细、并发性能更强

不用Lock原因:JDK1.6后synchronized经过偏向锁/轻量级锁优化,性能对标Lock,占用内存更小。

二、多线程 & 并发(面试重中之重)

1. 线程六大生命周期状态

NEW(新建)→RUNNABLE(就绪/运行)→BLOCKED(阻塞抢锁失败)→WAITING(无限等待wait)→TIMED_WAITING(限时sleep/wait(time))→TERMINATED(线程终止)

2. 禁止使用Executors创建线程池原因

Executors工具类创建的线程池存在OOM风险:

  1. newCachedThreadPool:无上限创建线程,高并发瞬间线程爆炸
  2. newFixedThreadPool/newSingleThreadExecutor:无界阻塞队列,任务无限堆积占满堆内存

生产规范:手动通过ThreadPoolExecutor自定义线程池参数

3. 线程池七大参数 + 四类拒绝策略

七大参数:核心线程数、最大线程数、空闲存活时间、时间单位、阻塞队列、线程工厂、拒绝策略

  1. AbortPolicy(默认):直接抛出任务满异常
  2. CallerRunsPolicy:让提交任务的主线程执行任务(生产最稳妥)
  3. DiscardPolicy:默默丢弃新任务不报错
  4. DiscardOldestPolicy:丢弃队列头部最早未执行任务

4. 线程池核心线程数配置

  • CPU密集型(大量计算):CPU物理核心数,减少上下文切换
  • IO密集型(DB/第三方接口调用):CPU核心数×2,等待IO时复用线程

5. sleep()与wait()核心区别

  1. sleep():Thread静态方法、不释放同步锁、时间到自动唤醒、任意代码块都能调用
  2. wait():Object成员方法、释放同步锁、必须写在synchronized同步块内、需要notify/notifyAll手动唤醒

6. volatile关键字作用

保证变量可见性、禁止指令重排序,无法保证原子性

  • 内存屏障强制修改立即刷入主内存,其他线程实时读取最新值
  • i++分为读取、修改、写入三步非原子操作,volatile无法解决并发i++安全问题

7. synchronized锁升级流程

无锁 → 偏向锁(单线程无竞争)→ 轻量级自旋锁(少量竞争)→ 重量级Monitor锁(大量线程争抢)

8. synchronized和Lock区别

  • synchronized:JVM底层实现,代码执行完毕/异常自动释放锁、不可中断、只支持非公平锁、无法精准唤醒指定线程
  • Lock:API手动实现,必须finally手动unlock释放锁、支持可中断、可选公平/非公平锁、多Condition精准唤醒指定等待线程

9. CAS乐观锁原理与缺点

原理:比较内存当前值与预期值,一致则更新数据,不一致自旋重试
缺点:ABA数据篡改问题、无限自旋空耗CPU、仅支持单个变量原子更新;ABA解决方案:添加版本号/时间戳

10. ThreadLocal原理与内存泄漏

  • 每个线程自带独立ThreadLocalMap,存储线程私有数据
  • Map初始容量16,扩容阈值2/3,哈希冲突采用线性探测向后找空位(HashMap是拉链法)
  • 泄漏原因:key是弱引用可被GC回收,value是强引用常驻内存;用完必须手动remove()释放

11. JUC三大工具类场景

  1. CountDownLatch:等待全部子任务执行完毕,主线程再继续执行(多分片报表汇总)
  2. CyclicBarrier:凑齐设定数量线程统一同时放行,支持循环复用
  3. Semaphore:信号量控制同一时间并发线程数(接口限流)

三、JVM 核心面试题

1. JVM运行时数据区

线程私有:程序计数器(唯一不会OOM区域)、虚拟机栈、本地方法栈
线程共享:堆(对象实例存放,GC主要区域)、方法区(JDK8改为元空间,使用本地物理内存)

JDK8废除永久代PermGen,彻底解决永久代OOM问题

2. 四种引用类型

  • 强引用:new创建对象,GC永远不会回收
  • 软引用:堆内存空间不足时才触发回收(缓存实现)
  • 弱引用:每次GC触发必回收(ThreadLocal的key)
  • 虚引用:仅用来追踪对象回收状态

3. 双亲委派模型

加载类时子类加载器优先向上委托父加载器加载,顶层启动类加载器无法加载再由子类加载;作用:防止JDK核心类被自定义类篡改、避免类重复加载。

4. 线上故障排查实战

  1. CPU占满100%:top查进程 → top -Hp定位耗CPU线程 → 转16进制 → jstack排查死循环代码
  2. 频繁FullGC:jmap导出堆dump文件,MAT工具分析大对象/内存泄漏

四、MySQL高频核心(索引+事务+锁)

1. InnoDB与MyISAM区别

  • InnoDB:支持事务、行级锁、MVCC、崩溃故障恢复、聚簇索引,生产业务表首选
  • MyISAM:表级锁、不支持事务、查询速度快,静态文章、只读归档表使用

2. ACID与四大隔离级别

ACID:原子性(undo日志回滚)、一致性、隔离性(MVCC实现)、持久性(redo刷盘落地)
隔离级别:读未提交→读已提交→**可重复读(InnoDB默认,间隙锁防幻读)**→串行化

3. 索引三大核心:组合索引、回表、覆盖索引

3.1 组合索引(联合索引)

多个字段联合创建索引,严格遵循最左前缀原则,查询条件缺失最左侧首字段,索引失效。
例:索引idx(phone,id,name),where id=?无法走索引。

3.2 回表

二级索引只存储主键值,根据主键再去聚簇索引查询完整行数据,额外IO开销即回表。

3.3 覆盖索引

查询需要的所有字段全部包含在联合索引中,无需回表查询原表,性能最优。
场景:select id,name from user where phone='138xxxx',索引(phone,id,name),命中覆盖索引。

4. 索引失效常见场景

  1. like左模糊%xxx
  2. 索引字段做运算、调用函数
  3. 字符串数字隐式类型转换
  4. or条件一侧字段无索引
  5. 违背最左前缀原则
  6. !=、is not null大概率不走索引

5. B+树适配MySQL原因

  • 非叶子节点只存索引键,树层级少、磁盘IO次数少
  • 叶子节点双向链表串联,范围查询、排序性能极强

五、Spring / SpringBoot 高频

1. IOC与DI

IOC控制反转:对象创建管理权从业务代码转交Spring容器,不再手动new;
DI依赖注入:容器自动注入Bean依赖,实现代码解耦。

2. AOP动态代理

  • JDK动态代理:目标类实现接口时使用
  • CGLIB代理:目标无接口,继承子类实现代理
    落地场景:接口日志记录、权限校验、全局异常、事务管理

3. 三级缓存解决单例setter循环依赖

  1. 一级缓存singletonObjects:完整初始化完毕的成品Bean
  2. 二级缓存earlySingletonObjects:半成品提前暴露Bean
  3. 三级缓存singletonFactories:ObjectFactory工厂,解决AOP代理提前暴露

仅支持单例+setter注入;构造注入、多例Bean无法解决循环依赖

4. @Transactional事务失效高频场景

  1. 方法非public修饰
  2. 同类内部方法互相调用(A方法无注解调用带@Transactional的B)
  3. 业务异常被try-catch捕获吃掉,无法触发回滚
  4. 事务传播属性配置错误
  5. 默认只回滚RuntimeException,非运行异常不回滚

5. Bean完整生命周期

实例化 → 属性填充赋值 → 各类Aware接口回调 → BeanPostProcessor后置处理 → @PostConstruct → init-method → 容器使用 → @PreDestroy → destroy销毁

六、Redis高频面试(必考100%)

1. 五大基础数据类型+落地场景

  • String:短信验证码、接口PV计数器、商品库存
  • Hash:用户基础信息、购物车数据
  • List:简易消息队列、用户浏览历史
  • Set:用户好友、抽奖去重、共同好友交集
  • ZSet:排行榜、延时任务队列

2. Redis高性能四点原因

  1. 全部基于内存操作
  2. 命令执行单线程无锁竞争(6.0仅IO多线程)
  3. IO多路复用模型处理客户端连接
  4. 底层高效简约数据结构

3. 缓存三大经典问题

3.1 缓存穿透(查询数据库不存在的数据,频繁打DB)

方案:布隆过滤器拦截非法key、空值缓存、接口参数合法性校验

3.2 缓存击穿(热点key瞬间过期,大量请求击穿到DB)

方案:热点key永不过期、互斥锁、分布式锁

3.3 缓存雪崩(大批量key同一时间过期/Redis宕机,全量请求落库压垮DB)

方案:过期时间随机±30分钟打散、Redis集群高可用、接口限流熔断

4. Redisson分布式锁原理

可重入锁、看门狗自动续期防死锁、Lua脚本原子加解锁;集群环境采用红锁方案规避主从切换丢锁。

5. Redis持久化

  • RDB:定时全量快照,恢复速度快,可能丢失最后一段时间数据
  • AOF:逐条记录写指令,数据安全,文件体积大
  • 生产推荐:RDB+AOF混合持久化

七、分布式 & MQ高频

1. CAP & BASE理论

CAP:分布式系统分区容错P必存在,只能在一致性C/可用性A中二选一(CP/AP架构);
BASE:基本可用、软状态、最终一致性,互联网分布式主流设计思想。

2. 分布式事务五种方案

  1. 强一致:2PC、TCC、Seata AT(项目最常用无侵入方案)
  2. 最终一致:本地消息表、RocketMQ事务消息

3. MQ核心面试

作用:系统解耦、异步处理、流量削峰

  • 防消息丢失:生产者confirm确认、消息队列持久化、消费者手动ACK
  • 防重复消费:业务幂等设计(唯一索引/Redis去重)
  • 消息积压:扩容消费者、批量消费
  • 死信队列:消息超时、业务异常拒收、队列塞满转入死信

八、大厂高频实战场景题(面试加分)

  1. 秒杀商品超卖如何解决?
    Redis预扣库存 + Redisson分布式锁 + 接口限流 + 数据库最终落地扣减

  2. 订单超时自动关闭
    RabbitMQ 死信队列/延时插件 ;
    RocketMQ延迟消息
    定时任务轮询
    Kafka时间轮
    Redisson 延时任务
    RDelayedQueue 通过「普通队列存消息 + ZSet 存到期时间索引」,依靠 Lua 原子脚本轮询过期索引实现分布式延时任务,天然防重复消费、支持持久化。

  3. 高并发接口优化思路
    多级缓存、异步化MQ、限流熔断、数据库索引优化、读写分离、分库分表、SQL 优化:禁止 select *、大事务,缩短事务持有连接时长、线程池资源隔离

  4. SQL慢查询优化
    explain执行计划分析、合理建联合+覆盖索引、规避索引失效、优化分页、缩小事务、读写分离
    第一阶段:发现慢查询(定位问题)
    开启慢查询日志(基础) 在 MySQL 配置中开启慢查询日志,并设置合理的阈值(如 500ms 或 1s)。
    slow_query_log = 1
    slow_query_log_file = /var/log/mysql/slow.log
    long_query_time = 1 # 超过1秒记录
    log_queries_not_using_indexes = 1 # 记录没走索引的SQL
    第二阶段:诊断慢查询(分析原因)
    找到慢 SQL 后,核心武器是 EXPLAIN(执行计划)。在 SQL 前面加上 EXPLAIN 执行,重点看以下几个字段:

  5. 看 type(访问类型,决定生死)
    type 反映了 MySQL 是如何查找数据的,性能从好到坏依次为:

system > const > eq_ref > ref > range > index > ALL
优化目标:至少要达到 range 级别,最好是 ref。如果看到 ALL(全表扫描) 或 index(全索引树扫描),这就是慢的根本原因,必须优化。
2. 看 key 和 rows(索引与扫描行数)
key:实际使用的索引。如果是 NULL,说明没走索引。
rows:MySQL 预估需要扫描的行数。这个值越大,查询越慢。
3. 看 Extra(额外信息,暗藏杀机)
Using filesort(文件排序):说明 MySQL 无法利用索引完成排序,必须在内存或磁盘中额外进行排序。极度消耗 CPU,必须优化。
Using temporary(使用临时表):常见于 GROUP BY 或 DISTINCT,MySQL 需要建临时表来存放中间结果。非常慢,必须优化。
Using index(覆盖索引):好现象!说明查询的列都在索引里,不需要“回表”查原数据。
Using where:说明在存储引擎检索行后再进行了过滤,通常意味着索引没完全发挥作用。
4. 其他诊断手段
是不是等锁了? 用 SHOW ENGINE INNODB STATUS 查看是否有长事务未提交,导致当前 SQL 在等待行锁。
是不是网络/连接池问题? 如果 SHOW PROFILE 显示 SQL 执行很快,但接口响应慢,可能是连接池满了在等连接,或者网络延迟。
第三阶段:治疗慢查询(优化方案)
根据诊断结果,对症下药。以下是 5 种最常见的优化手段:

  1. 索引失效排查与修复(占 80% 的慢查询原因)
    明明建了索引,但 EXPLAIN 显示 type=ALL,通常是踩了以下“索引失效”的坑:

违背最左前缀法则:联合索引 (a, b, c),查询条件只有 b 和 c,索引失效。
在索引列上做运算/使用函数:WHERE YEAR(create_time) = 2023 会失效。改为 WHERE create_time >= ‘2023-01-01’ AND …。
隐式类型转换:字段 phone 是 VARCHAR,但 SQL 写的是 WHERE phone = 13800000000(数字),会导致全表扫描。必须加引号!
LIKE 以通配符开头:WHERE name LIKE ‘%张’ 会失效。尽量改为 LIKE ‘张%’,如果必须左模糊,请使用 Elasticsearch。
OR 条件中有未加索引的列:WHERE a = 1 OR b = 2,如果 b 没索引,整个查询都会全表扫描。
2. 避免“回表”:使用覆盖索引
痛点:SELECT * FROM user WHERE age > 20。虽然 age 有索引,但 MySQL 找到 age 后,还需要拿着主键 ID 回到主键索引树去查 name、phone 等字段(这叫回表),非常耗时。
优化:坚决杜绝 SELECT !只查需要的字段。如果查询字段较少,可以建立联合索引包含这些字段。
例如:建立 (age, name) 索引,SQL 改为 SELECT age, name FROM user WHERE age > 20。此时 Extra 会显示 Using index,速度起飞。
3. 解决“深度分页”问题
痛点:SELECT * FROM orders ORDER BY id LIMIT 1000000, 10。MySQL 会扫描 1000010 行,然后丢弃前 100 万行,只返回 10 行。越往后翻越慢。
优化方案 A(延迟关联/子查询):先通过覆盖索引查出主键 ID,再 JOIN 原表。
SELECT t1.
FROM orders t1
INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 10) t2
ON t1.id = t2.id;
优化方案 B(游标法/记录上次 ID,🌟 最推荐):前端记住上一页最后一条记录的 ID,下次请求带上。
SELECT * FROM orders WHERE id > #{last_max_id} ORDER BY id LIMIT 10;
4. 优化 JOIN 和子查询
小表驱动大表:IN 和 EXISTS 的选择。
如果外表小、内表大,用 IN。
如果外表大、内表小,用 EXISTS。
子查询改写:MySQL 对子查询的优化较弱,尽量将 WHERE id IN (SELECT …) 改写为 JOIN。
5. 架构级降维打击(当 SQL 优化到极限时)
如果单表数据量达到千万级,或者查询逻辑极其复杂,不要死磕 MySQL:

读写分离:主库负责增删改,从库负责复杂的 SELECT。
引入缓存:将热点数据放入 Redis,挡住 90% 的读请求。
异构数据源:复杂的全文检索、多条件组合筛选,把数据同步到 Elasticsearch;海量的报表统计、聚合分析,把数据同步到 ClickHouse 或 Doris。
第四阶段:预防慢查询(建立规范)
优化是事后补救,预防才是最高境界。

研发规范约束
强制禁止使用 SELECT *。
强制要求表必须有主键,且尽量使用自增 ID(防止页分裂)。
字段尽量设置为 NOT NULL 并给默认值(NULL 值会影响索引和统计)。

总结 Checklist(排查时直接对照):
是否开启了慢查询日志?
是否使用了 EXPLAIN 查看执行计划?
type 是不是 ALL 或 index?
是否发生了隐式类型转换?
联合索引是否满足最左前缀?
是否存在 Using filesort 或 Using temporary?
是否存在深度分页 (LIMIT 100万, 10)?
是否存在大事务导致的锁等待?
是否还在使用 SELECT *?
5. 接口幂等性方案
全局唯一令牌、数据库唯一约束、业务状态机、Redis去重
6. 三级缓存
构造器注入产生的循环依赖能解决吗?
答:不能。构造器注入是在实例化阶段(new A(B))就产生依赖,而 Spring 的三级缓存是在实例化后、属性注入阶段解决循环依赖,因此构造器循环依赖会直接抛出 BeanCurrentlyInCreationException。
多例(Prototype)Bean 通过 setter 注入产生的循环依赖能解决吗?
答:不能。多例 Bean 每次getBean()都会新建对象,Spring 不会对多例 Bean 使用三级缓存缓存,因此循环依赖时会无限递归创建对象,最终抛出异常。
若只有一级缓存(map1),能解决循环依赖吗?
答:从解决循环依赖的角度能执行,但使用过程会有问题。若成品和半成品都存在 map1 中,当 A 依赖 B、B 又依赖 A 时,A 在半成品阶段被 B 引用,后续 A 初始化完成后属性可能不完整,导致空指针或逻辑错误。
若只有一级、二级缓存(map1+map2),能解决循环依赖吗?
答:在无 AOP 代理的情况下可以解决;有 AOP 时无法解决。
无 AOP:实例化 A→放入 map2(半成品)→A 依赖 B→实例化 B→B 依赖 A 时从 map2 取 A 的半成品→B 初始化完成放入 map1→A 注入 B 后完成初始化→A 放入 map1,流程通顺。
有 AOP:A 的代理对象需要在初始化后生成,若只有 map1(成品)和 map2(半成品),B 在属性注入时拿到的是 A 的半成品(无代理),后续 A 生成代理后,B 引用的还是旧对象,导致代理不一致。
三级缓存的核心作用是什么?
答:解决 “循环依赖” 与 “AOP 代理” 的兼容性问题。
无循环依赖时:保证 Bean 的代理对象在初始化最后阶段生成,遵循 Spring “初始化完成后生成代理” 的设计原则。
有循环依赖时:通过 ObjectFactory 提前暴露 Bean 的早期引用(若有 AOP 则直接生成代理对象),确保循环依赖的 Bean 能拿到正确的代理引用。
循环依赖中,AOP 代理对象何时生成?
答:在 BeanPostProcessor#after 阶段生成。若存在循环依赖,会提前通过三级缓存的 ObjectFactory 生成代理对象;若不存在循环依赖,则在初始化完成后生成,且 AOP 源码会判断是否已生成过代理,避免重复创建。
只有一级缓存和三级缓存能解决循环依赖吗?
当 Bean A 依赖 Bean B,Bean B 又依赖 Bean A 时,B 在获取 A 的早期引用时,只能从三级缓存的工厂反复生成新的引用(而非复用同一引用),会导致循环引用的 Bean 不是同一实例,最终引发逻辑错误或空指针异常。
14. spring的bean是线程安全的吗?
实际上⼤部分时候 spring bean ⽆状态的(⽐如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean有状态的话(⽐如 view model 对象),那就要开发者⾃⼰去保证线程安全了,最简单的就是改变 bean 的作⽤域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
如果要保证线程安全
1.可以将bean的作用域改为prototype,比如像Model View。
2.采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量,每个线程只操作自己的副本变量。
15. 接口幂等性怎么做?
定义
同一个请求,执行一次和重复执行多次,对系统数据状态、返回结果完全一致,不会产生脏数据、重复业务。
六种落地实现方案
数据库唯一索引 / 底层约束(最强兜底)
业务唯一字段建立唯一键,重复插入直接抛出唯一冲突异常,从数据库层面拦截重复请求。
前端一次性Token机制(防表单重复提交)
- 原理:前端先调用接口申请一次性Token,提交业务请求时携带Token;后端校验Token有效后立即删除Token,再执行业务逻辑。
- 适用场景:页面表单提交、订单创建。
分布式锁(通用高并发方案)
- 原理:以orderId/assetId/userId等全局唯一业务标识作为Redis锁Key;拿到锁的线程执行业务,未获取到锁直接返回「处理中」,拒绝重复操作。
- 工具:Redisson可重入锁。
业务状态机控制(业务层优雅实现)
- 原理:业务状态单向流转、不可逆,更新SQL强制携带前置状态条件;已完成状态无法再次变更,拦截重复更新。
- 示例:订单「待支付→已支付→已发货」,无法重复支付。
数据库乐观锁(version版本号)
- 原理:数据表新增version版本字段,每次更新version = version + 1,更新条件带上当前版本号;版本不匹配代表已被其他线程修改,更新失败。
MySQL特有语法:Insert on Duplicate Key Update
插入时命中唯一索引冲突,自动执行更新逻辑,适用于统计、库存类写入场景。

  1. 接口响应慢的原因及完整排查优化方案
    一、数据库层(80%慢请求根源)
    原因A:慢SQL / 索引失效
  • 排查手段:开启MySQL慢查询日志,explain分析执行计划;重点观察type=ALL全表扫描、Extra包含Using filesort/Using temporary
  • 优化方案:
    1. 合理创建联合索引、覆盖索引,遵循最左前缀原则;
    2. 禁止索引字段运算、函数调用、隐式类型转换;
    3. 杜绝select *,只查询业务所需字段减少回表。

原因B:超大事务、事务嵌套

  • 问题:事务内部包含RPC、HTTP、发MQ、复杂内存计算,长期占用数据库连接池。
  • 优化:事务瘦身,所有非DB操作移出@Transactional,数据库提交放在链路最后一步。

原因C:行锁/间隙锁等待、死锁

  • 排查:SHOW ENGINE INNODB STATUS查看当前锁、死锁日志。
  • 优化:固定多表资源访问顺序避免死锁;隔离级别从RR降至RC减少间隙锁;缩小锁粒度。

原因D:N+1循环查询

  • 问题:for循环内单次循环查询数据库,大量IO消耗。
  • 优化:改用in批量查询、联表join、MyBatis批量映射。

二、应用代码层(逻辑、并发、线程池问题)
原因A:串行调用多个下游接口

  • 场景:同步调用A/B/C三个第三方接口,每个200ms,串行总耗时600ms。
  • 优化:使用CompletableFuture并行异步调用,总耗时等于最慢单个接口耗时。

原因B:线程池参数配置不合理

  • 问题:核心线程过小、无界队列堆积任务、线程耗尽触发拒绝策略;无隔离池导致非核心业务拖垮核心链路。
  • 优化:区分CPU/IO密集型配置参数;采用舱壁模式,不同业务使用独立线程池。

原因C:频繁Full GC,STW停顿卡顿

  • 场景:代码产生大量短生命周期大对象,老年代快速占满,频繁Full GC造成接口周期性超时。
  • 优化:Arthas/jmap导出dump文件,MAT定位内存泄漏;调整新生代比例,替换低延迟收集器G1/ZGC。

原因D:正则表达式回溯爆炸

  • 场景:复杂嵌套贪婪正则,匹配特定字符串时时间复杂度指数级增长。
  • 优化:简化正则、关闭多层贪婪匹配;设置正则执行超时时间。

三、缓存与中间件层
原因A:缓存三大经典问题(穿透/击穿/雪崩)

  1. 缓存穿透(查询不存在数据,全部打DB):布隆过滤器拦截、缓存空值短期过期;
  2. 缓存击穿(热点key过期,并发打库):Redisson互斥锁、逻辑永不过期;
  3. 缓存雪崩(大批量key同时过期/Redis宕机):过期时间随机±30min打散、集群高可用、接口限流熔断。

原因B:Redis大Key、热Key阻塞主线程

  • 问题:Hash/List存储几十万条数据,单条操作阻塞Redis单命令线程;热点Key单机读写压力巨大。
  • 优化:大Key分片拆分;scan替代keys;热Key叠加本地二级缓存Caffeine、Redis节点分片打散。

四、网络与容器基础设施层
原因A:未复用HTTP连接,频繁握手

  • 问题:每次调用下游接口新建TCP/TLS连接,三次握手损耗大量耗时。
  • 优化:OkHttp/Apache HttpClient连接池,开启Keep-Alive长连接复用。

原因B:K8s容器CPU节流 Throttling

  • 问题:容器limitsCPU配额设置过小,进程被内核频繁限流,CPU跑不满但接口很慢。
  • 优化:合理调整容器requests/limits资源配置,监控cpu_throttled指标告警。

五、架构级终极优化(单点优化无效时使用)

  1. 多级缓存:本地Caffeine缓存 → Redis分布式缓存 → MySQL,读请求层层拦截;
  2. 异步削峰:短信、日志、积分、通知等非核心链路丢入MQ异步处理,主链路直接返回;
  3. 读写分离CQRS:写走主库,复杂报表、多维度查询走从库/ES/ClickHouse;
  4. 限流熔断降级:Sentinel/Resilience4j,下游服务慢时快速失败、返回兜底数据,防止雪崩。

六、线上慢接口标准排查SOP

  1. 看监控:SkyWalking/Pinpoint/Prometheus定位瓶颈(DB慢、RPC慢、代码计算慢);
  2. 看日志:应用异常日志、MySQL慢SQL日志、GC停顿日志;
  3. 看线程jstack/Arthas thread命令,区分线程WAITING阻塞、RUNNABLE死循环;
  4. 看服务器资源top/vmstat/iostat检查CPU、内存、磁盘IO、网络带宽是否打满。

17. 什么是RWA?
RWA = Real World Assets。就是把现实世界中有价值的资产(如黄金、房产、应收账款、设备收益权等),通过区块链技术进行数字化确权,变成链上的代币(Token)或数字凭证,从而实现更高效、透明的发行和交易。

18. 为什么要做这个平台?(传统痛点 vs RWA优势)

  • 传统痛点
    • 流动性差:实物资产(如大型设备、房产)或非标资产(如供应链应收账款)很难分割,交易门槛高,流转慢。
    • 信任成本高/确权难:依赖大量中介(律所、审计、评估机构),尽调成本高,且存在信息不对称、数据易被篡改的风险。
    • 清结算效率低:跨机构、跨国界的资产流转涉及复杂的对账和清算,周期长。
  • RWA的优势(平台价值)
    • 资产碎片化/高流动性:把大资产拆分成小份额,降低投资门槛。
    • 可信与透明(防篡改):利用区块链不可篡改的特性,将资产的确权、估值、流转全生命周期上链,降低信任成本。
    • 智能合约自动执行:通过代码(智能合约)自动执行分红、清算等逻辑,提高清结算效率。
  1. 平台的解决方案与核心价值
    “针对这些痛点,我们的 RWA 平台提供了完整的解决方案,创造了三个核心价值:

首先是‘可信存证,降低信任成本’。 我们结合了物联网(IoT)数据和区块链。设备的运行数据、资产的权属证明,在生成时就计算哈希并上链(Merkle Root存证)。利用区块链不可篡改的特性,实现了穿透式监管,让资金方可以实时、可信地查看底层资产状态,省去了大量中介尽调成本。

其次是‘资产碎片化,提升流动性’。 通过智能合约,我们将大额的实物资产或收益权拆分成标准化的数字份额(Mint/铸币),降低了投资门槛,让资产能够在合规的前提下快速流转(Transfer)。

最后是‘智能合约自动清结算’。 资产存续期间的派息、分红、赎回,全部由链上智能合约自动执行,资金清结算从原来的 T+N 天缩短到了实时到账,极大提升了资金周转率。”

  1. 结合业务场景升华(展现你的业务深度,非常加分!)
    “具体结合业务场景来看,有庞大的供应链体系和物流仓储网络。 我们这个平台,实际上是把生态内的‘沉睡资产’(比如物流设备的收益权、上下游企业的应收账款)盘活了。通过 RWA 平台把这些资产打包上链,不仅为中小微企业拓宽了融资渠道(供应链金融),也为机构投资者提供了透明、合规的优质资产标的。

九、面试精简背诵总结

  • 想要提速:多线程
  • 并发防脏数据:本地锁/分布式锁
  • 查询SQL慢大概率:回表、没使用覆盖索引
  • 缓存故障:穿透、击穿、雪崩
  • 事务失效高频:同类内部调用、非public、异常被catch
  • 高并发架构核心:缓存+异步MQ+限流+集群
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘右今

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值