1、JAVA基础
1.1、HashMap
hasdMap.put(k,v)原理:判断数组是否已创建,没有则创建数组;通过k根据hash算法计算出数组下标,判断当前下标是否有数据,没有数据构造node节点插入;有数据就发生了hash碰撞,此时判断k是否相等,相等就覆盖原来的值;不相等判断是否是树型节点,如果是树就构造一个树型节点放到红黑树里,如果不是就创建node节点放在链表里;判断链表长度是否大于 8并且数组长度大于64,大于的话链表转换为红黑树;插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的二倍。
1.2、String类能被继承吗 ?
不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
1.3、面向对象特性
封装、继承、多态、抽象
1.4、重定向与转发的区别
请求转发特点:一次请求,浏览器地址不变,访问的是自己本身的web资源,传输的数据不 会丢失。
重定向特点:两次请求,浏览器地址发生变化,可以访问自己Web之外的资源,传输的数据 会丢失。
1.5、Cookie与Session的区别
什么是会话 :用户打开一个浏览器访问页面,访问网站的很多页面,访问完成后将浏览器关闭的过程称为是一次会 话.
会话的作用 进行数据共享 针对某个浏览器的多次请求
Cookie: 在客户端保存用户信息;不安全;单个cookie存储上限不能超过4kb;只能存储ASCII字符串;
Session:在服务端保存用户信息;安全性高;存储容量没有上限;可以存储任何数据类型;
1.6、JVM 的内存结构是什么?新生代、老年代、永久代(元空间)的区别?
JVM 内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区 / 元空间):
堆:存储对象实例,分为新生代(Eden + Survivor0 + Survivor1,存放新创建的对象,GC 频繁)、老年代(存放存活时间长的对象,GC 频率低);
元空间(JDK8 替代永久代):存储类元信息、常量池等,直接使用本地内存,避免永久代 OOM。
1.7、ArrayList 和 LinkedList 的区别,适用场景?
1.8、synchronized 和 Lock 的区别?
1.9、什么是线程池?核心参数有哪些?为什么要用线程池?
线程池:管理一组可复用的线程,避免频繁创建 / 销毁线程的开销;
核心参数(ThreadPoolExecutor):
corePoolSize:核心线程数(常驻线程,即使空闲也不销毁);
maximumPoolSize:最大线程数(核心 + 非核心线程总数上限);
keepAliveTime:非核心线程空闲超时时间;
workQueue:任务队列(存储等待执行的任务);
threadFactory:创建线程的工厂;
handler:拒绝策略(任务满时的处理方式,如 AbortPolicy、CallerRunsPolicy 等);
优势:降低资源消耗、提高响应速度、统一管理线程、可监控(如任务数、执行时间)。
1.11、什么是类加载机制?双亲委派模型的作用?
类加载过程:加载→验证→准备→解析→初始化;
双亲委派模型:类加载时先委托父类加载器加载,父类加载器无法加载时才自己加载;
作用:避免类重复加载、保证核心类(如 java.lang.String)不被篡改(沙箱安全)。
2、Spring
2.1、bean的作用域
1)singleton单例(默认就是);
2)prototype原型,每次请求都会创建一个新的 bean 实例;
3) request 每一次HTTP请求都会产生一个新的bean,该bean 仅在当前HTTP request内有效;
4)session每一次HTTP请求都会产生一个新的 bean,该bean 仅在当前 HTTP session 内有效;
5)globalSession 类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才 有意义;
2.2、bean的生命周期
注意:singleton单例情况下spring才会管理bean生命周期;
1)对scope为singleton且非懒加载的bean进行实例化;
2)按照Bean定义信息配置信息,注入所有的属性;
3)如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id, 此时该Bean就获得了自己在配置文件中的id;
4)如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的 BeanFactory,这样该Bean就获得了自己所在的BeanFactory;
5)如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入 该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext;
6)如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方 法;
7)如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法;
8)如果Bean配置了init-method方法,则会执行init-method配置的方法;
9)如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方 法;
10)经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓 存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,期生命周 期就交给调用方管理了,不再是Spring容器进行管理了 ;
11)容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法;
12)如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的 生命周期结束;
2.3、用到设计模式
1)简单工厂模式;又叫做静态工厂方法(StaticFactory Method)模式, 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象;
2)工厂方法模式;采用工厂模式,即应用程 序将对象的创建及初始化职责交给工厂对象。如果将应用程序自己的工厂对象交给Spring管理,那 么Spring管理的就不是普通的bean,而是工厂Bean;
3)单例模式;保证一个类仅有一个实例,并提供一个访问它的全局访问点。 bean的单例模式;
4)适配器模式;AdvisorAdapter
5)包装器模式;
6)代理模式;1、JDK动态代理。2、CGLib字节码生成技术代理。
7)观察者模式;
8)策略模式;
9)模板方法模式;
2.4、IOC
控制反转,创建对象的控制权限由手动创建交给spring管理创建;底层实现:xml解析、工厂、反射(工厂通过加载xml配置文件获取对象的权限定名,通过反射技术创建对象实例;通过set属性方法进行依赖注入(如果没有设置beanName,默认是通过类型来注入);存放在map中,key=beanName,value=实例);
手写IOC简单案例:
public class BeanFactory {
private static final Map<String,Object> map = new HashMap<>();
static {
try {
//加载配置文件
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//dom4j解析xml配置文件
Document document = new SAXReader().read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> beanList = rootElement.selectNodes("//bean");
//将xml里面所配置的bean全部加载到map中
for (Element element : beanList) {
map.put(element.attributeValue("id"), Class.forName(element.attributeValue("class")).newInstance());
}
List<Element> propertyList= rootElement.selectNodes("//property");
String parentId;
Object parentObject;
for (Element element : propertyList) {
parentId = element.getParent().attributeValue("id");
parentObject = map.get(parentId);
for (Method method : parentObject.getClass().getMethods()) {
if (method.getName().equalsIgnoreCase("set" + element.attributeValue("name"))){
method.invoke(parentObject,map.get(element.attributeValue("ref")));
}
}
map.put(parentId,parentObject);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBeanInstance(String id) {
return map.get(id);
}
}2.5、AOP
面向切面编程;对某个方法进行增强;底层实现:jdk动态代理(默认)、cglib动态代理;
1、横切关注点 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect) 类是对物体特征的抽象,切面就是对横切关注点的抽象 3、连接点(joinpoint) 被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方 法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut) 对连接点进行拦截的定义
5、通知(advice) 所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五 类
6、目标对象 代理的目标对象
7、织入(weave) 将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction) 在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
通知方式:
1)前置通知(@Before):在方法前面执行;
2)正常返回通知(@AfterReturning):在方法正常返回后执行;
3)异常通知(@AfterThrowing ):在方法抛出异常时执行
4)后置通知(@After):方法正常执行或者抛出异常都会执行,相当于finally
5)环绕通知(@Around):在方法执行前后;
2.6、事务
传播机制:
1) PROPAGATION_REQUIRED ,Spring默认的事务传播级别,使⽤该级别的特点是,如果上下⽂中 已经存在事务,那么就加⼊到事务中执⾏,如果当前上下⽂中不存在事务,则新建事务执⾏。所以这个 级别通常能满⾜处理⼤多数的业务场景。
2)PROPAGATION_SUPPORTS ,如果 上下⽂存在事务,则⽀持事务加⼊事务,如果没有事务,则使⽤⾮事务的⽅式执⾏。
3)PROPAGATION_MANDATORY , 该级别的事务要求上下⽂中必须要存在事务,否则就会抛出异 常!
4)PROPAGATION_REQUIRES_NEW ,每次都会新建⼀个事务,并且同时将上下⽂中的事务挂起,执⾏当前新建事务完成以后,上下 ⽂事务恢复再执⾏。
5)PROPAGATION_NOT_SUPPORTED ,上下⽂中存在事务,则挂起事务,执⾏当前逻辑,结束后恢复上下⽂的事务。
6)PROPAGATION_NEVER ,上下⽂中不能存在事务,⼀旦有事务,就抛出runtime异 常,强制停⽌执⾏;
7)PROPAGATION_NESTED ,如果上 下⽂中存在事务,则嵌套事务执⾏,如果不存在事务,则新建事务
隔离级别:
1、Serializable :最严格的级别,事务串⾏执⾏,资源消耗最⼤;
2、REPEATABLE READ :保证了⼀个事务不会修改已经由另⼀个事务读取但未提交(回滚)的数据。 避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。(spring事务默认的隔离级别)
3、READ COMMITTED :⼤多数主流数据库的默认事务等级,保证了⼀个事务不会读到另⼀个并⾏事务 已修改但未提交的数据,避免了“脏读取”。该级别适⽤于⼤多数系统。
4、Read Uncommitted :保证了读取过程中不会读取到⾮法数据。 上⾯的解释其实每个定义都有⼀些拗⼝,其中涉及到⼏个术语:脏读、不可重复读、幻读。 这⾥解释⼀下: 脏读 :所谓的脏读,其实就是读到了别的事务回滚前的脏数据。⽐如事务B执⾏过程中修改了数据X,在 未提交前,事务A读取了X,⽽事务B却回滚了,这样事务A就形成了脏读。 不可重复读 :不可重复读字⾯含义已经很明了了,⽐如事务A⾸先读取了⼀条数据,然后执⾏逻辑的时 候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读 了。 幻读 :⼩的时候数⼿指,第⼀次数⼗10个,第⼆次数是11个,怎么回事?产⽣幻觉了? 幻读也是这样⼦,事务A⾸先根据条件索引得到10条数据,然后事务B改变了数据库⼀条数据,导致也 符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产⽣了幻读
3、动态代理
3.1、JDK动态代理;被代理对象必须实现接口;
/**参数:1、类加载器,2、被代理对象实现的接口数组,3、InvocationHandler实例(必须要重写invoke方法,最终通过invoke方法来对被 *代理类的方法进行增强;)
*/
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
/**
* proxy 代理对象
* method 被代理对象的方法对象
* args 被代理对象的方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("aop增强,方法执行前");
Object result = method.invoke(obj, args);
System.out.println("aop增强,方法执行后");
return result;
}
});
通过Proxy.newProxyInstance方法生成代理对象,然后通过代理对象调用被代理对象的任何方法都会执行invoke方法进行增强;3.2、cglib动态代理;被代理对象不用实现接口;
3.3、动态代理和静态代理的区别;
静态代理:代理对象都是固定写好的.java文件;
动态代理:代理对象是动态生成的,不需要我们手动去编写;
4、Spring Mvc
4.1原理:
1:⽤户发送请求⾄前端控制器DispatcherServlet
2:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
3:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截 器(如果 有则⽣成)⼀并返回DispatcherServlet
4:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
5:处理器适配器执⾏Handler
6:Handler执⾏完成给处理器适配器返回ModelAndView
7:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个 底层对 象,包括 Model 和 View
8:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
9:视图解析器向前端控制器返回View
10:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域 第⼗⼀步:前端控制器向⽤户响应结果
人话:DispatcherServlet 拦截所有请求,根据url定位到对应的Controller,执行业务逻辑,返回视图模型解析模型数据渲染视图,返回客户端;
简单手写springmvc:
public class MyDispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过req获取请求的url,对handlerList中handler的url进行匹配,匹配中了,通过反射调用该对象的对应方法执行业务逻辑进行返回;
}
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载配置文件springmvc.properties,获取包路径
//2、根基包路劲递归扫描所有.class结尾的文件 通过反射实例化放在list集合中
//3、初始化bean对象,初始化IOC容器;遍历list里面的所有实例化对象判断是否标有@MyController和MyService注解添加到Map容器中;
//key = 注解value值/获取该对象实现接口名的首字母小写,value = 就是该实例化对象
//4、依赖注入;遍历map容器,通过反射获取每个对象的所有字段,匹配@MyController注解。根据注解value值通过name从map中做出对应的对象并注入
//再或者根据字段类型,获取到key去map中寻找对象通过反射调用set方法注入
//5、构造HandlerMapping处理器映射器,将URL和Method建立映射关系
//继续遍历IOC容器里面所有对象,并且匹配@RequestMapping注解获取类上的parentUrl,再获取对象的所有方法匹配@RequestMapping注解获取
//subUrl,拼接起来组成新的URL。创建Handler类,定义属性 url,object,method,paramMap:key = 方法的参数名,value = 参数位置,第几个参数
//将url、实例化对象,方法名、参数信息封装到Handler添加到handlerList中。
}
}4.2、拦截器、过滤器之间的区别和执行顺序
拦截器:只会拦截访问的控制器⽅法(Handler)
过滤器:对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果配置为/*可以对所 有的资源访问(servlet、js/css静态资源等)进⾏过滤处理
执行顺序:过滤器(filter) > 拦截器(inteceptor) > sevlet
5、Spring AI
5.1、核心组件
ModelClient:统一调用大模型接口,如 ChatModel、EmbeddingModel 等。
PromptTemplate:参数化模板,用于复用 Prompt 结构 。
VectorStore:向量数据库抽象层,用于存储和检索向量数据。
RAG:实现检索增强生成的组件集合。
ChatMemory:管理对话上下文记忆。
5.2、Spring AI、LangChain4j、LangChain 之间有什么区别?
LangChain:最知名的 Python 框架,提供丰富的模块(Chain、Agent)来处理复杂任务 。
LangChain4j:LangChain 的 Java 移植版本,为 Java 开发者提供了类似的功能 。
Spring AI:基于 Spring 生态(Spring Boot、Spring Cloud)构建,核心优势在于无缝集成。它更适合 Java 团队进行企业级业务系统的智能化改造(如智能客服、知识库),可以零成本融入现有微服务体系
5.3、核心技术:RAG 与 Function Calling
概念:RAG 旨在解决大模型知识陈旧和幻觉问题。流程是先根据用户问题从向量数据库中检索相关知识,然后将这些知识作为上下文发送给大模型生成答案 。
Spring AI 实现:通过
VectorStore查询相关文档,然后用PromptTemplate将检索结果与原始问题拼装成增强 Prompt,最后调用大模型生成答案 。
5.4、系统提示词(System Prompt)通常包含哪些组成部分?
一个高质量的 System Prompt 应包含以下要素:
角色定义:赋予 AI 身份(如资深面试官、数学老师)。
核心任务:明确要完成的目标。
规则约束:规定什么不能做(如禁止提及政治、必须包含步骤)。
风格语气:规定表达风格(严肃、温和)。
参考示例:特别是需要结构化输出(如 JSON)时,提供示例至关重要。
优先级界定:多任务时明确优先级 。
6、SpringBoot
6.1、启动原理:
Spring Boot的启动过程,本质上就是创建并初始化Spring应用上下文(ApplicationContext)的过程。
main方法:一切都从一个带有@SpringBootApplication注解的main方法开始,它调用了SpringApplication.run()。创建
SpringApplication实例:run方法首先会创建一个SpringApplication实例。在这个创建过程中,它会进行一些关键的初始化工作,比如判断应用类型(是否为Web应用)、设置初始化器和监听器 。准备环境:紧接着,它会准备应用的环境(
Environment),加载application.properties或application.yml等配置文件,为后续的Bean创建提供配置数据 。创建
ApplicationContext:根据应用类型(如Web应用),创建对应的ApplicationContext(应用上下文)。默认情况下,非Web应用是AnnotationConfigApplicationContext,Web应用是AnnotationConfigServletWebServerApplicationContext。刷新上下文 -
refreshContext:这是最核心的一步。它调用了Spring框架的“刷新”方法,完成了以下工作 :执行自动配置:解析并加载所有符合条件的自动配置类(这正是我们下面要讲的自动配置原理)。
扫描组件:根据
@ComponentScan的配置,扫描并注册用户自定义的Bean 。实例化Bean:创建所有单例Bean的实例,并进行依赖注入。
启动内嵌服务器 & 运行Runner:如果是Web项目,此时会启动内嵌的Tomcat、Jetty等服务器 。最后,会执行所有实现了
ApplicationRunner或CommandLineRunner接口的Bean,执行一些自定义的启动后逻辑 。
总结:
判断应用类型(是否为Web应用)、设置初始化器和监听器,
加载 application.properties 或 application.yml 等配置文件
解析并加载所有符合条件的自动配置类,以及扫描注册自定义Bean注入
启动内嵌的Tomcat、Jetty等服务器
6.2、自动配置原理
自动配置的核心思想是:根据项目类路径下的依赖、已有的Bean以及配置文件,智能地判断并自动创建所需Bean。
主要依赖于 @SpringBootApplication 中的一个核心注解 @EnableAutoConfiguration 。它的工作原理可以分为以下几步:
开启总开关 (
@EnableAutoConfiguration):这个注解通过@Import(AutoConfigurationImportSelector.class)导入了一个非常重要的组件 。获取候选配置名单 (
AutoConfigurationImportSelector):AutoConfigurationImportSelector会去加载所有Jar包中的特定配置文件。Spring Boot 2.7 之前,这个文件是
META-INF/spring.factories。Spring Boot 3.0 之后,优化为
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。这个文件中列出了所有可能的自动配置类(如
DataSourceAutoConfiguration,WebMvcAutoConfiguration等)的完整类名 。
应用条件注解:拿到所有候选名单后,Spring Boot并不会一股脑全部启用。它会逐一检查每个自动配置类上的条件注解(
@Conditional系列),只有满足所有条件的配置类才会真正生效 。常用的条件注解包括:@ConditionalOnClass: 当类路径下存在指定的类时才生效。例如,只有当你引入了数据库驱动,DataSourceAutoConfiguration才会生效 。@ConditionalOnMissingBean: 当Spring容器中没有指定的Bean时才生效。这保证了自定义配置的优先级最高——只要你手动定义了一个DataSource的@Bean,Spring Boot就不会再自动创建一个默认的 。@ConditionalOnProperty: 当配置文件中指定的属性值满足条件时才生效。
注册Bean并绑定属性:最后,通过筛选的自动配置类(本身就是
@Configuration配置类)会像普通配置类一样,利用@Bean注解的方法向容器中注册所需的Bean 。同时,这些Bean的属性(如数据库URL、用户名等)会通过@ConfigurationProperties与配置文件(application.properties)中的值进行绑定 。
总结:
扫描META-INF/spring.factories文件中所有的自动配置类,根据条件相应的自动配置
7、Shiro
7.1、三大核心组件
Subject:可以理解为当前“用户”。但它不单单指人,也可以是第三方进程、爬虫等任何与当前应用交互的实体。
Subject是一个门面,所有和安全相关的操作(如登录、注销、权限检查)都通过它来发起。SecurityManager:安全管理器,是 Shiro 框架的核心。它就像 Spring MVC 中的
DispatcherServlet,负责协调和管理所有组件,实际执行Subject委托的各种安全操作。Realm:可以看作是 Shiro 与应用程序安全数据之间的“桥梁”或“数据源”。当 Shiro 需要验证用户身份或检查用户权限时,它会通过配置的一个或多个
Realm来从数据库、LDAP 或配置文件等地获取用户、角色和权限信息。
7.2、认证流程
收集凭证:应用程序收集用户提交的身份(principals,如用户名)和凭证(credentials,如密码),并封装成一个
AuthenticationToken(如UsernamePasswordToken)。执行登录:调用
Subject.login(token)方法。Subject会将此请求委托给SecurityManager。认证委托:
SecurityManager将认证工作委托给内部的Authenticator组件。多 Realm 策略:如果配置了多个
Realm,Authenticator会根据设定的认证策略(如所有 Realm 都成功、只要一个成功即可等)来协调认证。Realm 验证:
Authenticator会将token传递给一个或多个配置好的Realm。在Realm的doGetAuthenticationInfo方法中,会根据token中的用户名去数据源查询用户信息。结果处理:
如果找不到用户信息,则抛出
UnknownAccountException。如果找到用户,则对用户提交的密码和数据库中存储的密码进行比对。若密码错误,则抛出
IncorrectCredentialsException。如果全部校验通过,认证成功。
8、Mysql
8.1、CHAR 和 VARCHAR 的区别是什么?
CHAR是定长字符串,存储时会用空格填充到指定长度;VARCHAR是变长字符串,存储时只占用实际字符长度加1-2字节的额外空间(用于记录长度)
8.2、InnoDB 和 MyISAM 的主要区别是什么?
8.3、什么是索引?索引的作用是什么?有哪些类型?
索引:MySQL 中用于快速查询数据的数据结构(主流是 B + 树),相当于书籍的目录。
作用:大幅提升查询效率,降低数据库 IO 成本;但会增加插入 / 更新 / 删除的开销(需要维护索引)。
常见类型:
按数据结构:B + 树索引(最常用)、哈希索引、全文索引、R 树索引;
按逻辑分类:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、联合索引(多字段组合)、前缀索引(对字符串字段的部分字符建索引)。
8.4、什么是事务?事务的 ACID 特性分别指什么?
事务:一组不可分割的数据库操作,要么全部执行成功,要么全部失败回滚(比如转账:扣钱 + 加钱必须同时成功 / 失败)。
ACID 特性:
A(Atomicity)原子性:事务是最小单位,不可拆分;
C(Consistency)一致性:事务执行前后,数据库数据的完整性约束不变(比如转账前后总金额不变);
I(Isolation)隔离性:多个事务并发执行时,相互隔离,互不干扰;
D(Durability)持久性:事务提交后,修改永久保存,不会因崩溃丢失。
8.5、常见的导致索引失效的场景有哪些?
1、索引字段参与函数 / 运算:如where age+1=10、where substr(name,1,2)='zh';
2、使用!=/<>/not in(除非是唯一索引);
3、联合索引不满足最左前缀原则:如联合索引(a,b,c),查询where b=1 and c=2会失效;
4、使用like '%xxx'(模糊查询以 % 开头);
5、字段类型不匹配:如索引字段是 int,查询用where id='123'(字符串);
6、使用or连接非索引字段:如where a=1 or b=2(仅 a 有索引)。
9、Redis
高性能:基于内存操作,单线程模型避免了线程切换开销,QPS 可达 10 万 +;
丰富的数据类型:支持 String、Hash、List、Set、ZSet、Bitmap、HyperLogLog 等;
持久化:支持 RDB 和 AOF 两种持久化方式,避免内存数据丢失;
原子性:所有操作都是原子的,支持事务(弱事务);
高可用:支持主从复制、哨兵(Sentinel)、集群(Cluster);
丰富的功能:发布订阅、过期时间、Lua 脚本、流水线(Pipeline)等。
9.1、Redis 的持久化机制(RDB 和 AOF)有什么区别?
9.2、 Redis 的过期键删除策略是什么?
Redis 采用三种策略结合的方式:
惰性删除:访问键时才检查是否过期,过期则删除(节省 CPU,但可能导致过期键长期占用内存);
定期删除:每隔一段时间(默认 100ms),随机抽取部分过期键删除(平衡 CPU 和内存);
内存淘汰:当内存达到 maxmemory 时,触发淘汰策略(如 volatile-lru:淘汰过期键中最近最少使用的;allkeys-lru:淘汰所有键中最近最少使用的)。
9.3、 Redis 主从复制的原理是什么?
主从复制用于实现数据备份和读写分离,核心流程:
从库连接主库,发送 SYNC 命令;
主库收到命令后,执行 bgsave 生成 RDB 快照,同时将后续写命令缓存到内存;
主库将 RDB 文件发送给从库,从库接收并加载 RDB,恢复数据;
主库将缓存的写命令发送给从库,从库执行这些命令,实现数据同步;
后续主库的写命令会实时同步给从库,保持主从数据一致。
补充:Redis2.8 + 支持增量复制,断线重连时仅同步断线期间的增量数据,无需全量同步。
9.4、 Redis 哨兵(Sentinel)的作用是什么?
哨兵是 Redis 的高可用解决方案,核心功能:
监控:持续检查主库和从库是否正常运行;
自动故障转移:主库宕机时,从哨兵集群选举出新的主库,将其他从库指向新主库,同时通知客户端;
配置更新:自动更新配置文件,记录新的主库地址;
通知:通过 API 向管理员 / 应用程序发送故障通知。
9.5、Redis 集群(Cluster)的原理是什么?
Redis Cluster 用于解决单节点容量和性能瓶颈,核心设计:
分片机制:将整个键空间分为 16384 个哈希槽(slot),每个节点负责一部分槽;
哈希分配:通过
CRC16(key) % 16384计算键对应的槽,路由到对应节点;主从架构:每个槽有 1 个主节点和多个从节点,主节点负责读写,从节点备份;
故障转移:主节点宕机时,从节点选举为新主节点;
无中心节点:客户端可直接连接任意节点,节点间通过