跳转到主要内容

面试题库

Java 高频必过面试题

String/集合/HashMap 高频题 + MySQL + 框架 + 项目话术,共 94 题。

  • Java
  • 面试

Java 高频必过面试题(2026)

来源:resource/御码IT教育Java必过面试题-v2026.03.24.pdf

特性

String

StringBuffer

StringBuilder

可变性 不可变(final) 可变 可变 线程安全 安全(无修改) 安全(方法加 synchronized) 不安全(无锁) 性能(修改操作) 极低(每次 new) 中等(锁开销) 极高(无锁)

版本

核心结构

备注

JDK1.7 数组 + 单向链表 所有冲突元素都挂在链表上 JDK1.8 数组 + 单向链表 + 红黑树 链表长度≥8 且数组长度≥64 时,链表转红黑树

御码IT教育Java高频面试题-2026版

一、Java

1.String的常用方法

2.String, StringBuffer,StringBuilder区别

不修改用 String,修改且多线程用 StringBuffer,修改且单线程用 StringBuilder。 JDK5 + 后,String 的 + 拼接会被编译器优化为 StringBuilder,循环内拼接仍建议手动用 StringBuilder(避免每次循环 new 对象)

3.HashMap的原理(jdk1.7, jdk1.8实现区别)

1.7 是数组 + 链表,1.8 新增红黑树(链表≥8 且数组≥64 时转换),解决查询性能退化;

"abc".length();   // 返回字符串长度 → 3
"abc".charAt(1);  // 获取指定索引的字符  → 'b'
"".isEmpty()      // 判断字符串是否为空(长度 0)→ true
"  ".isBlank()    // 判断是否为空 / 仅含空白字符 → true
"abc".getBytes("UTF-8")  // 将字符串转字节数组(可指定字符编码,也可不指定使用默认)
"abc".contains("bc")     // 判断是否包含指定子串  → true
"abc".indexOf("c")       // 返回子串首次出现的索引,无则返回-1    → 2
"abac".lastIndexOf("a")  // 返回子串最后出现的索引  → 2
"abc".startsWith("ab")   // 判断是否以指定前缀开头  → true
"abc".endsWith("c")      // 判断是否以指定后缀结尾 → true
"123".matches("\\d+")    // 判断是否匹配正则表达式  → true
"abc".substring(1)       // 从指定索引截取到末尾    → "bc"
"abc".substring(0,2)     // 截取 [begin,end) 区间 → "ab"
"a".concat("b")          // 拼接字符串   → "ab"(等价于 +,但效率略高)
String.join(",", "a","b")      // 静态方法,按分隔符拼接 → "a,b"
"  abc  ".trim()         // 去除首尾空白字符(不含全角空格) → "abc"
" abc ".strip()         // (jdk11+)去除首尾所有空白(含全角) → "abc"
"abc".toUpperCase()      // 转大写 → "ABC"
"ABC".toLowerCase()      // 转小写 → "abc"
"abac".replace("a","x")  // 替换所有指定子串 → "xbxc"
"a1b2".replaceAll("\\d","")    // 按正则替换 → "ab"
"a,b,c".split(",")       // 按正则分割成数组 → ["a","b","c"]
"abc".equals("ABC")      // 判断内容是否相等(区分大小写) → false
"abc".equalsIgnoreCase("ABC")  // 忽略大小写判断相等 → true

1.8 哈希计算简化、链表插入改为尾插法、扩容无需重新计算 hash,性能和稳定性提升; 两者均非线程安全,1.7 头插法扩容易出环形链表,1.8 尾插法规避该问题,但仍不建议多线程使 用。

4.ConcurrentHashMap的底层实现(jdk1.7, jdk1.8实现区别)

1.7 用分段锁(Segment+ReentrantLock),粒度粗;1.8 用 Node+CAS+synchronized 节点锁, 粒度极细,并发性能大幅提升; 1.7 是 Segment 数组嵌套 HashMap,1.8 直接复用 HashMap1.8 的数组 + 链表 / 红黑树,结构 更简洁; 1.7 分段扩容,1.8 多线程协助整体扩容,效率更高。 1.7 支持并发最大Segment数(默认16),理论上无上限(节点级锁)

5.流的使用

list.stream().filter(n->n%2==0)   // 过滤元素 → 过滤出偶数 [2,4]
list.stream().map(n->n*2)           // 元素转换 → [2,4,6,8]
List<List<Integer>>lists=Arrays.asList(Arrays.asList(1,2),
Arrays.asList(3,4)); lists.stream().flatMap(Collection::stream) //扁平化映射(将每个
元素转为Stream后合并)→ [1,2,3,4]
Arrays.asList(1,2,2,3).stream().distinct() // 去重(基于 equals ())  → [1,2,3]

6.多线程的实现几种方式

继承 Thread 类、实现 Runnable 接口、实现 Callable 接口(JDK5+),前两者无返回值,后者有 返回值且可抛异常 线程池(ExecutorService),基于 Runnable/Callable 封装,兼顾性能和可管理性 Thread 受单继承限制,Runnable 灵活,Callable 支持返回值,线程池解决线程复用问题。

7.线程的生命周期

NEW 新建态 创建Thread 实例→ NEW,未调用start() RUNNABLE 可运行态 调用start() 后进入,包含 “就绪” 和 “运行中” 两个细分状态 BLOCKED 阻塞状态 竞争 synchronized 锁失败→ BLOCKED;抢到锁→ RUNNABLE WAITING 等待状态

调用Object.wait() / Thread.join() / LockSupport.park()→ WAITING;
其他线程调用Object.notify() / LockSupport.unpark()→ RUNNABLE

需其他线程主动唤醒 TIMED_WAITING 超时等待状态

调用Thread.sleep(long) / Object.wait(long) / Thread.join(long)→

TIMED_WAITING;超时 / 被唤醒→ RUNNABLE 有时间限制的等待,超时后自动唤醒 TERMINATED 终止状态 RUNNABLE 状态下run() 执行完成 / 抛出未捕获异常→ TERMINATED

8.两种单例模式

饿汉(立即加载,线程安全)

list.stream().sorted()           // 自然排序(元素需实现 Comparable)  → [1,2,3,4]
list.stream().sorted(Comparator.reverseOrder())  // 自定义排序 → [4,3,2,1]
list.stream().limit(2)    // 限制返回前 n 个元素 → [1,2]
list.stream().skip(2)     // 跳过前 n 个元素 → [3,4]
list.stream().filter(n->n%2==0).collect(Collectors.toList())   //收集流元素为集合
→ [2,4]
Collectors.toSet()
Collectors.toMap(...)
Collectors.groupingBy(..)  // 分组
list.stream().forEach(System.out::println) // 遍历每个元素执行操作  → 打印 1、2、3、4
list.stream().filter(n->n>2).count()       // 返回元素个数 → 2
list.stream().max(Integer::compare)        // 求最大值 → Optional[4]
list.stream().min(Integer::compare)        // 求最小值 → Optional[1]
list.stream().filter(n->n%2==0).findFirst() // 获取第一个元素  → Optional[2]
list.parallelStream().findAny()            // 获取任意一个元素返回 Optional  → 随机

返回一个元素

list.stream().anyMatch(n->n>3)             // 判断是否存在符合条件的元素 → true
list.stream().allMatch(n->n>0)             // 判断所有元素是否符合条件 → true
list.stream().noneMatch(n->n>5)            // 判断是否无元素符合条件 → true

懒汉 (双检锁)

9.Lambda表达式

必须基于「只有一个抽象方法」的接口(如Runnable 、Consumer 、Predicate 、Function ),这

是 Lambda 能生效的核心条件(JDK8 用@FunctionalInterface 注解标识)。 简化 Runnable

publicclassSingletonHungry {

// 1. 私有静态常量:类加载时直接初始化唯一实例(饿汉式核心)

privatestaticfinalSingletonHungryINSTANCE=newSingletonHungry();

// 2. 私有构造方法:禁止外部new创建实例

privateSingletonHungry() {}

// 3. 公有静态方法:返回唯一实例

publicstaticSingletonHungrygetInstance() {
returnINSTANCE;
}
}
publicclassSingletonLazy {
// 1. 私有静态变量:volatile禁止指令重排(关键),初始为null
privatestaticvolatileSingletonLazyINSTANCE;
// 2. 私有构造方法:禁止外部new
privateSingletonLazy() {}

// 3. 公有静态方法:双重校验锁实现懒加载+线程安全

publicstaticSingletonLazygetInstance() {

// 第一层校验:避免每次调用都加锁(提升性能)

if (INSTANCE==null) {

// 加锁:保证多线程下仅一个线程进入创建实例

synchronized (SingletonLazy.class) {

// 第二层校验:防止多个线程等待锁后重复创建

if (INSTANCE==null) {
INSTANCE=newSingletonLazy();
}
}
}
returnINSTANCE;
}
}

// 传统方式(匿名内部类)

newThread(newRunnable() {
@Override
publicvoidrun() {
System.out.println("传统线程");
}
}).start();

// Lambda简化(无参数,单行代码)

newThread(() ->System.out.println("Lambda线程")).start();

接口名称

核

心

方

法

方法签

名

使用

Consumer<T>

消 费 者 入参 T, 无返回 值

Consumer<String> ps= System.out::println;
ps.accept("Hello, World!")
Supplier<T>

供 应 商 无入 参,返 回 T

Supplier<String> sup= () -> "Hello, World!";
String result = sup.get();
Function<T,R>

函 数 入参 T, 返回 R

Function<String, Integer> toLength =
String::length; int length =
toLength.apply("Hello, World!");
Predicate<T>

断 言 入参 T, 返回 boolean

Predicate<String> isEmpty = String::isEmpty;
boolean result = isEmpty.test("");

简化集合遍历(forEach + Consumer) 简化 Stream 流操作(filter/map 等)

10.四个内置函数式接口

11.四种方法引用赋值

List<String>list=Arrays.asList("a", "b", "c");

// 传统方式

for (Strings : list) {
System.out.println(s);
}
// Lambda简化
list.forEach(s->System.out.println(s));
// 进一步简化(方法引用):list.forEach(System.out::println)
List<Integer>numList=Arrays.asList(1,2,3,4,5);
// Lambda实现:过滤偶数 -> 乘以2 -> 求和
intsum=numList.stream()
.filter(n->n%2==0) // 单参数省略括号
.map(n->n*2)         // 单行代码省略大括号/return
.reduce(0, (a,b) ->a+b);
System.out.println(sum); // 输出:2+4 → 4+8=12

方法引用类型

格式

核心特点

示例

静态方法引用 类名::静态方法 名 无实例依赖,直接引用类方 法 Integer::parseInt 实例方法引用 对象::实例方法 名 依赖特定对象的实例方法 System.out::println 特定类型实例方法引 用 类名::实例方法 名 任意该类型对象的实例方法 String::length 构造方法引用 类名::new 引用构造方法,创建对象 ArrayList::new

12.异常类都有哪些

Throwable接口: Error(系统错误) Exception (异常) RuntimeException(运行时异常,非受检) NPE、数组 / 字符串越界 CheckedException(编译时异常,受检) IO 异常、数据库异常、类型转换异常 处理核心:RuntimeException 侧重预防,CheckedException 必须显式捕获 / 声明,Error 无需 处理。

13.什么是CopyOnWriteArrayList

CopyOnWriteArrayList 是并发包下线程安全的 ArrayList,核心是「写时复制」 写操作加锁复制新数组,读操作无锁访问原数组; 优点:读操作高性能、线程安全、迭代器不抛并发修改异常; 缺点:写操作性能低、内存开销大、仅保证最终一致性; 适用场景:读多写少的并发场景,不适合写频繁或强一致性要求的场景

14.集合应该如何遍历,在遍历过程中操作元素有没有安全问题,如何解决安全问

题

遍历方式: 普通 for 循环: 通过索引遍历 增强 for 循环(for-each):语法糖,底层基于迭代器实现 迭代器遍历(Iterator)通过集合的iterator() 获取迭代器,支持手动控制遍历、删除 forEach 方法(JDK8+,函数式):基于 Lambda 表达式,底层仍依赖迭代器 Stream 流遍历(JDK8+): 需要结合过滤、转换等操作的遍历,支持并行遍历 (parallelStream) 安全问题:ConcurrentModificationException(并发修改异常)

类加载器

加载范围

特点

启动类加载器 JRE/lib/rt.jar(核心类库) 由 C++ 实现,无 Java 对象 扩展类加载器/平台类加载 器 JRE/lib/ext/*.jar(扩展类库) Java 实现,父类是 Bootstrap 应用程序类加载器 类路径(classpath)下的类 Java 实现,父类是 Extension 自定义类加载器 自定义路径的类(如加密字节 码) 继承 ClassLoader,按需加 载 快速失败(fail-fast):遍历前记录修改次数(modCount),遍历中检测到 modCount 变化 则抛异常 非线程安全集合在多线程下遍历 + 修改,除了抛异常,还可能导致数据错乱、数组越界 普通 for 循环删除的坑:下标遍历删除元素会导致后续元素前移,漏遍历 解决: 迭代器的 remove 使用线程安全集合:CopyOnWriteArrayList / CopyOnWriteArraySet / ConcurrentHashMap 加锁访问

15.JVM的内存模型

程序计数器:记录当前线程执行的字节码指令地址(行号),线程切换时恢复执行位置 虚拟机栈:局部变量表、操作数栈、方法返回地址、动态链接、附加信息。可通过-Xss 调整栈大 小 本地方法栈:为 Native 方法(如 JNI 调用 C/C++ 方法)提供执行空间 堆:对象实例、数组。可通过-Xms (初始堆大小)、-Xmx (最大堆大小)调整 方法区 / 元空间:类名、方法、字段、常量池、静态变量、即时编译器编译后的代码等;可通过-

XX:MetaspaceSize 、-XX:MaxMetaspaceSize 调整。(JDK8 关键变化:永久代替换为元空间

(使用本地内存),解决永久代内存溢出问题)

16.类的加载机制

类从被加载到 JVM 内存中,到卸载出内存,整个生命周期包含加载、验证、准备、解析、初始化 5 个 核心阶段 类加载器分类: 双亲委派模型(核心规则) 逻辑:类加载时,先委托父加载器加载,父加载器加载失败(找不到类),子加载器才自己加载; 优势:避免类重复加载,保证核心类(如java.lang.String )不被篡改(沙箱安全);

17.JVM的调优,结合实际开发说说情况

电商核心接口频繁 Full GC

定位:用jmap -dump:format=b,file=heap.hprof导出堆快照,通过 MAT 工具分析,发

现大量OrderVO 对象未被回收,排查代码发现订单查询接口中,一个 HashMap 缓存了全量 订单数据(百万级),且未设置过期时间,导致老年代被占满; 方案: 代码优化: ①将 HashMap 缓存替换为 Redis,设置 10 分钟过期时间,仅缓存热点订单; ②优化查询逻辑,分页查询订单数据,避免一次性加载百万级数据到内存。 参数调优: ①调整堆内存:-Xms8g -Xmx8g (初始 / 最大堆一致) ②调整新生代:-Xmn4g (新生代占堆 50%,减少 Minor GC 频率)

③切换 GC 收集器:-XX:+UseG1GC -XX:MaxGCPauseMillis=200 (控制 GC 停顿时

间) 效果: Full GC 频率从 5 分钟 / 次降至 1 天 / 次,Minor GC 频率降至 1 次 / 分钟; 接口响应时间恢复至 50ms 以内,高峰期无超时。

大数据同步任务 OOM

定位:分析堆快照:发现大量DataEntity 对象堆积,排查代码发现:同步任务中,读取数 据库数据后存入ArrayList ,未分批处理,一次性加载 500 万条数据到内存。处理完的数据 未及时置为 null,导致对象被线程局部变量引用,无法被 GC 回收。 方案: 代码优化: ①分批读取数据库(每次 5000 条),处理完一批后清空 List 并手动触发

System.gc() (仅离线任务建议)

②处理完的数据变量及时置为 null,解除引用 参数调优: ①调整堆内存:-Xms16g -Xmx16g (离线任务允许更大堆内存)

②开启 G1GC 的大对象直接进入老年代:-XX:G1HeapRegionSize=16m -
XX:G1MaxNewSizePercent=60

效果: 任务稳定运行 8 小时无 OOM,堆内存使用率稳定在 60% 左右,Full GC 仅触发 2 次

Tomcat 服务启动慢,元空间溢出

定位:应用集成了大量第三方 jar 包,动态生成类较多代理类。Tomcat 启动耗时 10 分钟 +偶尔抛出OutOfMemoryError: Metaspace。元空间默认大小仅 256M,大量类加载导致元 空间不足,触发频繁 Full GC(元空间不足会触发 Full GC),启动时加载类过多,且 ParallelGC 的 GC 停顿时间长。 方案: 参数调优:

①调整元空间大小:-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m

②切换 GC 收集器为 G1:减少启动时 GC 停顿时间

③开启类卸载:-XX:+CMSClassUnloadingEnabled -
XX:+UseCMSInitiatingOccupancyOnly

部署优化: ①移除无用的第三方 jar 包,减少类加载数量。 效果: 启动时间从 10 分钟降至 2 分钟,元空间溢出问题彻底解决。 总结: 先代码,后参数:80% 的 JVM 问题是代码导致的,调参数只是 “治标”,优化代码才是 “治 本”; 监控先行:无监控不调优,必须通过 GC 日志、堆快照、性能监控工具定位瓶颈; 循序渐进:每次只改 1~2 个参数,对比效果,避免一次性改多个参数无法定位有效项; 场景适配:高并发接口优先 G1GC(低延迟),离线任务优先 ParallelGC(高吞吐量),超 大堆(16G+)考虑 ZGC。

18.自定义线程池

定义:

参数:

corePoolSize:线程池常驻的最小线程数,即使空闲也不会销毁,除非设置

allowCoreThreadTimeOut=true

maximumPoolSize:线程池允许创建的最大线程数,核心线程 + 非核心线程的总数上限 keepAliveTime:非核心线程空闲超过该时间则销毁,释放资源 unit:keepAliveTime的时间单位(如 SECONDS、MILLISECONDS) workQueue:核心线程满时,新任务进入队列等待 ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。 SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们 PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。 DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取 元素 LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类 似,还含有非阻塞方法。 LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。 threadFactory:创建线程的工厂,可自定义线程名称、优先级、是否守护线程等 handler:拒绝策略:任务队列满 + 最大线程数已满时,处理新任务的策略 AbortPolicy(默认):直接抛出RejectedExecutionException 异常,终止任务提交 CallerRunsPolicy:由提交任务的线程(如主线程)直接执行该任务,降低任务提交速度 DiscardPolicy:静默丢弃新任务,无异常、无提示 DiscardOldestPolicy:丢弃队列中最旧的任务(队列头部),将新任务加入队列

执行流程:

  1. 线程池初始化时,核心线程不会立即创建,而是等待任务提交后才创建(可通过
prestartCoreThread() / prestartAllCoreThreads() 预创建);
  1. 非核心线程执行完任务后,空闲时间超过keepAliveTime 会被销毁;
  2. 任务队列的优先级高于非核心线程(核心线程满→先入队列,队列满→再创建非核心线程,非核心 满→拒绝策略)

19.synchronized和ReentranLock区别

// 自定义线程池核心示例

ExecutorServiceexecutor=newThreadPoolExecutor(
2,   // 核心线程数
5,   // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS,   // 时间单位
newArrayBlockingQueue<>(10),        // 任务队列(有界)
Executors.defaultThreadFactory(),    // 线程工厂
newThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

对比维度

synchronized

ReentrantLock

核心实现 JVM 的内置锁(关键字) JDK 显式锁(java.util.concurrent.locks 包) 锁类型 非公平锁 默认非公平锁,可手动设置为公平锁 可中断性 不可中断 可中断 超时获取 锁 不支持 支持(tryLock) 等待 / 唤 醒 只能通过 wait ()/notify (),随机唤醒 线程 支持多个 Condition,精准唤醒指定线程 可重入性 支持 支持 释放方式 自动释放 必须手动释放(finally 中调用 unlock ()) 锁状态获 取 无法获取 可获取(isLocked ()、getHoldCount () 等)

20.volatile关键字

用于修饰成员变量 / 静态变量,核心作用是保证变量的「可见性」和「有序性」,但不保证原子性,是 JMM(Java 内存模型)的核心实现之一。 可见性 ①对 volatile 变量的写操作:强制将工作内存的新值同步到主内存; ②对 volatile 变量的读操作:强制从主内存读取最新值,禁用工作内存缓存; 有序性(禁止指令重排) 通过「内存屏障」禁止 volatile 变量相关指令的重排,保证指令执行顺序与代码顺序一致,单例模 式必须加 volatile 不保证原子性 volatile 修饰的变量执行「读 - 改 - 写」操作(如i++ )时,多线程下仍会数据错乱;

解决:原子性需通过synchronized 、Lock 或AtomicInteger (CAS)实现。

使用场景:①状态标记位②单例模式 DCL(双重校验锁)③保证变量的可见性(替代锁)

21.CAS是什么

CAS(Compare And Swap)—— 比较并交换,CPU 层面的原子指令(非 Java 语法),通过原子操作 替代锁,避免线程上下文切换的开销。 核心参数:①内存地址②预期值③新值 执行逻辑:内存地址V的值==预期值A ?将V的值更新为B,返回true :不更新,返回false 整个过程是CPU层的原子性,无锁;更新失败,会通过「自旋」(循环重试)再尝试,直到成 功。

Java 通过sun.misc.Unsafe 类调用 CAS 指令,java.util.concurrent.atomic 包均基于 CAS

实现 优缺点:

优点

缺点

无锁,开销远小于 synchronized ABA 问题(值被修改后又改回,CAS 误判) 原子性,无需担心线程安全 自旋重试导致 CPU 占用高 适用于低冲突场景 只能保证单个变量的原子性

模式

核心逻辑

典型应用

独占模 式 同一时间仅一个线程获取 锁 ReentrantLock、ReentrantReadWriteLock(写 锁) 共享模 式 同一时间多个线程可获取 锁 CountDownLatch、Semaphore、 ReentrantReadWriteLock(读锁)

22.AQS是什么

AQS 是 Java 并发包的核心抽象类,是 ReentrantLock、CountDownLatch、Semaphore 等并发工具 的底层实现框架,封装了 “等待队列 + 同步状态” 的核心逻辑

核心设计:

同步状态(state) 用volatile int state 表示同步状态(state=0 表示无锁,state>0 表示重入次数) 基于 CAS 修改 state,保证状态更新的原子性。 CLH 等待队列(双向链表) 当线程获取锁失败时,会被封装为「节点」加入队列尾部,阻塞等待; 队列遵循 FIFO(先进先出),只有队列头节点能竞争锁。 tryAcquire(int arg) :独占模式获取锁(ReentrantLock 重写);

tryRelease(int arg) :独占模式释放锁;

tryAcquireShared(int arg) :共享模式获取锁(CountDownLatch 重写);

tryReleaseShared(int arg) :共享模式释放锁。

23.ThreadLocal实现原理

ThreadLocal(线程本地变量)是 Java 提供的线程隔离工具。 核心:ThreadLocal 的核心是「ThreadLocalMap」,每个Thread对象都有一个 ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map 中,其中键就是ThreadLocal,值可以是任意类型。 源码: set get getMap 内存泄漏问题:

  1. ThreadLocalMap 的 Entry 中,key 是 ThreadLocal 的弱引用(GC 时若 ThreadLocal 无强 引用,key 会被回收),但value 是强引用;
  2. 若 ThreadLocal 使用后未调用remove() ,key 被回收后,value 仍被线程的 ThreadLocalMap 强引用,导致 value 无法被 GC,最终内存泄漏; 解决泄露:
  3. 使用后必须调用 remove ()
  4. 避免使用静态 ThreadLocal(减少强引用持有时间);
  5. 线程池场景:线程复用前可通过线程池的afterExecute 钩子方法进行清理。

二、Mysql

1.sql优化

开发设计 ①建表使用合理的存储引擎,使用合理的列类型,创建适当的索引以加速检索 ②大表的分库分表的合理分割 查询语句

publicvoidset(Tvalue) {
Threadt=Thread.currentThread();
// 根据获取当前线程的Map,ThreadLocal是key, value是参数
ThreadLocalMapmap=getMap(t);
if (map!=null)
map.set(this, value);
else
createMap(t, value);
}
publicTget() {
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if (map!=null) {
ThreadLocalMap.Entrye=map.getEntry(this);
if (e!=null) {
@SuppressWarnings("unchecked")
Tresult= (T)e.value;
returnresult;
}
}
returnsetInitialValue();
}
ThreadLocalMapgetMap(Threadt) {
returnt.threadLocals;
}

索引类

型

底层

结构

核心特点

适用场景

B + 树 索引 B + 树 ①所有数据存在叶子节点,按顺序排列②支 持范围查询、排序; ③ MySQL 默认索引类型 绝大多数查询场景 哈希索 引 哈希 表 ①基于哈希值快速匹配,等值查询极快;② 不支持范围查询、排序 仅等值查询场景 全文索 引 倒排 索引 ①针对文本内容的关键词检索; ②支持模糊匹配(MATCH AGAINST) 文本检索场景(如商品描 述、文章内容) R- Tree 索引 R 树 ①针对空间数据(如经纬度); ②支持空间范围查询 地理空间数据

索引类型

核心特点

示例

聚簇索引 ①索引与数据行存储在一起(叶子节点就是数据 行); ②每张表仅能有 1 个; ③ InnoDB 的主键索引就是聚簇索引

PRIMARY KEY (id)

非聚簇索 引 ①索引与数据行分离(叶子节点存储主键值); ②可创建多个; ③查询需回表(通过主键查数据行)

INDEX idx_age
(age)

①使用具体列名代替* ②执行计划分析通过explain查看SQL执行计划(重点看type 、key 、rows 、Extra 字段)避 免索引失效,③分页查询结合where+limit ④小表驱动大表join查询(join的字段必须建索引,避免超过3表的join) 开启慢查询日志,改慢查询时间为50ms,捕获执行时间超过阈值的 SQL 清理无效索引,监控锁的状态 数据库配置优化 ①调整连接池参数:max_connections,wait_timeout ②优化缓存配置设置为物理内存50%~70% ③读多写少则开启查询缓存query_cache

2.索引是啥,mysql都有哪些索引

索引是 MySQL 中用于加速查询的特殊数据结构(本质是 B + 树 / 哈希表等) 索引类型 聚簇/非聚簇 索引分类

索引类

型

核心定义

示例 & 注意事项

主键索 引 ①唯一标识数据行; ②聚簇索引; ③不允许 NULL,且唯一

PRIMARY KEY (id) ;每张表仅 1 个

唯一索 引 ①索引列值唯一; ②允许 NULL(多个 NULL); ③非聚簇索引

UNIQUE idx_phone (phone) ;避免重复

数据 普通索 引 ①最基础的索引; ②无唯一性约束; ③非聚簇索引

INDEX idx_age (age) ;加速普通查询

联合索 引 ①包含多个字段的索引; ②遵循「最左匹配原则」

INDEX idx_age_name (age, name) ;
匹配age 、age+name ,不匹配name

前缀索 引 ①对字符串字段的前 N 个字符创建索 引; ②减少索引占用空间

INDEX idx_name (name(10)) ;

适用于长字符串(如姓名、地址)

3.B+树的特点

B + 树核心特点: ①数据仅在叶子节点,非叶子节点只存索引; ②叶子节点有序链表连接,支持高效范围查询; ③多路平衡,高度低(IO 少); ④非叶子键值重复,节点调整简单; 核心优势:适配磁盘 IO、范围查询高效、读写性能平衡,是 MySQL InnoDB 索引的最优选择; 与 B 树的核心区别:数据存储位置、叶子节点链表、范围查询效率,B + 树更适配数据库场景。

4.索引失效

WHERE 中字段使用函数 / 运算 使用OR 连接无索引字段

LIKE '%xxx' (左模糊)

隐式类型转换(如字符串字段传数字)

NOT IN / NOT EXISTS

违反最左原则

5.联合索引(a,b,c) 的失效

查询 SQL

索引匹配情况

执行计划关键标识

SELECT * FROM user WHERE a=1

完全匹配(a) Using index condition

SELECT * FROM user WHERE a=1 AND
b=2

完全匹配(a,b) Using index condition

SELECT * FROM user WHERE a=1 AND
c=4

部分匹配(a) c筛选 Using index condition Using where

SELECT * FROM user WHERE b=2 AND
c=4

完全失效 Using where(全表扫描)

SELECT * FROM user WHERE a=1 AND
b>2 AND c=4

匹配 (a,b) c筛 选 Using index condition Using where

隔离级

别

核心特点

解决的问题

存在的问题

读未提 交 RU 能读取其他事务未提交的数据 无 脏读、不可重复读、幻 读 读已提 交 RC 只能读取其他事务已提交的数据 脏读 不可重复读、幻读 可重复 读 RR 事务内多次读取同一数据,结果一致 (MySQL 默认) 脏读、不可重复 读 幻读(InnoDB 通过 MVCC 解决) 串行化 事务串行执行,完全隔离 脏读、不可重复 读、幻读 性能极低(并发差)

6.事务ACID

A(Atomicity):原子性 “要么全成,要么全败”,基于Undo Log(回滚日志)实现 C(Consistency):一致性 “数据始终合法” ,事务前后,数据库的业务规则和数据约束始终保持 一致(如主键唯一、余额不为负、转账总金额不变) I(Isolation):隔离性“事务之间互不干扰”,多个并发执行的事务之间相互隔离,避免并发导致 数据错乱 D(Durability):持久性 “提交后永不丢失”,事务一旦提交(COMMIT ),会永久保存到数据库 中。

7.事务隔离性和传播

RC和RR基于MVCC(多版本并发控制)+ 锁实现: MVCC:为每行数据创建多个版本,不同事务读取不同版本,避免加锁阻塞; 锁机制:行锁(针对单行数据)、表锁(针对全表)、间隙锁(防止幻读)。

传播级别

核心语义

典型案例

REQUIRED(默

认)

①有事务则加入当前事务; ②无事务则新建事务 下单接口(调用库存扣减), 共用一个事务

REQUIRES_NEW

①无论是否有事务,都新建独立事 务; ②原有事务挂起,新事务执行完后恢 复原有事务 下单成功后记录日志,即使日 志插入失败,不影响下单事务 提交

SUPPORTS

①有事务则加入; ②无事务则以非事务方式执行 订单详情查询,有事务则复 用,无则非事务执行

NOT_SUPPORTED

①有事务则挂起; ②始终以非事务方式执行 导出订单报表,避免长时间占 用事务导致锁等待

MANDATORY

①必须在已有事务中执行; ②无事务则抛异常 (IllegalTransactionStateException) 订单状态更新,必须在下单 / 支付的事务中执行

NEVER

①必须以非事务方式执行; ②有事务则抛异常 纯内存操作(如缓存更新), 避免事务开销

NESTED

①有事务则创建嵌套子事务(保存 点); ②无事务则新建事务 批量下单(一个订单失败,仅 回滚该订单,不影响其他订 单)

8.MVCC的工作原理

MVCC(Multi-Version Concurrency Control,多版本并发控制)是 InnoDB 引擎实现行级锁的补充机 制,核心是:为每行数据维护多个版本,不同事务读取不同版本的数据,实现 “读不加锁、读写不冲突” —— 既保证了并发读的性能,又避免了读写阻塞。

读操作

类型

定义

是否使用

MVCC

快照读 普通SELECT (不加锁),读取数据的历史版本 ✅是 当前读

加锁读(SELECT ... FOR
UPDATE / INSERT / UPDATE / DELETE ),读取最新版本

❌否 InnoDB 的读操作分为两种,MVCC 仅作用于「快照读」:

  1. 快照读(核心流程) 事务执行普通SELECT 时,MVCC 通过「版本链 + Read View」筛选可见版本
1. trx_id == create_trx_id : 可以访问这个版本(自己创建的)
  1. trx_id < min_trx_id :该版本由已提交的事务修改,可见;
  2. trx_id > max_trx_id :该版本由未来事务修改,不可见;
4. min_trx_id ≤ trx_id ≤ max_trx_id :

若trx_id 在m_ids 中(事务未提交),不可见; 若trx_id 不在m_ids 中(事务已提交),可见。 2. 写操作(版本链更新) 事务执行INSERT / UPDATE / DELETE 时,MVCC 维护版本链:

INSERT :新行的DB_TRX_ID 设为当前事务 ID,DB_ROLL_PTR 为 null;

UPDATE :先将旧数据写入 Undo Log,再更新新数据的DB_TRX_ID ,并将DB_ROLL_PTR 指 向旧版本; DELETE :本质是 “标记删除”,将旧数据写入 Undo Log,新数据的DB_TRX_ID 设为当前事 务 ID,标记为删除。

MVCC 与隔离级别的关联(面试高频)

隔离

级别

Read View 生成时机

MVCC 效果

读未 提交 无 Read View,直接读最新版本 能看到未提交事务的数据(脏读),不使用 MVCC 读已 提交

每次 SELECT 都生成新的 Read

View

每次读都能看到已提交的最新版本,MVCC 生效 但仅保证 “读已提交” 可重 复读

事务中第一次 SELECT 生成 Read

View,后续复用

事务内多次读同一数据,版本一致,MVCC 核心 场景 串行 化 加表锁,不使用 MVCC 完全串行执行,无并发读优化

9.事务@Transactional失效的情况

同一 Bean 内非事务方法调用事务方法(内部调用); 事务方法修饰符非 public; 异常被捕获且未重新抛出,或异常类型非默认回滚类型(仅 RuntimeException/Error 默认回 滚); 目标类未被 Spring 容器管理; 引擎不支持事务; 传播级别配置为 NOT_SUPPORTED/NEVER; 事务方法内多线程执行数据库操作,新线程脱离原事务上下文。

10.mysql的各种锁和锁的内容

颗粒度:

表锁:锁表,alter table 行锁: 记录锁: 锁一行主键 间隙锁:不锁行锁间隙 临键锁:记录+间隙

乐观悲观:

乐观锁和悲观锁 悲观: select …from … where … for update… 锁当前行-> update … commit; 释放锁 乐观锁: 列version=1, select version , update … set …,version=2 where id=xx and version=1

是否共享:

共享锁(读锁)和排他锁(写) 核心锁信息表(高频使用):

performance_schema.data_locks :实时显示当前持有的所有锁(行锁、表锁等),包含锁类

型、锁模式、锁定对象、关联事务等核心信息,MySQL 8.0 主推的锁监控表;

performance_schema.data_lock_waits :显示当前等待锁的事务信息,关联等待的锁与持有的

锁,定位锁等待 / 阻塞问题。

11.mysql慢查询优化

先定位:

①开启慢查询日志(slow_query_log=1 )
②通过slow_query_log_file 或sys.schema_unoptimized_queries 筛选慢 SQL
③用EXPLAIN 分析执行计划,重点看type 、key 、rows 、Extra 。

优化: ①优先优化索引(避免失效、遵循最左匹配、新增 / 调整索引) ②优化 SQL 语句(减少扫描范围、避免SELECT * 、优化 JOIN / 分页) ③优化数据库层(分库分表、读写分离、缓存)。

12.mysql 聚合函数

max, min, sum, avg, count

13.mysql行专列

14.mysql多表连接方式

INNER JOIN:两表交集行,是最常用的连接方式,默认可简写为JOIN ; LEFT JOIN:返回左表所有行,右表匹配不到则补 NULL,保证左表数据完整性; RIGHT JOIN:返回右表所有行,左表匹配不到则补 NULL,可等价转换为 LEFT JOIN(调换表顺 序),实际使用较少; FULL JOIN:MySQL原生不支持,需通过LEFT JOIN UNION RIGHT JOIN 实现; CROSS JOIN:返回两表笛卡尔积,无连接条件时触发,需谨慎使用(通常加 WHERE 过滤)。

15.InnoDB中一定有主键吗

InnoDB 表一定有主键,即使未手动创建,InnoDB 也会自动生成隐藏的聚簇索引(行 ID)作为主键; 主键生成规则(优先级): 优先使用用户手动定义的PRIMARY KEY ; 若无显式主键,选择第一个非空唯一索引(UNIQUE NOT NULL)作为聚簇索引; 若无上述索引,InnoDB 自动生成 6 字节的隐藏行 ID(DB_ROW_ID )作为隐式主键,行 ID 自增

16.深度分页的优化

SELECT
name,
MAX(IF(subject = '语文', score, 0)) AS语文,
MAX(IF(subject = '数学', score, 0)) AS数学,
MAX(IF(subject = '英语', score, 0)) AS英语
FROM score
GROUPBY name;

利用主键 / 唯一索引的有序性,通过如WHERE id > 10000 LIMIT 10 ,直接定位起始位置,避免 全表扫描; 高频深度分页场景,将分页结果预计算存入 Redis / 临时表,查询时直接读取; 分库分表法:大表按分片键拆分,分页时先定位分片,再在分片内小范围分页;

三、框架

1.Spring MVC工作流程

业务之间使用转发方式跳转 ①客户端请求发送至DispatcherServlet (前端控制器,核心入口);

②DispatcherServlet 通过HandlerMapping (处理器映射器)找到匹配的Handler (Controller
方法)及HandlerInterceptor ;

③HandlerAdapter (处理器适配器)适配并执行Handler ,处理请求参数绑定、数据转换等; ④Handler 执行业务逻辑,返回ModelAndView (模型 + 视图名);

⑤DispatcherServlet 通过ViewResolver (视图解析器)将视图名解析为View 对象;

⑥View 渲染模型数据(Model)到视图,生成响应内容;

⑦DispatcherServlet 将响应返回客户端;

2.Spring boot的启动流程

  1. 初始化阶段:创建SpringApplication实例
①执行new SpringApplication(主启动类.class) ,触发构造器逻辑

②根据classpath是否存在web相关依赖去判定为传统web/ 响应式web/非web),从 org.springframework.boot.context.ApplicationContextInitializer.imports加载初始化器 ③加载监听器 ④推断主启动类,通过栈轨迹找到执行main 方法的类,作为核心配置类 ⑤自定义的启动参数在此阶段完成配置 2. 执行 run () 方法,启动 Spring 容器 ①启动全局计时器,记录整个启动过程的耗时,最终启动完成后可打印总耗时 ②加载系统环境变量、JVM 参数、命令行参数、配置文件(application.yml/properties)等;绑定 外部配置到环境中,支持多环境(dev/test/prod)的激活;触发EnvironmentPostProcessor 扩展 点,允许自定义扩展环境配置 ③打印启动 Banner:读取 classpath 下的banner.txt ④创建应用上下文,负责 Bean 的扫描、实例化和管理

⑤上下文前置初始化,执行第一步加载的所有ApplicationContextInitializer ,对刚创建的上

下文进行前置配置 ⑥发布启动事件,通过应用监听器发布ApplicationStartingEvent 事件,通知所有监听器 “应用 开始启动”

⑦加载 Bean 定义并刷新上下文

执行ApplicationContext.refresh() ,是启动流程的核心,包含步骤:

✅初始化 BeanFactory:创建DefaultListableBeanFactory ,作为 Bean 的工厂; ✅执行 BeanFactory 后置处理器(BeanFactoryPostProcessor):扫描主启动类及

@ComponentScan 指定的包,解析@Configuration / @Component / @Service 等注解,生成

BeanDefinition; ✅自动配置生效:通过@EnableAutoConfiguration 加载自动配置类,按需启用组件 ✅注册 Bean 后置处理器(BeanPostProcessor):用于 Bean 实例化后的增强(如 AOP、注解 注入) ✅初始化消息源、事件广播器等容器核心组件

依赖类型

是否能

解决

核心原因

单例 Bean + 属性注入 /setter 注入 ✅能 解决 三级缓存支持提前暴露未完全初始化的 Bean 引用 单例 Bean + 构造器注入 ❌不 能 构造器执行时 Bean 尚未实例化,无法提前暴露引用 原型(Prototype)Bean ❌不 能 原型 Bean 每次创建新实例,Spring 不缓存原型 Bean,无提前暴露基础 ✅实例化非懒加载的单例 Bean:执行 Bean 的 “实例化→属性填充(依赖注入)→初始化 (@PostConstruct/InitializingBean)→销毁方法注册” 全生命周期; ✅ Web 场景:初始化内嵌服务器(Tomcat/Jetty):创建并启动内嵌服务器,绑定端口(默认 8080) ⑧注册 JVM 关闭钩子:确保应用停止时(如 Ctrl+C、kill 命令),Spring 容器能优雅关闭,释放资 源 ⑨执行运行器:扫描容器中所有实现ApplicationRunner 的 Bean,按@Order 排序后执行run() 方法

3.Spring 解决循环依赖

Spring 解决循环依赖的核心是三级缓存机制,仅支持单例 Bean 的构造器之外的循环依赖(构造器循环 依赖无法解决) 一、哪些循环依赖能解决 Spring 通过DefaultSingletonBeanRegistry 中的三个 Map 实现三级缓存,各司其职:

  1. 一级缓存(singletonObjects):存放完全初始化完成的单例 Bean(最终可用的 Bean), key=Bean 名称,value=Bean 实例;
  2. 二级缓存(earlySingletonObjects):存放提前暴露的未完全初始化的 Bean 引用(已实例化、 未填充属性 / 执行初始化方法),临时缓存;
  3. 三级缓存(singletonFactories):存放Bean 的工厂对象(ObjectFactory),用于生成 Bean 的早期引用,核心是支持 AOP 代理的提前生成。

4.解释一下IOC

IOC(Inversion of Control,控制反转)本质是将对象的创建、依赖管理、生命周期控制等权力,从业 务代码中 “反转” 到 Spring 容器中,核心解决 “对象耦合过高” 的问题。 Spring IOC 容器的核心能力: 1.Bean 的实例化:通过@Component / @Service / @Bean 等注解,告诉容器需要创建哪些对象 2.依赖注入:自动解析 Bean 之间的依赖关系,将依赖对象注入到目标 Bean 3.生命周期管理:提供@PostConstruct (初始化)、@PreDestroy (销毁)等注解,统一管理 Bean 生命周期 4.Bean 的配置与扩展:支持通过配置类、注解、XML 等方式配置 Bean,还可通过 BeanPostProcessor 等扩展 Bean 的创建逻辑。

5.解释一个AOP

AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 核心特性之一,核心是将系统中重

复的通用逻辑(如日志、权限、事务)从业务代码中抽离,形成 “切面”,通过 “织入” 的方式动态植入

到业务方法的指定位置,实现 “业务逻辑与通用逻辑解耦”。 Spring 5.x 及以上默认规则:

特性

JDK 动态代理

CGLIB 代理

底层原理 基于接口实现(反射) 基于继承实现(ASM 字节码) 适用场景 目标类必须实现接口 目标类可无接口 代理对象 生成接口的实现类 生成目标类的子类 方法限制 仅代理接口中声明的方法 可代理目标类所有非 final 方法 性能 JDK8 + 后性能优化,与 CGLIB 接近 略高于 JDK 代理(无反射开销) ✅若目标类实现了接口→默认使用JDK 动态代理(基于接口生成代理类,代理类和目标类实现同一 接口); ✅若目标类未实现接口→自动切换为CGLIB 代理(基于继承生成代理类,代理类继承目标类)。

特殊配置:可通过@EnableAspectJAutoProxy(proxyTargetClass = true) 强制全局使用 CGLIB 代

理(无论是否实现接口)。

6.CORS如何处理

CORS(Cross-Origin Resource Sharing)是浏览器的安全策略,限制跨域请求,Spring 提供了多种层 级的解决方案: 全局配置(推荐,统一管控所有接口) 注解局部配置(细粒度控制单个接口 / Controller)

@Configuration
publicclassCorsConfigimplementsWebMvcConfigurer {
@Override
publicvoidaddCorsMappings(CorsRegistryregistry) {
registry.addMapping("/**") // 匹配所有接口
.allowedOriginPatterns("*") // 允许的跨域源(Spring 5.3+推荐用
originPatterns,替代allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求

方法

.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 是否允许携带Cookie(前端需配合
withCredentials=true)
.maxAge(3600);    // 预检请求(OPTIONS)的缓存时间,减少预检请求次

数

}
}

过滤器配置(适配 Spring WebFlux / 低版本 Spring,或需自定义过滤逻辑) 通过CorsFilter 手动注册过滤器,优先级最高(早于 DispatcherServlet),适用于特殊场景 (如整合网关、自定义过滤) 网关层配置(微服务场景,推荐)

7.微服务使用的技术栈都有哪些

Spring Boot + Spring Cloud Alibaba

// 对整个Controller的所有方法生效
@RestController
@CrossOrigin(origins="*", maxAge=3600)
@RequestMapping("/user")
publicclassUserController {
// 对单个方法生效(覆盖Controller的配置)
@CrossOrigin(allowedMethods= {"GET"})
@GetMapping("/{id}")
publicStringgetUser(@PathVariableStringid) {
return"用户"+id;
}
}
@Configuration
publicclassCorsFilterConfig {
@Bean
publicCorsFiltercorsFilter() {
CorsConfigurationconfig=newCorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
config.setMaxAge(3600L);
// 配置跨域匹配路径
UrlBasedCorsConfigurationSourcesource=new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
returnnewCorsFilter(source);
}
}
# Spring Cloud Gateway配置示例(application.yml)
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods: ["GET","POST","PUT","DELETE"]
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600

核心功能

具体职责

服务注册与发 现

  1. 服务提供者(Provider)启动时将自身信息(IP、端口、服务名)注册到 Nacos;
  2. 服务消费者(Consumer)从 Nacos 拉取服务列表,实现基于服务名的调 用;
  3. 实时感知服务上下线,动态更新服务列表(心跳检测 + 推送更新)。 配置中心
  4. 集中管理微服务配置(多环境、动态刷新);
  5. 服务启动时拉取配置,运行时可动态推送配置变更(无需重启服务);
  6. 支持配置版本管理、灰度发布、配置加密。 ①注册 / 配置:Nacos ②网关:Spring Cloud Gateway ③调用:OpenFeign / Dubbo ④熔断限流:Sentinel ⑤分布式事务:Seata ⑥消息队列:RocketMQ ⑦链路追踪:SkyWalking ⑧容器:Docker + K8s ⑨监控:ELK/ Prometheus + Grafana

8.Nacos负责什么,如果Nacos宕机,服务还能调用吗

Nacos 宕机后,服务还能调用吗?

①已完成服务发现的节点仍可正常调用 ②新启动 / 未缓存服务列表的节点无法调用;配置中心宕机不影响已加载配置的服务运行。

Nacos 高可用 + 降级方案?

① Nacos 高可用部署:生产环境需部署 Nacos 集群(至少 3 节点),避免单点宕机,集群模式下单 个节点宕机不影响整体服务; ②客户端缓存机制:Nacos 客户端默认将服务列表 / 配置缓存到本地文件,即使 Nacos 全宕机,重 启消费者仍可加载本地缓存的服务列表; ③服务调用降级:可结合 Sentinel 配置服务调用降级策略(如缓存兜底、返回默认值),避免因服 务列表失效导致调用完全失败。

9.Sentinel如何设置限流

Sentinel 是阿里开源的流量控制组件,核心是通过 “流控规则” 限制服务 QPS / 并发数,避免服务过 载。

配置限流规则:

阈值类型:选择 “QPS” 或 “并发数”; 单机阈值:如设置为 10(每秒最多 10 个请求); 流控模式: 直接:对当前接口限流(默认);

关联:关联接口触发限流(如/order/create 过载时限制/user/get );

链路:对指定链路限流(如仅限制从 A 方法调用该接口的流量); 流控效果: 快速失败:超出阈值直接返回限流提示(默认); Warm Up:预热限流(阈值从低到高逐步提升,适用于秒杀); 排队等待:超出阈值的请求排队等待(适用于平稳流量)。

场景

推荐算法

简单低精度限流 固定窗口 通用接口限流、防突刺 滑动窗口 / 令牌桶 严格控速、削峰填谷 漏桶 需支持突发流量的业务接口 令牌桶 核心配置方式:控制台配置 > 配置文件配置 > 注解配置(细粒度)> 硬编码(定制化); 限流核心维度:优先用 QPS 限流(适配大多数接口),资源密集型接口用并发数限流;

Sentinel和Gateway限流区别:

①作用域不同 Gateway:作用于网关入口层,控制进入微服务架构的外部流量,保护后端服务整体不被冲垮。 Sentinel:作用于应用内部或服务层级,可对具体接口、方法甚至热点参数进行精细化保护。 ②限流颗粒度不同 Gateway:通常按Route(路由)或IP/用户/API 分组限流,颗粒度较粗。 Sentinel:支持QPS、线程数、热点参数、调用来源、系统负载等多维度限流,颗粒度更细。 ③限流算法实现不同 Gateway:默认基于Redis 的令牌桶算法(如RedisRateLimiter ),适合分布式场景。

Sentinel

默认使用滑动时间窗口算法; 排队等待模式采用漏桶算法; 热点参数限流采用令牌桶算法 ④功能生态差异 Gateway:核心职责是路由与转发,限流是其附加能力。 Sentinel:是全链路流量控制框架,除限流外,还提供熔断、降级、系统保护、热点防护等能力

10.限流算法

11.Gateway鉴权是怎么做的

  1. 自定义 JWT 全局过滤器(最常用) 自定义全局过滤器实现轻量鉴权,适配大部分微服务场景:
  2. 先放行白名单接口(登录、注册、跨域预检 OPTIONS 请求等);
  3. 从请求头解析 Token,校验 JWT 的签名、过期时间;
  4. 合法则将用户信息(userId 等)透传到后端请求头,放行请求;非法则直接返回 401。
@Component
publicclassAuthFilterimplementsGlobalFilter, Ordered {
// 白名单接口
privatefinalList<String>WHITE_LIST=Arrays.asList("/auth/login",
"/auth/register");
@Override
publicMono<Void>filter(ServerWebExchangeexchange, GatewayFilterChain
chain) {
ServerHttpRequestrequest=exchange.getRequest();
  1. 集成 Spring Security OAuth2:适配 SSO/OAuth2 场景,无需自定义过滤器,自动完成令牌校验
  2. 远程调用统一鉴权中心:适配复杂鉴权场景,过滤器中把 Token 传给统一鉴权服务,远程调用校验 身份后,再决定是否放行

12.Seata的工作原理

Seata 是阿里开源的分布式事务框架,核心解决微服务跨服务的事务一致性问题,基于三角色架构 + 多 事务模式实现,默认使用无侵入的 AT 模式。 三个核心角色 TC(事务协调器):独立的 Seata 服务端,负责全局事务的状态管理,协调所有分支事务, 最终决议全局提交 / 回滚。 TM(事务管理器):全局事务的发起方(通常是入口微服务),负责开启全局事务,最终向 TC 发起全局提交 / 回滚请求。 RM(资源管理器):分支事务的参与方(各个业务微服务),负责向 TC 注册分支事务,执 行分支的提交 / 回滚,上报分支状态。 核心模式: AT 模式: Seata 的默认模式,对业务代码无侵入,基于本地事务 + undo log实现,分两阶 段执行: 一阶段:分支执行与注册

// 白名单直接放行
if (WHITE_LIST.contains(request.getPath().value())) return
chain.filter(exchange);
// 解析并校验Token
Stringtoken=request.getHeaders().getFirst("Authorization");
if (StrUtil.isBlank(token) ||!JwtUtil.verify(token)) {
// 非法请求直接返回401
ServerHttpResponseresponse=exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
returnresponse.setComplete();
}

// 透传用户信息给后端,后端无需重复解析Token

LonguserId=JwtUtil.getUserId(token);
ServerHttpRequest.Builderbuilder=
request.mutate().header("userId", String.valueOf(userId));
return
chain.filter(exchange.mutate().request(builder.build()).build());
}
@Override
publicintgetOrder() {
return-100; // 优先级最高,优先拦截非法请求
}
}
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://auth-server/.well-known/jwks.json # 授权中心公钥

地址

问题

定义

核心解决思路

缓存

穿透

查询不存在的数据,请求直接打到数据库 布隆过滤器、缓存空对象、参数校验

缓存

击穿

热点key过期瞬间,大量并发请求涌入数 据库 互斥锁、逻辑过期、永不过期+异步更新

缓存

雪崩

大量key同时过期或Redis宕机,导致数据 库压力剧增 随机过期时间、多级缓存、Redis高可 用、限流熔断 ① TM 向 TC 申请开启全局事务,TC 生成全局唯一的XID (全局事务 ID),XID 通过微 服务调用(如 Feign 请求头)透传到所有参与的分支服务,作为全局事务的唯一标识。 ②各 RM 执行本地业务 SQL 时,Seata 的代理数据源会自动拦截 SQL,生成: ③ RM 直接提交本地事务(一阶段就完成本地提交,无需阻塞等待全局决议),并向 TC 注册分支事务,上报分支状态为 “待决议” 二阶段:全局决议 所有分支执行完成后,TM 根据分支执行结果,向 TC 发起全局提交 / 回滚请求: ①全局提交:TC 通知所有 RM,直接删除对应的undo log 和全局锁即可(一阶段本地 事务已提交,无需额外操作),异步完成,性能极高。 ②全局回滚:TC 通知所有 RM,RM 通过之前生成的undo log 反向执行 SQL,把数据 恢复到事务执行前的状态,完成回滚。 TCC 模式:手动模式,用户自行实现 Try(预留资源)、Confirm(确认提交)、 Cancel(取消回滚),适配非数据库的事务场景。 SAGA 模式:长事务模式,通过状态机编排事务,适配长周期的分布式事务。 XA 模式:传统两阶段提交,基于数据库 XA 协议,强一致性但性能较低。

13.Seata你们项目中使用哪种

核心数据库事务:优先选 Seata AT 模式,无侵入开发快; 异步业务:选 RocketMQ 事务消息,解耦异步; 长事务 / 非数据库事务:选 SAGA/TCC; 低并发强一致场景:选 XA。

14.Redis的数据类型都有哪些

String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Sorted Set(有序集合)、 Bitmap(位图)、HyperLogLog、Geospatial(地理空间)、Stream(流)等。

15.Redis的缓存击穿,穿透,雪崩都如何解决

16.Redis的有没有事务,如何实现的

Redis有事务,但是事务不支持回滚,隔离性强,乐观锁通过WATCH实现,LUA是更灵活使用在原子操 作的场景。

1. 前置数据快照(`undo log`,回滚用的镜像数据)
  1. 行级全局锁(防止脏写,保证隔离性)

场景

推荐方案

数据可丢失,追求极致性能

关闭持久化(纯缓存)

需要快速恢复,允许丢失几分钟数据

仅 RDB

几乎不丢数据,接受稍慢的恢复

仅 AOF(everysec )

兼顾恢复速度和数据安全

混合持久化(RDB + AOF 同时开启) ①开启事务:客户端发送MULTI ,Redis返回OK 。 ②命令入队:客户端继续发送其他命令(如SET 、INCR 等),Redis将这些命令存入队列,不执行,

返回QUEUED 。

③执行事务:客户端发送EXEC ,Redis按顺序执行队列中的所有命令,并将结果返回给客户端。 ④放弃事务:客户端发送DISCARD ,清空队列并退出事务状态。

17.Redis如何实现持久化

两种主要方式:RDB(Redis DataBase)和AOF(Append Only File)。 RDB:生成某个时间点的快照。触发方式:手动(SAVE/BGSAVE),自动(配置save规则)。优 点:紧凑、恢复快、对性能影响小(BGSAVE fork子进程)。缺点:可能丢失最后一次快照后的数 据。 AOF:记录所有写操作命令,以追加形式写入文件。配置策略:always, everysec, no。优点:数 据安全性高(最多丢失1秒数据),可读性好,可修复。缺点:文件体积大,恢复慢。 混合持久化(Redis 4.0+):结合RDB和AOF,AOF重写时变为RDB格式+增量日志,兼顾恢复速 度和数据安全。

18.Redis的setnx和Redisson分布式锁的区别

Redis的SET NX 命令和Redisson框架都可以用来实现分布式锁,但它们在功能完备性、可靠性、使用 复杂性上有本质区别。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET user:1:name "Alice"
QUEUED
127.0.0.1:6379> INCR user:1:age
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (integer) 1

特性维度

原生 SET NX (基础实

现)

Redisson (成熟框架)

核心优势说明

原子性

现代用法通过SET key
value NX EX
seconds实现单命令原

子操作 所有复杂操作(加锁、解 锁、续期)均封装在Lua 脚本中,保证原子性 原生方案需注意避免 使用非原子的”先 SETNX后EXPIRE”组 合命令

锁续期

(Watchdog)

不支持,锁过期时间固 定,业务未完成则锁自 动失效 内置看门狗机制,自动为 持有中的锁续期(默认每 10秒续至30秒) Redisson解决了原生 方案最棘手的”锁超 时导致任务中断”问 题

可重入性

不支持,需自行实现计 数器逻辑 原生支持,同一线程可多 次获取同一把锁,内部通 过Hash结构记录重入次数 允许在递归或嵌套调 用场景中安全使用锁

锁释放安全

性

需手动组合UUID(唯一 标识)和Lua脚本,否 则可能误删其他线程的 锁 自动保障,通过Lua脚本 确保只有锁的持有者才能 释放锁 Redisson将复杂 的”校验+删除”原子操 作封装透明

高可用

单节点模式存在单点故 障风险 支持Redlock算法(多节 点独立Redis实例)和哨 兵/集群模式 Redisson可构建更可 靠的分布式锁服务

开发复杂度

高,需处理续期、可重 入、原子校验等边缘逻 辑 低,提供RLock接口, API与JUC的Lock接口类 似,开箱即用 Redisson大幅降低了 正确实现分布式锁的 门槛

性能 (单节

点)

较高,TPS约35,000次/ 秒 稍低,TPS约28,000次/ 秒,因涉及更多Redis操 作和后台线程 原生方案在极致性能 场景下略有优势

补充:Redlock 算法

无论是原生SET NX还是单节点的 Redisson 锁,在Redis主从架构或哨兵模式下,都存在一个理论上 的风险:客户端A在主节点加锁成功,数据还未同步到从节点时主节点宕机,从节点升级为主节点后, 客户端B可能获取到同一把锁,造成锁失效。 Redlock算法正是为了解决此问题而提出的。它不再依赖主从复制,而是同时操作多个相互独立的 Redis节点(官方推荐奇数个,如5个)。客户端会依次向所有节点申请锁,只有当超过半数节点(如 3/5)加锁成功,且总耗时小于锁有效期时,才认为获取锁成功。Redisson的RedissonRedLock实现 了该算法,提供了更高强度的分布式锁,但会牺牲一部分性能和可用性。

19.RabbitMQ的延时队列如何实现

RabbitMQ 实现延时队列主要有两种方式:

  1. TTL + 死信队列(DLX) 为队列设置消息存活时间(TTL),或为单条消息设置过期时间。 消息过期后会被路由到一个绑定了死信交换器(DLX)的“死信队列”。 消费者消费死信队列,从而实现延时。 缺点:过期时间只能设一个(若队列TTL),且不灵活;单消息过期需要额外插件或定制。
2. 延时消息插件(rabbitmq_delayed_message_exchange )

维

度

对称加密

非对称加密

密

钥

加密和解密使用同一把密钥 使用公钥(公开)和私钥(保密),公钥加密、 私钥解密;私钥签名、公钥验签。

速

度

运算量小,速度快,适合大量数据的 加密。 运算复杂,速度慢(比对称加密慢数百倍),通 常只用于密钥交换、数字签名或加密小数据。

安

全

性

密钥管理是最大风险,需通过安全信 道传输密钥,否则易泄露。 私钥不公开,公钥可任意分发,无需事先交换密 钥,安全性更高。

典

型

算

法

AES、DES、3DES、RC4、SM4 RSA、ECC(椭圆曲线)、DSA、SM2

主

要

用

途

数据加密(如文件加密、数据库加 密)、SSL/TLS 中实际传输数据的加 密。 数字签名(身份认证、防抵赖)、密钥协商(如 HTTPS 握手时交换对称密钥)、证书体系。

长

度

密钥较短(128/256 位即可)。 密钥较长(RSA 通常 2048/4096 位),安全性 基于大数分解或离散对数等数学难题。 安装插件后,创建x-delayed-message类型的交换器。 发送消息时通过x-delay头指定延迟毫秒数。 消息在交换器内暂存,到期后路由到目标队列。 优点:支持灵活的任意延时,实现简单。

20.RabbitMQ哪些情况消息会放到死信队列

  1. 消息被拒绝且不重新入队
消费者调用basic.reject或basic.nack ,并将requeue参数设为false时,会被路由到

死信交换器。 2. 消息过期(TTL) 为消息设置了expiration参数,且在队列中存活时间超过该值。 或队列设置了x-message-ttl ,消息在队列中超过该时间未被消费,过期后消息会进入死信 队列。 3. 队列达到最大长度或容量 队列设置了x-max-length (最大消息条数)或x-max-length-bytes (最大字节数),当新消息 导致队列溢出时,队列头部的消息(通常是未被消费的旧消息)会被丢弃或转发到死信队列。

21.对称加密和非对称加密的区别

凡是需要身份验证、防抵赖、安全交换密钥、或在不安全信道上建立信任的场合,都会优先使用非对称 加密。实际系统中通常与对称加密结合,发挥各自优势

22.CAP定理和BASE理论

CAP 定理和 BASE 理论是分布式系统设计的两个核心理论,分别从理论边界和实践方向指导系统架构。

  1. CAP 定理 ①C(一致性):所有节点在同一时间看到的数据是一致的(强一致性)。 ②A(可用性):每个请求都能收到非错误的响应,但不保证返回的是最新数据。 ③P(分区容错性):系统在网络分区(节点间通信中断)时仍能继续运行。 由于网络分区是分布式系统的常态(P 必须选),实际设计时往往在CP(牺牲可用性)和AP(牺 牲一致性)之间权衡: ① CP 系统:如 ZooKeeper、HBase,在分区发生时保证数据一致,但可能拒绝部分请求。 ② AP 系统:如 Cassandra、Eureka,分区时保证服务可用,但数据可能短暂不一致。
  2. BASE 理论,它放弃了强一致性,转而追求最终一致性: ①BA(基本可用):系统出现故障时,允许损失部分功能(如响应时间变长、降级页面),而非 完全不可用。 ②S(软状态):允许系统中存在数据中间状态(如副本同步中),且该状态不影响系统整体可用 性。 ③E(最终一致性):经过一段时间的同步后,所有副本最终会达到一致状态,而不要求实时一 致。
  3. CAP 与 BASE 的关系 ① CAP 定义了理论上限:在分区下,必须在 C 和 A 之间做取舍。 ② BASE 提供了实现路径:当选择 AP 时,用“最终一致性”替代“强一致性”,用“基本可用”保障高可 用,是 AP 系统的具体落地方式。 现代分布式系统(如微服务、NoSQL、云原生)大多基于 BASE 思想设计,在保证高可用的同时,通过 异步复制、补偿机制等手段实现数据的最终一致。

23.设计模式在项目中的应用

①单例:数据库连接池、线程池、日志记录器、配置管理类等需要全局唯一实例的场景 ②工厂:根据不同类型创建对象,解耦客户端与具体实现类。(支付渠道:支付宝,余额,微信) ③建造者:复杂对象的分步构建,避免大量构造参数。构建复杂的请求对象(如HttpRequest )、实 体类装配(Lombok @Builder ) ④适配器:使不兼容的接口协同工作,常用于第三方库集成或旧系统改造。日志框架(SLF4J)适配不 同日志实现 ⑤代理:控制对对象的访问,实现增强(如权限校验、日志、事务)(Spring AOP 基于动态代理实现 方法拦截;MyBatis 的 Mapper 接口通过代理生成实现类。) ⑥装饰器:动态扩展对象功能,比继承更灵活。(Java I/O 流(BufferedInputStream包装 FileInputStream );权限校验时动态添加角色校验功能) ⑦策略:算法或业务规则可动态切换,消除大量 if-else。(优惠券计算(多种折扣算法)、订单价格计 算(不同会员等级)、支付路由(根据金额或渠道选择策略)) ⑧观察者:对象间一对多依赖,状态变化时自动通知所有依赖。(事件驱动架构(Spring 事件监 听)、消息发布订阅(如消息队列)、前端 MVVM 数据绑定。) ⑨模板:定义算法骨架,将可变步骤交给子类实现。(数据同步流程(读取→转换→写入)抽象为模 板,不同数据源(MySQL、ES)实现具体步骤;JdbcTemplate 执行 SQL 的封装) ⑩责任链:多个对象依次处理请求,直到某个对象处理完毕。(Spring Security 过滤器链;请求参数 校验链(如非空、格式、业务规则);工作流审批环节。)

24.Mybatis的#{}和${}区别

对比项

#{} ${}

处理方

式

预编译占位符 (PreparedStatement) 直接字符串替换(拼接 SQL)

SQL

注入

安全,自动转义,防止 SQL 注 入 不安全,直接拼接,存在注入风险

引号处

理

自动为字符串类型添加单引号 不会添加引号,原样替换

适用场

景

传递参数值(where 条件、插 入值等) 动态表名、列名、order by 字段等结构无法使用 预编译的地方

性能

预编译可复用执行计划,性能 较好 每次 SQL 都重新解析,无法预编译

元素

作用

<if>

条件判断,满足条件则拼接 SQL 片段

<choose>/<when>/<otherwise>

类似 Java 的switch ,多选一

<where>

智能处理WHERE关键字,自动去除多余的AND / OR

<set>

智能处理SET关键字,自动去除多余的逗号

<foreach>

遍历集合,常用于IN条件或批量插入

<trim>

灵活修剪 SQL 片段,可替代 /

<bind>

从 OGNL 表达式创建变量,供 SQL 使用

特性

一级缓存

二级缓存

作用范围 单个 SqlSession Mapper 级别(namespace) 默认状态 开启 关闭 生命周期 随 SqlSession 创建而存在,关闭即销毁 随 Mapper 的 Factory 生命周期 共享性 同一会话内共享 多个会话共享 数据一致性 易产生脏读(会话内) 易产生脏读(跨会话) 适用场景 会话内重复查询,如循环中查询 全局热点数据,但变更频率低

25.Mybatis的动态SQL

26.Mybatis的一级缓存和二级缓存

四、项目(以装机APP举例)

1.描述你的项目和职责

我最近主导的一个代表性项目是XXX DIY电脑定制小程序,面向游戏玩家提供在线配置组装、社区分 享、一键下单的闭环服务。项目日活约 815 万,峰值 QPS 8000+,订单 TPS 200400。 我的核心职责: 架构设计:负责整体技术选型与架构演进,采用 Spring Cloud Alibaba + K8s 微服务体系,按业务 域拆分用户、商品、配置单、订单、营销等微服务。 高并发方案:设计多级缓存(Caffeine + Redis)、库存预热与 Lua 脚本扣减、MQ 削峰,支撑秒 杀场景下稳定读写;引入 Sentinel 实现限流熔断,保障核心链路。 数据持久化:规划 MySQL 分库分表(16 库 16 表,按 user_id 路由),订单表冷热分离;利用 MongoDB 存储用户行为日志、配件动态规格;Redis 承担热点数据与分布式锁。 稳定性与可观测性:搭建 Prometheus + Grafana + SkyWalking 全链路监控,制定核心指标 (RT、QPS、GC、慢查询)告警规则;主导故障演练与预案制定,系统可用性达 99.99%。 团队协作:带领 5 人后端小组,制定代码规范、Code Review 机制,推动引入 GitOps 流程,提 升交付效率。 技术亮点与成果: 通过 Redis 缓存 + binlog 同步(Canal),配件价格更新延迟控制在秒级,数据库读压力降低 80%。 设计配置单快照机制,确保订单生成后配件价格变动不影响历史订单。 实现基于 Redisson 的分布式锁 + 乐观锁双重防超卖,秒杀场景零超卖。 引入 G1 GC,配合内存分析与 JVM 调优,将 Full GC 频率降至一周不足 1 次。 支撑业务从 0 到 1 快速迭代,上线半年订单量增长 300%,大促期间系统平稳。 这个项目让我在高并发架构、数据库优化、稳定性保障等方面积累了较深的实践经验。

2.微服务根据什么来分割业务模块

业务边界 按业务域:用户、订单、商品、营销。 按业务环节:购物车、结算、支付、履约。 按访问频率:读服务(查询)与写服务(交易)分离(CQRS 思想)。

3.三高场景的设计

  1. 高并发 ①分层削峰:CDN(静态资源)→ Nginx(连接限流)→网关(令牌桶限流)→ Redis(热点 库存)→ MQ(异步削峰) ②缓存多级:本地 Caffeine + Redis 二级缓存,热点数据命中率 95%+ ③读写分离:MySQL 主从,查询走从库 ④无状态化:应用水平扩展,通过 K8s HPA 自动扩缩容
  2. 高可用 ①冗余部署:应用多 AZ 部署,任一节点故障自动摘除 ②数据库高可用:MySQL 主从自动切换(MHA/Orchestrator),Redis 哨兵/集群 ③降级熔断:Sentinel 配置熔断规则(慢调用比例、异常比例),核心链路降级(如秒杀高峰期 关闭非核心服务) ④超时与重试:RPC/HTTP 设置合理超时,接口幂等重试 ⑤监控告警:Prometheus + Grafana 全覆盖,核心指标 5 分钟未恢复自动电话告警
  3. 高扩展 ①微服务拆分:按业务域拆分(用户、商品、配置单、订单、营销),独立部署 ②数据库水平扩展:分库分表(16 库 16 表),按用户 ID 路由,支撑亿级数据 ③存储冷热分离:历史数据归档至 TiDB/ClickHouse,保持在线库轻量 ④弹性伸缩:基于 CPU/内存/QPS 指标配置 K8s HPA,高峰期自动扩容节点 ⑤异步解耦:非核心流程(积分、推送、报表)通过 RocketMQ/RabbitMQ 异步处理,降低主链 路耦合 兜底原则:每一层都要考虑限流、熔断、隔离、超时,确保局部故障不蔓延至整个系统。

4.微信登录业务的实现

步骤:

①前端获取 code:调用wx.login()获得临时登录凭证code 。 ②后端换取 openid/session_key:后端携带code调用微信接口code2Session ,获取

openid 、unionid 、session_key 。

③生成业务 Token:后端生成自定义登录态(JWT 或随机 token),将openid与用户信息(首次 登录自动注册)存入数据库/缓存,设置过期时间(如 2 小时)。返回 token 给前端。 ④前端存储 Token:前端将 token 存入wx.setStorageSync ,后续请求在 header 中携带(如

Authorization: Bearer <token> )。

⑤请求鉴权:后端拦截器验证 token 有效性,解析用户身份,支持自动续期(通过 refresh_token 或双 token 机制)。

安全措施:

①所有接口使用 HTTPS。 ② token 设置合理过期时间,敏感操作(修改密码、支付)需二次验证。 ③避免在客户端存储敏感信息(如 session_key)。 ④登录接口限制频率,防止暴力破解。

分布式环境:

token 存储在 Redis,实现无状态与共享,支持水平扩展。

5.订单业务的实现流程

  1. 下单前(防重与校验) ①用户提交配置单或购物车,后端校验库存、价格、兼容性 ②生成分布式锁(用户ID+商品组合),防止重复提交 ③获取地址、优惠券等前置信息
  2. 下单(核心事务) ①开启事务(或使用分布式事务) ②生成订单号(雪花ID),插入订单表(状态:待付款)
③扣减库存:Redis 预减 + 数据库乐观锁最终确认(update stock set stock=stock-1 where
sku_id=? and stock>0 )

④锁定优惠券:更新优惠券使用状态 ⑤清除购物车(若来源购物车) ⑥发送 MQ 消息:延迟检查支付超时、积分变更等 ⑦提交事务,返回订单信息 注意:库存扣减通常放在事务外或事务末尾,避免长事务锁表 3. 支付 ①调用微信支付统一下单,生成支付参数返回前端 ②用户支付成功后,微信异步回调通知 ③回调处理:校验签名、更新订单状态为“已付款”,触发后续流程 ④未支付订单通过延迟消息(30分钟)自动取消,回滚库存和优惠券 4. 履约与状态流转 ①已付款→组装/发货→发货→确认收货→完成 ②状态变更通过 MQ 异步通知:积分发放、库存报表、推送消息 ③每个状态变更记录日志(订单状态流水表) 5. 关键保障 ①幂等:支付回调使用订单号+状态机防止重复处理 ②最终一致性:库存扣减失败则回滚订单;支付超时自动取消并恢复库存 ③分库分表:订单按用户ID分表,历史订单归档 ④查询优化:用户订单列表走索引,后台订单管理使用 ES 或 OLAP 库

6.秒杀业务的实现

秒杀核心是削峰填谷、限流降级、防超卖,整体流程: ①静态资源走 CDN,页面按钮点击后置灰防重 ②网关层限流(令牌桶/漏桶),单用户/IP 限频,阻断机器刷单 ③活动开始前将库存加载到 Redis,用String或Hash存储 ④ Redis Lua 脚本原子扣减,返回成功/失败 ⑤扣减成功后发送 MQ 消息,异步创建订单,减少数据库压力

⑥消费 MQ 时使用数据库乐观锁(update stock set count=count-1 where product_id=? and

count>=1 ),保证最终一致性 ⑦订单表写入,同时记录用户秒杀记录(防重复购买) ⑧ Redis 记录用户已购数量,与库存扣减在同一 Lua 脚本中校验

降级与快速失败

①秒杀入口直接返回“排队中”或“已售罄”,避免长时间等待 ②当 Redis 库存归零或系统负载过高时直接降级

结果反馈

前端轮询或 WebSocket 获取秒杀结果(成功/失败)

7.支付模块的实现,如果支付成功但是服务器宕机如何处理

  1. 支付平台回调 微信/支付宝在用户支付成功后,会异步回调我们的接口,并带有重试机制(通常 15 秒、15 分 钟、30 分钟等多轮,持续 24 小时),直到返回成功状态。
  2. 回调处理幂等 回调接口收到请求后,先根据支付流水号(transaction_id )查询订单,若订单已是“已支付”状 态,直接返回成功,避免重复处理。
  3. 宕机场景下的可靠性 如果回调处理过程中服务器宕机,支付平台收不到成功响应,会继续重试。 下次重试时,订单状态可能仍为“待支付”,此时正常完成更新。 若重试期间我们的服务恢复,会正确完成订单状态变更。
  4. 主动补偿 为防止回调丢失,定时任务会扫描长时间“待支付”的订单,主动向支付平台查询订单最终状态,确 保同步。
  5. 本地消息表/消息队列 回调处理时先落库(记录支付流水),再通过 MQ 异步更新订单状态,避免长事务。若宕机导致 MQ 消息未发送,可通过定时任务扫描本地消息表重试。 关键:接口幂等 + 回调重试 + 主动补偿,保证服务器宕机不影响最终结果。

8.对账业务如何实现

对账流程:

  1. 从微信/支付宝下载对账文件(含交易明细、汇总数据)。
  2. 提取同一日内的本地支付记录(订单号、金额、支付状态)。
  3. 逐笔比对 双边匹配:按订单号/流水号匹配,核对金额、状态一致则通过。 长款(我方有,渠道无):渠道漏传或未回调成功,需主动向渠道查询或补单。 短款(渠道有,我方无):可能系统漏记录或异常,需人工介入查明原因,确认后补账或退 款。 金额不一致:记录差错,启动异常处理流程。
  4. 差错处理 长款:调用渠道查单接口,若支付成功则更新本地订单状态;若未支付则忽略或退款。 短款:人工核查日志、数据库,必要时发起退款或补录。 金额差异:核实是否存在手续费、优惠拆分等特殊场景。
  5. 汇总对账结果(总笔数、金额、差异明细),发送给财务与业务方。

技术实现

①定时任务(xxl-job)每日凌晨拉取账单,异步处理。 ②对账结果写入对账结果表,差异数据存入差错表。 ③对大文件采用分批流式处理,避免内存溢出。 ④若对账失败,支持自动重试并告警。

9.如何处理OOM

堆 1.使用jmap导出堆 dump,MAT 或 JProfiler 分析大对象与引用链。 2.检查代码:无限循环创建对象、集合未清理、流未关闭、ThreadLocal 未 remove。 3.调整 JVM:增大-Xmx ,同时设置-Xms与-Xmx相等,避免扩容开销。 4.优化数据结构(如使用基本类型替代包装类、精简对象字段) 方法区(元空间)

1.增加元空间:-XX:MaxMetaspaceSize (JDK 8+),或-XX:MaxPermSize (JDK 7-)。

2.排查类加载器泄漏(如热部署未释放旧 ClassLoader)。 3.限制动态类生成(如缓存代理类、减少反射)。 4.避免大量静态变量引用大对象。 虚拟机栈

1.栈深度溢出(StackOverflowError )

原因:递归过深、方法调用链过长。 解决:改递归为迭代;减少方法嵌套;必要时调整-Xss (增加栈容量,但会减少可创建线程 数)。 2.无法创建新线程(OutOfMemoryError:unabletocreatenewnativethread ) 原因:线程数超过系统限制或 JVM 内存不足。 解决:限制线程池大小;排查线程泄漏;减少栈大小(-Xss )以容纳更多线程;调整系统 ulimit -u提高用户进程上限;使用异步/事件驱动模型降低线程需求。 本地方法栈 / 虚拟机栈 1.检查 JNI 代码,避免深度递归与内存泄漏。 2.减少 native 方法调用,或用纯 Java 实现替代。 3.尝试调整栈大小(-Xoss ),但实际效果有限,通常与虚拟机栈共用。

10.G1和ZGC垃圾收集器的区别

维度

G1

ZGC

设计

目标

可预测的停顿时间(默认 200ms),兼 顾吞吐 超低延迟(亚毫秒级,<10ms),几乎不 影响业务

内存

布局

Region 分代(年轻代/老年代),Region 大小固定 动态 Region,无分代(JDK 21+ 支持分代 ZGC),支持 TB 级堆

并发

度

并发标记,但部分阶段(如初始标记、最 终标记、转移暂停)仍需要 STW 几乎所有阶段并发,仅根扫描等少数阶段 短暂 STW,使用染色指针+读屏障

停顿

时间

几十到几百毫秒,可配置 <10ms,随堆增大停顿时间仍稳定

适用

堆大

小

4GB ~ 几十 GB,适合中等规模堆 几十 GB ~ TB 级,适合超大堆,小堆优势 不明显

吞吐

量

较高(默认策略平衡延迟与吞吐) 相对较低(因并发开销大)

JDK

版本

JDK 7 引入,JDK 9 默认 JDK 11 实验,JDK 15 生产就绪,JDK 21 支持分代模式 选型建议:延迟敏感型(交易、游戏)选 ZGC;通用服务、吞吐优先选 G1。

11.如果每天20万条记录如何优化数据库

优化策略如下: ①表结构优化:合理按业务拆分,字段精简,主键设计合理 ②索引优化:覆盖索引,控制索引数量(单表索引不超过 5~6 个),避免冗余索引(定期用pt-

duplicate-key-checker清理)

③分库分表:分平分表(按业务键分 1632 表,单表控制在 500 万1000 万行),合理分库 ④读写分离:一主多从,主写从读,业务内区分读写数据源,借助中间件(ShardingSphere)或框 架路由 ⑤冷热数据分离:热数据(近 3~6 个月)保留在主表,使用分区表(按时间)便于快速删除旧分区 ⑥写入优化:批量插入减少事务,非核心数据(日志、埋点)通过 MQ 异步落库,顺序写入 ⑦查询优化:分页优化,缓存前置 ⑧监控与运维:定期OPTIMIZE TABLE回收空间,监控慢查询、死锁、连接数,及时加索引或重构 SQL。

12.Redis中存什么

①热点数据缓存:配件价格、库存、规格参数,高频访问的公开配置单等缓存 5~10 分钟 ②用户会话与状态:微信登录态,未登录用户的 DIY 临时配置,用户购物车 ③秒杀/限流相关:库存,限购标记,接口限流计数器 ④计数与排序:配置的浏览量,热门配置单排行 ⑤分布式锁:幂等key,库存扣减锁 ⑥布隆过滤器,空值缓存

13.MongoDB存什么

①用户行为埋点 ②配件规格参数 ③配置单的评论、点赞、分享 ④用户 DIY 会话状态 ⑤商品评价与晒单 ⑥价格变动历史

14.ElasticSearch存什么,如何分片

①配件搜索索引:配件名称、型号、规格参数(CPU/显卡等),支撑全文检索和筛选 ②配置单搜索:用户公开的配置单标题、配件组合,用于社区推荐和搜索 ③用户行为日志:点击、搜索词、DIY操作,用于分析和个性化推荐 ④监控日志(ELK)

分片:

①主分片每个分片大小控制在 10~50GB 配件索引数据约 500 万条,存储 2GB,设1 主分片 + 1 副本即可; 日志索引日增 5GB,按天滚动,每天3 主分片 + 1 副本,便于水平扩展。

15.业务表都是如何设计的

用户表,配件表,配置单表,类别表,订单表,订单项表,购物车表,库存流水表…

16.项目的数据量,用户数,QPS,TPS是多少

  1. 数据量

MySQL

①用户表:注册用户50100万(主要来自游戏玩家社群) ②配置单表:用户保存的自定义配置200500万条(含配件清单、价格) ③订单表:日均订单20005000单,活跃订单数据2050万行 ④配件库:SKU(CPU/显卡/主板/内存等)约500010000个,价格、库存实时同步 Redis:热数据:热门配置单、配件价格缓存、用户购物车、秒杀库存,约1020GB 日志与埋点:用户行为(浏览配件、DIY 模拟组装、分享)日增量300800万条 2. 用户数 ①注册用户:50万100万 ②日活(DAU):3万8万(周末、硬件新品发布时激增) ③高峰并发用户:1万2万(如显卡首发、618/双11) 3. QPS ①配件列表/详情、配置单查看、价格查询:峰值50008000 QPS ②写接口:收藏配置、加入购物车、提交定制需求:峰值8001500 QPS 4. TPS ①订单生成 + 库存扣减 + 优惠:峰值100200 TPS ②支付回调(微信支付):峰值50100 TPS,通过 MQ 削峰 ③ DIY 配置单保存:峰值300~500 TPS(独立于订单事务)

17.项目是否上线

如果上线可能面试官会现场打开,如果没上线说明原因

18.你在项目中遇到问题如何排查,如何解决的

确认问题:

CPU 飙升:top -Hp 找到线程 ID,jstack转十六进制定位到具体线程栈,常发现死循 环、正则回溯、频繁 GC 内存溢出:jmap -heap查看内存分布,jmap -histo:live查看存活对象,导出堆 dump 用 MAT/JProfiler 分析大对象和引用链 接口慢:结合链路追踪(SkyWalking/Pinpoint)看耗时节点;若未接入则打点日志或 arthas trace命令定位方法耗时 中间件堆积:检查 MQ 积压、数据库连接池活跃数、Redis 慢日志 举例:曾遇线上 Full GC 频繁,通过 dump 分析发现某定时任务批量查询数据库时将全部结果放入 List,导致大量对象进入老年代。解决:改为分页查询 + 批量处理,并优化 JVM 参数。

堆内存

GC 选择

元空间

OOM 处理

GC 日志(JDK 9+)

19.RabbitMQ里都有哪些交换机和队列

交换机类型: ①Direct:根据routing key精确匹配,消息发送到绑定相同routing key的队列。 ②Topic:routing key支持通配符(*匹配一个单词,#匹配零或多个),按模式匹配。 ③Fanout:广播,忽略routing key ,将消息发送到所有绑定队列。 ④Headers:根据消息头中的键值对匹配,不依赖routing key ,性能较低,实际少用。 队列特性: ①普通队列:默认行为。 ②持久化队列:durable=true ,Broker重启后队列仍存在。 ③排他队列:exclusive=true ,仅创建它的连接可见,连接断开自动删除。 ④自动删除队列:auto-delete=true ,最后一个消费者取消时删除。 ⑤死信队列:配合x-dead-letter-exchange参数,处理被拒绝/过期的消息。 ⑥延迟队列:通过插件或设置x-message-ttl + 死信实现。

20.如果MQ消息堆积如何处理

-Xms8g-Xmx8g               # 初始与最大堆一致,避免扩容开销
-XX:NewRatio=2              # 老年代:年轻代=2:1,默认适合多数场景
-XX:SurvivorRatio=8         # Eden:Survivor=8:1
-XX:+UseG1GC                # G1(推荐 4G+ 堆)
-XX:MaxGCPauseMillis=200    # 目标停顿时间
-XX:G1HeapRegionSize=16m    # 区域大小
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m  # 防止扩容抖动
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/dump
-XX:+ExitOnOutOfMemoryError   # 容器环境便于重启
-Xlog:gc*:file=/path/gc.log:time,uptime:filecount=5,filesize=100m

消息堆积通常因消费能力不足或下游故障导致 ①紧急止损:临时扩容消费者,限流上游,跳过非关键消息 ②定位根因:检查消费者,查看队列指标,排查下游 ③优化消费能力:批量消费,异步化,提升单机性能 ④消息转移:新建临时Topic/队列,启用死信队列 ⑤长期治理:设置告警,重试策略,流量压测

21.如何排查死锁

数据库死锁(MySQL)

①执行SHOW ENGINE INNODB STATUS ,查看LATEST DETECTED DEADLOCK部分,分析事务持有锁

和等待锁的 SQL。

②开启innodb_print_all_deadlocks = ON将所有死锁记录到错误日志。

③根据锁信息优化 SQL,调整事务顺序,或降低隔离级别。

Java 应用死锁

①jps定位进程 ID。 ②jstack 导出线程栈,搜索deadlock ,会明确显示死锁线程及锁资源。

③用jconsole或jvisualvm可视化检测。

④分析堆栈中waiting to lock和locked的关系,修改代码避免循环等待锁。 通用原则:统一资源获取顺序、缩短事务/锁持有时间、使用超时机制。

22.AI都使用过吗?都在哪些地方使用了

基于 Spring AI 构建电脑 DIY 智能推荐与智能客服能力,采用RAG 架构(Embedding + 向量检索)实 现硬件知识库精准匹配;通过动态 Prompt 模板、流式响应、会话上下文持久化提升交互体验;设计 Token 成本控制、接口限流、多模型降级策略,保障 AI 服务稳定可用,实现预算匹配、智能装机、配 置咨询等智能化能力。

“智能装机 / 智能客服” 里的具体流程:

①用户输入:5000 元游戏主机,主要玩黑神话:悟空 ②意图识别:大模型识别预算、用途、偏好 ③向量检索:从硬件库召回 3 套最匹配配置 ④ Prompt 拼装:把检索结果 + 用户需求塞进模板 ⑤大模型生成:输出配置清单 + 选购建议 ⑥流式返回:前端实时展示 ⑦日志与监控:记录调用、耗时、Token、异常

23.雪花算法组成,时钟回拨问题如何解决

雪花算法组成(64位): 1位符号位(固定0)+ 41位时间戳 + 10位工作机器ID(数据中心ID+机器ID)+ 12位序列号 时钟回拨问题解决方案:

  1. 记录上次生成时间戳:若当前时间小于上次时间戳,判定发生回拨。
  2. 回拨容忍阈值:若回拨幅度小(如<5ms),让线程短暂自旋等待时钟追上;若超过阈值,直接抛 出异常,拒绝生成。
  3. 备用机制:可预留少量序列号位或采用缓存ID策略,确保回拨期间从预留池获取。
  4. 协调分配:利用ZooKeeper/etcd等组件,在节点启动时注册并维护本地时钟状态,回拨严重时主 动下线或切换机器ID。 实际生产常用方案:回拨等待 + 抛异常 + 告警,确保ID绝对单调递增且唯一。

24.线上的各种软硬件指标

①线上服务器采用16 核 64GB Linux 物理机 / 云服务器 ②应用通过Docker 容器化部署,单实例分配8GB 内存;JDK 版本为JDK8,使用G1 收集器, JVM 参数配置:-Xms6g -Xmx6g -Xss512k -XX:MetaspaceSize=256m -

XX:MaxMetaspaceSize=512m -XX:MaxGCPauseMillis=200

③开启 GC 日志与 OOM 自动 Dump,线上无频繁 FullGC,GC 停顿稳定在200ms 内。 ④系统支撑日均订单 5000+、节假日峰值并发 10000+,峰值可用性99.95%,核心接口响应时间 <200ms,数据库负载降低30%,缓存命中率95%+,服务高可用、低延迟稳定运行。

25.Redis和Mysql的一致性如何保证

  1. 先更新数据库,再删除缓存(Cache Aside Pattern) 读:先查缓存,命中则返回;未命中查数据库并回写缓存。 写:先更新数据库,再删除缓存(而非更新缓存)。 风险:并发下可能出现短暂脏数据(读请求在写删除前读到旧缓存),但概率低,对一致性 要求不苛刻的场景可用。
  2. 延迟双删 写操作:先删除缓存,再更新数据库,异步延迟(如几百毫秒)后再删一次缓存。 解决因主从延迟或并发读导致旧数据被回写的问题。
  3. 监听binlog 异步同步(Canal ,Maxwell等) 监听 MySQL binlog,将变更推送到消息队列,消费端更新/删除 Redis。 解耦、可靠,适合高一致性要求的复杂场景。

26.TCP/IP协议三次握手

  1. 第一次握手:客户端发送SYN=1, Seq=X包,进入SYN_SENT状态,请求建立连接。
2. 第二次握手:服务端收到后,回复SYN=1, ACK=1, Seq=Y, Ack=X+1 ,进入SYN_RCVD状态,

表示已收到客户端的同步请求并同意。

3. 第三次握手:客户端收到后,发送ACK=1, Seq=X+1, Ack=Y+1 ,双方进入ESTABLISHED状

态,连接建立完成。

27.Docker容器如何优化

镜像体积:选用精简基础镜像(Alpine、Debian slim);多阶段构建分离编译环境与运行环境; 合并 RUN 命令减少层数;清理临时文件(apt-get clean 、rm -rf

/var/lib/apt/lists/* )。

构建效率:合理利用构建缓存,将变动少的指令前置;使用.dockerignore排除无用文件。 运行时资源:设置—memory 、—cpus限制容器资源;JVM 需感知容器内存限制(JDK 10+ 默认支

持,老版本加-XX:+UseContainerSupport )。

安全与权限:以非 root 用户运行;只读根文件系统(—read-only );限制—cap-drop=ALL按需 添加。 网络与存储:使用—network自定义网络;持久化数据用卷而非容器层;日志驱动限制(—log-

opt max-size )。

Java 专用:设置合理的堆内存(-Xmx ),避免过量或不足;使用-

XX:+ExitOnOutOfMemoryError便于容器重启。

28.Linux常用命令

文件操作:ls 、cd 、pwd 、cp 、mv 、rm 、touch 、mkdir 、find 、tar
文本处理:cat 、less / more 、head / tail 、grep 、awk 、sed 、vi / vim
权限与进程:chmod / chown 、ps 、top / htop 、kill 、jobs / fg / bg
网络与系统:netstat / ss 、curl / wget 、ping 、ifconfig / ip 、ssh 、scp 、df / du 、
free 、systemctl
Java 常用:jps 、jstack 、jstat 、jmap 、tail -f看日志、grep筛堆栈

29.Git如何解决冲突

  1. 拉取/合并时出现冲突提示:git pull或git merge报冲突。
2. 查看冲突文件:git status列出处于both modified状态的文件。
3. 手动编辑:打开冲突文件,找到<<<<<<< HEAD 、======= 、>>>>>>> branch标记,按需保留

代码(当前分支/合并分支/双方整合),删除标记和多余内容。 4. 标记已解决:git add <冲突文件> ,将文件标记为已解决。 5. 完成合并:git commit (若为 merge 会自动生成合并提交)或继续git rebase — continue (若在变基过程中)。 6. 可选工具:git mergetool调用图形化对比工具,或直接用 IDE(IntelliJ、VS Code)的可视化 合并界面。

五、其他

  1. 你的缺点是啥 无关紧要的缺点
  2. 离职原因 项目结束合同结束,公司不给交五险一金,公司发展受限等
  3. 爱好都有什么 运动,技术文章,AI,潮流等
  4. 职业发展规划 架构师(技术方向) / 项目经理(管理方向)