2023年java面试题

本文详细解答了2023年Java面试中涉及的关键问题,涵盖RocketMQ消息可靠性、消费者积压处理、Spring Security认证流程、Java中的哈希冲突解决、Spring Cloud与Dubbo的区别、Spring Boot和Spring Cloud的关系、内存管理、并发编程、数据库索引等多方面。内容深入浅出,旨在帮助面试者掌握核心技术并提升面试成功率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、RocketMQ消息可靠性怎么保证?
To:消息丢失可能发生在生产者发送消息、MQ本身丢失消息、消费者丢失消息三个方面
生产者丢失:
可能发生在程序发送异常了没有重试处理、或者发送成功但是网络出现问题导致MQ没有收到.这个时候消息就丢失了
处理方法:可以通过异步发送+回调通知+本地消息表的形式处理;
比如:
1.下单后先保存本地数据和MQ消息表,这个时候消息的状态是发送中,如果本地事物失败,那么下单失败,事物回滚
2.下单成功,直接返回客户端成功,异步发送MQ消息
3.MQ回调通知消息发送结果,对应更新数据库MQ发送状态
4.JOB轮询超过一定时间还未成功发送,就去重试
5.在监控平台JOB程序处理一定次数一直发送不成功,就告警,人工处理
MQ本身丢失:
可能发生在MQ收到消息后还在内存中,这时候宕机了又没来得及同步给节点,这时候导致消息丢失
处理方法:进行刷盘的处理,RocketMQ分为同步刷盘和异步刷盘两种方式,默认的是异步刷盘,就有可能导致消息还未刷到硬盘上就丢失了,可以通过设置为同步刷盘的方式来保证消息可靠性,这样即使MQ挂了,恢复的时候也可以从磁盘中去恢复消息.
消费者丢失:
可能发生在消费者刚收到消息,此时服务器宕机,MQ认为消费者已经消费,不会重复发送消息,消息丢失.
处理方法:RocketMQ默认是需要消费者回复ack确认,消费方不返回ack确认,重发的机制根据MQ类型的不同发送时间间隔、次数都不尽相同,如果重试超过次数之后会进入死信队列,需要手工来处理了.
2、如果消费者一直消费失败,消息积压怎么处理?
To:
1、消费者出错,肯定是程序或者其他问题导致,如果容易修复,先把问题修复,让消费者(consumer)恢复正常消费
2、做转发处理,写一个临时的消费方案,先把消息消费,然后转发到新的topic和MQ资源,这个topic单独申请,要能承载当时积压的消息
3、处理完积压后,修复consumer,去消费新的MQ和现有的MQ数据,新的MQ消费完成后恢复原状
3、RocketMQ实现原理?
To:RocketMQ由NameServer注册中心集群、Producer生产者集群、Consumer消费者集群和若干Broker(RocketMQ进程)组成
架构原理:
1、Broker在启动的时候去向所有的NameServer注册,并保持长连接,每30s发送一次心跳
2、Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择一台服务器来发送消息
3、Conusmer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费
4、Broker是怎么保存数据的?
To:Broker在收到消息之后,会把消息保存到commitlog的文件中
5、Master和Slave之间是怎么同步数据的?
To:
1、在broker收到消息之后,会被标记为uncommitted状态
2、然后会把消息发送给所有slave
3、slave在收到消息之后返回ack响应给master
4、master在收到超过半数的ack之后,把消息标记为committed
5、发送committed消息给所有slave,slave也修改状态为committed
6、Elasticsearch 与 Solr 的比较总结
To:
1、Solr利用Zookeeper进行分布式管理,而Elasticsearch自身带有分布式协调管理功能
2、Solr支持更多的数据类型,而Elasticsearch仅支持json文件格式
3、Solr在传统的搜索应用中表现好于Elasticsearch,但在处理实时应用时效率明显低于Elasticsearch
7、为什么用自增列作为主键?
To: 如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页
如果使用非自增主键,由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现在索引页的中间某个位置
此时,MySQL不得不移动数据,甚至每次把缓存清掉后,又重新从磁盘上读取出来,这增加了很多开销
8、为什么使用数据索引能提高效率?
To:
1、数据索引是有序的存储
2、在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
3、极端情况下,数据索引的查询效率为二分法查询效率,趋近于log2(N)
9、B+树索引和哈希索引的区别?
To:
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,是有序的
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需要一次哈希算法即可,是无序的
10、哈希索引的优势是什么?
To:
等级查询,哈希索引具有绝对优势(前提是:没有大量重复键值,如果大量重复键值时,哈希索引的效率很低,因为存在所谓的哈希碰撞问题)
11、哈希索引不适用的场景有哪些?
To:
1、不支持范围查询
2、不支持索引完成排序
3、不支持联合索引的最左前缀匹配规则
通常,B+树索引结构适用于绝大多数的场景,像下面这种场景用哈希索引才更有优势:
如果存储的数据重复度很低(也就是说基数很大),对该列数据以等值查询为主,没有范围查询、没有排序的时候,特别适合采用哈希索引
12、B树和B+树的区别是什么?
To:
B树,每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为null,叶子节点不包含任何关键字信息
B+树,所有的叶子节点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子节点本身依关键字的大小自小而大的顺序链接,所有的非终端节点可以看成是索引部分

13、java中如何解决哈希冲突?
To:
1、再哈希发:又叫双哈希法,有多个不同的hash函数,出现冲突后采用其他的哈希函数计算,直到不再冲突为止
2、拉链法:又叫链地址法,将具有相同哈希地址的记录链成一个单链表,m个哈希地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构
(拉链法优点:
1、拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短
2、由于拉链法中各链表上的节点空间是动态申请的,故它更适合于造表前无法确定表长的情况)

14、spring security的认证流程是怎么样的?
To:
1、用户发起表单登陆请求,首先进入UsernamePasswordAuthenticationFilter,在UsernamePasswordAuthenticationFilter中根据用户输入的用户名、密码构建了UsernamePasswordAuthenticationToken 并将其交给AuthenticationManager来进行认证处理
2、接着跳转到ProviderManager,该类是AuthenticationManager的实现类
3、AuthenticationManager跳转到AbstractUserDetailsAuthenticationProvider来进行处理
4、再次调用UserDetailsService类里面的UserDetails方法,将我们的用户存入UserDetails,最后进行Authentication认证
15、使用JWT认证原因是什么?
To:
支持用户通过用户名和密码登录,登录后通过http header返回token,每次请求,客户端需要通过header将token带回,用于权限校验;服务端负责token的定时刷新
16、SpringClound和Dubbo有哪些区别?
To:
dubbo是二进制传输,占用带宽少一点.SpringClound是http传输,带宽会多一点,同时使用http协议一般会使用JSON报文,消耗会更大
dubbp开发难度较大,所依赖的jar包有很多问题大型工程无法解决.SpringClound对第三方的继承可以一键式生成,天然集成
SpringClound接口协议约定比较松散,需要强有力的行政措施来限制接口无序升级
最大的区别:
SpringClound抛弃了Dubbo的RPC通信,采用的是基于HTTP和REST方式
17、SpringBoot和SpringClound之间的关系?
To:
SpringBoot专注于快速方便的开发单个个体微服务;SpringClound关注全局的微服务协调治理框架,将SpringBoot开发的一个个单体微服务组合并管理起来
SpringBoot可以离开SpringClound独立使用,但是SpringClound不可以离开SpringBoot,属于依赖关系.
18、什么是熔断?什么是服务降级?
To:
服务熔断的作用类似于家用的保险丝,当服务出现不可用或者响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用
服务降级时从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了防止某些功能出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback错误处理信息.这样,虽然提供的是一个有损的服务,但是却保证了整个系统的稳定性和可用性
19、eureka和zookeeper的区别是什么?
To:
zookeeper是CP原则,强一致性和分区容错性
eureka是AP原则,可用性和分区容错性
zookeeper当主节点故障时,zk会在剩余节点重新选择主节点,耗时过长,虽然最终能够恢复,但是选取主节点期间会导致服务不可用,这是不能容忍的
eureka各个节点时平等的,一个节点挂掉,其他节点仍会正常保证服务

20、Hystrix的使用方式?
To:
Hystrix翻译过来就是“豪猪”的意思.它的设计目的:
1⃣️:通过第三方客户端的库来访问依赖服务时的潜在故障提供保护和控制
2⃣️:防止在复杂分布式系统中出现级联故障
3⃣️:快速失败和迅速恢复
4⃣️:在允许的情况下,提供退路对服务进行优雅降级
5⃣️:提供近实时的监控、报警和操作控制
使用方式:
现在pom文件引入依赖;再修改启动类,在启动类上加上@EnableCircuitBreaker注解,也可以直接使用@SpringCloundApplication注解来代替@SpringBootApplication(或者@EnableEurekaServer)、@EnableDiscoveryClient、@EnableCircuitBreaker这三个注解。在进行修改Controlle,在方法上面增加断路器功能(注解 @HystrixCommand(fallbackMethod = “getMsgFallback”)fallbackMethod(退回的方法名)
)
Feign结合Hystrix:通过配置@FeignClient注解的fallback属性来指定fallback处理类
修改yml配置,Feign默认是关闭Hystrix的支持的
21、JWT的token是怎么解析的?
To:
获取token之后,使用jwts.parse的climes方法进行解析,因为解析数据存放在climes里面,从climes里面或取数据即可

22、Feign的原理是什么?
To:
Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,返回给调用者.
23、synchronized 和Lock区别?
To:
synchronized存在jvm层面上,lock是一个类; synchronized性能少量同步,lock大量同步; synchronized锁状态无法判断,lock可以判断

24、什么是SQL注入?

To:
	SQL注入是通过把SQL命令插入到web表单提交或通过页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL指令。

25、mybatis中的#{} 为什么能防止sql注入,${}不能防止sql注入?

To:
	 #{}在mybatis中的底层是运用了PreparedStatement 预编译,传入的参数会以 ? 形式显示,因为sql的输入只有在sql编译的时候起作用,当sql预编译完后,传入的参数就仅仅是参数,不会参与sql语句的生成,而${}则没有使用预编译,传入的参数直接和sql					
              进行拼接,由此会产生sql注入的漏洞。

26、父类的静态方法能否被子类重写?
To:
不能,因为重写的定义是重写父类的虚函数,虚函数式动态绑定的,而静态方法是静态绑定的,所以静态必然不是虚函数,就不存在重写之说了
27、mysql行锁和表锁的区别是什么?
To:
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
28、双亲委派模型。 向上委托 向上委托
TO:
程序运行类加载器--------->扩展类加载器--------->引导类加载器

29、垃圾回收算法
TO:
标记-清楚算法
标记-复制算法
标记-整理算法

30、什么是内存泄漏?什么是内存溢出?
TO:
内存溢出:程序申请内存时,没有足够空间存放
内存泄漏:程序运行后,没有被释放占用的内存空间,垃圾回收器无法进行回收,时间长了,就导致内存泄漏,堆积一定程度就会产生内存溢出
资源未关闭,长时间也会导致内存泄漏
31、类加载过程
TO:
java程序运行------->javac编译.class文件------>链接(验证、准备、解析)------->初始化------->使用-------->卸载

32、创建线程池的方法(7种)
TO:
1⃣️:Executors.newFixedThreadPool
创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待
2⃣️:Executors.newCachedThreadPool
创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后回收,若线程数不够,则新建线程

	3⃣️:Executors.newSingleThreadExecutor
	创建单个线程数的线程池,它可以保证先进先出的执行顺序
	4⃣️:Executors.newScheduledThreadPool
	创建一个可以执行延迟任务的线程池

	5⃣️:Executors.newSingleThreadScheduledExecutor
	创建一个单线程的可以执行延迟任务的线程池

	6⃣️:Executors.newWorkStealingPool
	创建一个抢占式执行的线程池(任务执行顺序不确定)[JDK1.8添加]

	7⃣️:ThreadPoolExecutor
	最原始的创建线程池的方式,它包含7个参数可供设置

33、hashMap底层原理是什么?阈值是多少?什么时候转红黑色?
TO:
HashMap底层原理是使用哈希表来实现的,通过哈希算法将键映射到一个固定大小的数组中,然后将值存储在数组中对应的位置中。当发生哈希冲突时,采用链表或红黑树的方式来解决。HashMap的哈希函数设计是将键的哈希值与table数组长度减1进行与运算,得到的结果即为键在table数组中的下标。阈值是指容量和负载因子的积,用于判断何时需要进行扩容操作。当HashMap中元素数量超过了阈值时,就会进行扩容操作。
默认的负载因子为0.75,即当元素占用空间超过了总空间的75%!时(MISSING),就需要进行扩容操作。阈值的默认值为16。在JDK1.8中,当链表的长度超过8时,会将链表转换为红黑树。当红黑树的节点数小于6时,会将红黑树转换为链表。这是为了提高在哈希冲突的情况下的查找性能。因为当链表长度过长时,查找效率会急剧下降,而红黑树可以在O(log n)的时间内完成查找、插入、删除等操作,相比之下,链表需要O(n)的时间。

34、前端发送请求到spring boot ,Spring boot怎么处理请求返回数据的?
TO:
前端发送请求到Spring Boot后端时,Spring Boot会根据请求的方法(如GET、POST、PUT、DELETE等)以及请求的URL路径来匹配相应的控制器方法。在控制器方法中,Spring Boot可以通过注解来获取请求参数、请求头、请求体等信息,并根据业务逻辑进行处理,最终返回数据给前端。
常用的注解包括:
@RequestMapping:映射URL和方法,可以指定HTTP请求方法和请求参数等。
@PathVariable:获取URL中的参数。
@RequestParam:获取请求参数。
@RequestBody:获取请求体中的数据。
控制器方法可以返回不同的数据类型,包括:
ModelAndView:返回一个ModelAndView对象,可以指定视图和模型数据。
String:返回一个视图名称,由视图解析器解析成实际的视图。
Object:返回一个对象,Spring Boot会自动将其转换成JSON格式返回给前端。
ResponseEntity:返回一个HTTP响应,可以指定状态码、头部信息和响应体等。
在处理完请求后,Spring Boot会根据返回的数据类型和响应头信息将数据封装成HTTP响应发送给前端。

35、ArrayList和LinkedList区别是什么?
TO:
ArrayList和LinkedList是Java中两种不同的数据结构,分别基于数组和链表实现。它们的主要区别如下:
底层实现
ArrayList底层实现是基于数组,因此它支持随机访问,可以通过下标快速访问元素。LinkedList底层实现是基于双向链表,因此它不支持随机访问,只能通过遍历来访问元素。
内存分配
ArrayList在初始化时会分配一块连续的内存空间,因此它的内存分配效率较高。而LinkedList每个节点都需要分配内存空间,因此内存分配效率较低。
插入和删除操作
ArrayList在插入和删除元素时需要移动元素,因此插入和删除操作的时间复杂度为O(n)。而LinkedList只需要修改节点的指针,因此插入和删除操作的时间复杂度为O(1)。
迭代器
ArrayList的迭代器是快速失败的,即在迭代过程中如果发现有其他线程修改了集合,就会抛出ConcurrentModificationException异常。而LinkedList的迭代器是弱一致性的,即在迭代过程中允许其他线程修改集合,不会抛出异常。
综上所述,ArrayList适合随机访问,对于频繁的插入和删除操作效率较低;而LinkedList适合频繁的插入和删除操作,对于随机访问效率较低。因此,在选择使用哪种数据结构时,应根据实际需求和场景进行选择。

36、主键索引和普通索引区别是什么?
TO:
主键索引和普通索引的区别如下:
唯一性
主键索引要求主键值唯一,而普通索引没有这个要求。
索引类型
主键索引是一种特殊的唯一性索引,用于对表中的主键列建立索引;而普通索引是一般性的索引,可以对表中的任意列建立索引。
索引结构
主键索引的索引结构是B+树,由于主键值唯一,因此在B+树中只有一个叶子节点;而普通索引的索引结构也是B+树,但在叶子节点上可能会有多个索引值相同的记录。
查询效率
主键索引的查询效率通常比普通索引的查询效率高,因为主键索引的B+树层数较少,查询时需要比较的节点数量较少。
空间占用
由于主键索引要求主键值唯一,因此主键索引会占用较少的空间;而普通索引可能会存在重复值,因此需要占用更多的空间。
总之,主键索引和普通索引都是用于提高查询效率的技术手段,但在具体使用时需要根据实际情况选择合适的索引类型。
37、hashMap底层原理解析
TO:
HashMap是一种常用的哈希表实现,用于存储键值对。其底层原理包括以下几个方面:
数组+链表/红黑树
哈希表的底层数据结构通常是数组+链表或数组+红黑树。数组是哈希表的主体,链表或红黑树用于解决哈希冲突。
哈希函数
哈希函数用于将键映射到数组下标。Java的哈希函数使用了键的hashCode方法和位运算。
扩容机制
当哈希表中元素数量达到一个阈值时,需要进行扩容。扩容会创建一个更大的数组,并将所有元素重新哈希到新数组中。扩容会消耗较大的时间和空间,因此需要在设计哈希表时考虑较为合理的扩容机制。
线程安全
HashMap在多线程环境下不是线程安全的,因为可能会出现多个线程同时修改一个链表或红黑树的情况。Java提供了ConcurrentHashMap类来解决这个问题。
总体来说,HashMap的底层原理比较复杂,需要考虑多个方面的因素,包括哈希函数、哈希冲突解决、扩容机制等等。在使用HashMap时,需要根据具体情况进行合理的配置和使用,避免出现性能问题和线程安全问题。
38、五百万级别的数据,如何保证线程安全?
TO:
在五百万级别的数据中,如果需要保证线程安全,可以考虑使用ConcurrentHashMap来代替HashMap。
ConcurrentHashMap是Java中的线程安全的哈希表实现,它采用了分段锁的机制,将整个哈希表分成了多个小的哈希表段,每个小的哈希表段都是一个独立的锁,不同的线程可以同时操作不同的小哈希表段,从而提高了并发性能。同时,ConcurrentHashMap在扩容时也采用了分段的方式,不会像HashMap一样对整个哈希表进行扩容,从而减少了扩容时对其他线程的影响。
除了使用ConcurrentHashMap,还可以使用synchronized关键字来实现线程安全。在使用HashMap的时候,需要对访问HashMap的代码块进行加锁,防止多个线程同时对HashMap进行修改而导致线程安全问题。
需要注意的是,使用synchronized关键字会对性能产生一定的影响,因此在高并发场景下,推荐使用ConcurrentHashMap来保证线程安全。

39、Java对一个接口实现限流处理详细说明?
TO:
java对一个接口实现限流处理的步骤如下:

	定义接口:首先需要定义需要进行限流的接口,例如:
	public interface UserService {
	    User getUserById(int id);
	}
	添加限流注解:使用AOP的方式来实现限流,可以在接口方法上添加限流注解,例如:
	@RateLimiter(key = "getUserById", limit = 10, timeUnit = TimeUnit.SECONDS)
	User getUserById(int id);
	其中,@RateLimiter是一个自定义注解,key表示限流的名称,limit表示限流的阈值,timeUnit表示限流的时间单位。

	实现限流逻辑:在AOP的切面中实现限流逻辑,例如:
	@Aspect
	@Component
	public class RateLimiterAspect {

	    private Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();

	    @Around("@annotation(rateLimiter)")
	    public Object limit(ProceedingJoinPoint point, RateLimiter rateLimiter) throws Throwable {
	        String key = rateLimiter.key();
	        int limit = rateLimiter.limit();
	        TimeUnit timeUnit = rateLimiter.timeUnit();
	        RateLimiter limiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(limit, timeUnit));
	        if (limiter.tryAcquire()) {
	            return point.proceed();
	        } else {
	            throw new RuntimeException("接口限流");
	        }
	    }
	}
	在RateLimiterAspect类中,首先定义了一个ConcurrentHashMap来存储限流器,通过computeIfAbsent方法来创建限流器。在limit方法中,先获取接口名称,阈值和时间单位,然后获取对应的限流器,使用tryAcquire方法来判断是否可以通过限流器。如果可以通过,则调用接口方法,否则抛出限流异常。

	配置AOP:最后需要在Spring Boot的配置文件中配置AOP,例如:
	spring:
	  aop:
	    auto: true
	通过以上步骤,就可以对一个接口实现限流处理。注意,为了保证限流器的线程安全,需要使用ConcurrentHashMap来存储限流器,同时需要在AOP的切面中使用线程安全的方式来获取和使用限流器。

40、百万级别的用户,对接口怎么作限流?
TO:
对于百万级别的用户,需要实现高效的限流算法和分布式限流方案来确保接口的稳定性和可用性。以下是一些实现限流的建议:
采用分布式限流方案:对于高并发的应用系统,单机限流很难满足需求,需要采用分布式限流方案。可以使用Redis等分布式缓存来实现分布式限流,将限流器的状态存储在Redis中,所有的应用节点共享同一个限流器状态。
选择合适的限流算法:不同的限流算法适用于不同的场景,需要根据具体的情况选择合适的算法。例如,计数器限流适用于请求处理时间较长的场景,漏桶限流适用于请求处理时间较短的场景。
合理设置限流参数:需要根据实际情况合理设置限流参数,例如,阈值和时间单位。阈值需要根据接口的处理能力和负载情况来设置,时间单位需要根据接口的请求速率和限流算法来设置。
动态调整限流参数:对于百万级别的用户,请求量会有较大的波动,需要动态调整限流参数来保证接口的稳定性和可用性。可以通过监控系统来实时监控接口的负载情况,根据负载情况动态调整限流参数。
优化接口性能:优化接口性能可以减少接口的响应时间,从而减少请求积压和限流的概率。可以通过优化SQL语句、使用缓存等方式来提高接口性能。
综上所述,对于百万级别的用户,需要采用分布式限流方案,选择合适的限流算法,合理设置限流参数,动态调整限流参数和优化接口性能来确保接口的稳定性和可用性。

41、sql优化怎么详细排查?
TO:
使用SQL分析工具:可以使用SQL分析工具来分析SQL语句的执行计划、消耗时间、IO等信息。常用的SQL分析工具有:Explain、Optimize、Profiler、Performance Schema等。
检查索引使用情况:索引是提高查询性能的重要手段,需要检查SQL语句是否使用了索引。可以使用Explain命令查看索引使用情况,判断是否需要增加或修改索引。
减少全表扫描:全表扫描是数据库性能问题的主要原因之一,需要尽量减少全表扫描。可以通过增加索引、使用分区表、优化查询条件等方式来减少全表扫描。
减少重复查询:重复查询也是数据库性能问题的原因之一,需要尽量减少重复查询。可以使用缓存技术、优化查询语句、增加索引等方式来减少重复查询。
减少数据传输量:数据传输量也是数据库性能问题的原因之一,需要尽量减少数据传输量。可以使用分页查询、优化查询语句、减少字段查询等方式来减少数据传输量。
使用合适的数据类型:使用合适的数据类型可以减少数据存储和传输的开销,提高数据库性能。需要根据数据类型的实际需求来选择合适的数据类型,避免不必要的数据类型转换。
对大表进行分析和优化:对于大表,需要进行分析和优化,避免影响整个系统的性能。可以使用分区表、水平分表、垂直分表等方式来优化大表。
综上所述,SQL优化需要使用SQL分析工具、检查索引使用情况、减少全表扫描、减少重复查询、减少数据传输量、使用合适的数据类型和对大表进行分析和优化。

---------------------------------------------------------------------------------Redis-------------------------------------------------------------------------------------
1、redis为什么这么快?
TO:
redis基于内存,内存的访问速度是磁盘的上千倍
redis基于Reactor模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和IO多路复用
redis内置了多种优化过后的数据结构实现,性能非常高
2、什么是redis的雪崩、穿透和击穿?
To:
雪崩:缓存机器意外发生了全盘宕机.缓存挂了,数据请求全部落数据库,数据库扛不住,它会报警一下,然后就挂了.这就是缓存雪崩
解决方案如下:
事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃
事中:本地ehcache缓存+hystrix限流&降级,避免MySQL被打死
事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据
穿透:缓存中查不到,每次去数据库里查,也查不到(视缓存于无物)
解决方案如下:
每次系统从数据库中只要没查到,就写一个空值到缓存里去
击穿:就是说某个key非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个key在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库
解决方案如下:
可以将热点数据设置为永不过期;或者基于redis or zookeeper实现互斥锁,等待第一个请求构建完缓存后,在释放锁,进而其它请求才能通过该key访问数据
3、redis的过期策略是什么?
To:
定时删除:redis会将每个设置了过期时间的key放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的key
懒惰删除:所谓懒惰策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西

4、Redis中两种持久化机制是什么?
To:
RDB和AOF
RDB机制:RDB其实就是把数据以快照的形式保存在磁盘上;RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘.也是默认的持久化方式,这种方式是将内存中数据以快照的方式写入到二进制文件中.
它的触发机制为:
1、save触发方式:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止
2、bgsave触发方式:执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求
3、自动触发:自动触发是由我们的配置文件来完成的
RDB的优势和劣势:
优势:
(1)、RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复
(2)、RDB在恢复大数据集时的速度比AOF的恢复速度要快
劣势:
可能会丢失数据(因为它存储的是内存数据的二进制序列化形式,存储上非常紧凑)
AOF机制:redis会将每一个收到的写命令都通过write函数追加到文件中(简称日志记录)
它的触发机制为:
1、每修改同步always:同步持久化每次发生数据变更会被立即记录到磁盘,性能较差,但数据完整性比较好
2、每秒同步eversec:异步操作,每秒记录,如果一秒内宕机,有数据丢失
3、不通no: 从不同步
AOF的优势和劣势:
优势:
(1)、AOF可以更好的保护数据不丢失
(2)、AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损
(3)、AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写
劣势:
(1)、对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
(2)、出现bug,进行数据恢复,没有恢复出一摸一样的数据出来
总结:
RDB:在指定的时间间隔能对你的数据进行快照存储
AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据

5、Redis常见的数据结构以及使用场景是什么?
To:
1、String:String数据结构时简单的key-value类型,value其实不仅可以是String,也可以是数字.常规key-value缓存应用
2、Hash:Hash是一个String类型的field和value的映射表,hash特别适合用于存储对象,后续操作的时候,可以直接仅仅修改这个对象中的某个字段的值,比如我们可以Hash数据结构用来存储用户信息,商品信息等等
3、List:List就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的list结构来实现
4、Set:Set对外提供的功能与list类似一个列表的功能,特殊之处在于set是可以自动排重的
5、Sorted Set:和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按照score进行有序排列
6、Redis 如何保证缓存与数据库双写时的数据一致性?
To:
解决方式:
1、如果是强一致性,读请求和写请求串行化,串到一个内存队列里面去,这样就保证一定不会出现不一致的情况(效率极低)
2、先更新数据库,再删除缓存(可能会暂时产生不一致的情况,但是发生的几率特别小);解决方案,首先给缓存设置有效时间是一个方案.其次,采用异步延时删除策略,保证读请求完成之后,再进行删除操作
7、为什么Redis是单线程的?
To:
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络宽带.Redis线程是安全的
8、Redis单线程为什么还能那么快?
To:
因为Redis是基于内存的,内存的读写速度非常快;Redis是单线程的,避免了不必要的上下文切换和竞争条件;Redis使用多路复用技术,可以处理并发的连接.
9、redis的缺点是什么?
To:
是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

---------------------------------------------------------------------------------MySql-------------------------------------------------------------------------------------
1、B树和B+树之间的区别是什么?
TO:
B+树是B树的一种变种,B+树的叶子节点存储的数据,保证了数据的完整性,叶子节点类似于一个单链表,非叶子节点存储的是数据和指向下一个节点的指针,增加了区间访问性,提高了范围查询
B树的键值分布在整棵树中(索引和数据都在每个节点里),查询时IO操作不及B+树,查询需要叶子节点和内部节点不停的往返移动
相对两者,B+树能够更有效的减少查询时间,减少了IO操作

2、Innodb中B+树有什么特点?
TO:
非叶子节点不会带上rowid,降低了树的高度,内部节点可以定位更多的叶子节点,叶子节点之间通过指针来连接,范围扫描将十分简单

3、什么是Innodb中的page?
TO:
是存储的基本结构,也是InnoDB管理的最小单位,与数据库相关的内容都存储在page结构中,InnoDB中每页的大小为16kb,每个Page使用一个32位的int值来表示,正好对应InnoDB最大64TB的存储容量

4、什么是聚簇索引?和非聚簇索引有什么区别?
TO:
聚簇索引一个表只能有一个,而非聚簇索引一个表可以存在多个.聚簇索引存储记录是物理上连续存在,物理存储按照索引排序,而非聚簇索引是逻辑上的连续,物理存储并不连续,物理存储不按照索引排序
非聚簇索引顺序与数据物理排序顺序无关

5、Innodb是如何支持范围查找能走索引的?
TO:
找到数据的id大小。定位到数据在哪里,然后,在这个数据后面的数据就是大于这个id的数据返回数据就行了

6、什么是联合索引?对应的B+树是如何生成的?
TO:
是指对表上的多个列进行索引,联合索引也是一颗B+树,存储引擎首先从根节点开始查找,第一个索引的第一个索引列为1,12大于1,第二个索引的第一个索引列为56,12小于56,于是从这俩索引的中间读到下一个节点的磁盘文件地址;
进行一次磁盘IO,将此节点值加载到内存中,然后根据第一步一样进行判断,发现数据都是匹配的,然后根据指针将此联合索引值所在的叶子节点也从磁盘中加载到内存,此时又发生了一次磁盘IO,最终根据叶子节点中索引值
关联的主建值;
根据主键值回表去主键索引树(聚簇索引)中查询具体的行记录

7、什么是最左前缀原则?
TO:
最左优先,以最左边的为起点任何连续的索引都能匹配上
1⃣️、如果第一个字段是范围查询需要单独键一个索引
2⃣️、在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边

8、为什么要遵守最左前缀原则才能利用到索引?
TO:
如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)

9、什么是索引条件下推?
TO:
当使用索引条件下推优化时,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器
索引下推能减少回表查询次数,提高查询效率

10、什么是覆盖索引?
TO:
覆盖索引就是把查询出的列和索引是对应的,不做回表操作。如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”

11、有哪些情况会导致索引失效?
TO:
1⃣️、有or必全有索引
2⃣️、复合索引未用左列字段
3⃣️、like以%开头
4⃣️、需要类型转换
5⃣️、where中索引列有运算
6⃣️、where中索引列使用了函数
7⃣️、如果mysql觉得全表扫描更快时

12、Mysql有哪些锁类型?
TO:
1⃣️、排它锁:排它锁允许持有锁,更新或删除行的事务
2⃣️、共享锁:共享锁允许持有锁,读取行的事务。
3⃣️、意向锁:意向锁是表级锁,指示事务稍后需要对表中的行使用哪些类型的锁,即用来标识该表上面有数据被锁住
4⃣️、间隙锁:间隙锁是对索引记录之间的间隙,锁定一个区间:加在两个索引之间,或者加在第一个索引之前,或者放在最后一个索引之后的间隙(注意:间隙锁锁住的是一个区间,而不仅仅是这个区间中目前仅存在的数据行)
5⃣️、记录锁;属于为行锁,表示对某一行记录加锁,记录锁总是锁定索引记录,即使表没有定义索引
6⃣️、全局锁:让整个库处于只读状态的时候,可以使用这个命令
7⃣️、表级锁:是MySQL中最大粒度的锁定机制,会锁定整张表,可以很好的避免死锁,是MySQL中最大颗粒度的锁定机制
8⃣️、行级锁:MySQL的行锁是在引擎层由各个引擎自己实现的,且锁定颗粒度在MySQL中最小的,行锁主要应用于InnoDB存储引擎
9⃣️、页级锁:页锁的颗粒度介于行锁与表锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间,另外,页锁和行锁一样,会发生死锁,页锁主要应用于BDB存储引擎

13、Mysql执行计划怎么看?
TO:
explan select * from 表名
每一列的含义:
id:表示查询中select子句或操作表的顺序,执行顺序从大到小执行,当id一样的时候执行顺序由上往下
select_type:表示查询中每个select子句的类型
table:表名称
type:表示MySQL在表中找到所需要行的方式,又称“访问类型也是索引类型”
possible_keys:指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
key:显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。当查询中若使用了覆盖索引,则该索引仅出现在key列表中
key_len:表示索引中的字节数,可通过该列计算查询中使用的索引的长度
ref:表示上述表的连接匹配条件,即那些列或常量被用于查找索引列上的值
rows:表示MySQL根据表统计信息及索引选用情况,估算的找到所需要的记录所需要读取的行数
extra:包含不适合在其他列中显示但十分重要的额外信息

14、ACID靠什么保证的?
TO:
A原子性由undo.log日志保证,它记录了需要回滚的日志信息,事物回滚时撤销已经执行成功的sql
C一致性由其他三大特性保证、程序代码要保证业务的一致性
I隔离性由MVCC来保证
D持久性由内存+redo.log来保证,mysql修改数据同时在内存和redo.log记录这次操作,宕机的时候可以从redo.log恢复
InnoDB redo.log写盘,InnoDB事物进入prepare状态,
如果前面prepare成功,binlog写盘,再继续将事物日志持久化到binlog,如果持久化成功,那么InnoDB事物则进入commit
redolog的刷盘会在系统空闲时进行
15、什么是MVCC?(重要)
TO:
MVCC是多版本并发控制,它是MySQL中的提高性能的一种方式,配合Undo log和版本链,替代锁,让不同事物的读写、写读操作都可以并发的执行,从而提升系统的性能
MVCC在MySQL InnoDB中实现主要是为了提高数据库并发性能,一般是在使用读已提交和可重复读隔离级别的事务中实现
16、幻读和不可重复读的区别?
TO:
不可重复读:一个事务在读区某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变或某些记录已经被删除
总的来看,两者都是对数据进行了两次查询,但两次查询的结果都不一样
但是不可重复读针对的是更新和删除,幻读针对的是插入
解决方案也不一样,不可重复读采用行锁和可重复读的事务隔离级别可以解决
17、什么是表分区?
To:
表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分.从逻辑上看,只有一张表,但是底层却是由多个物理分区组成

18、MySql四种隔离级别是什么?
To:
1、串行化:可避免脏读、不可重复读、幻读的发生
2、可重复读:可避免脏读、不可重复读的发生(MySQL默认隔离级别)
3、读已提交:可避免脏读的发生
4、读未提交:最低级别,任何情况都无法保证
19、MySQL的行级锁定的优点是什么?
To:
1、当在许多线程中访问不同的行时只存在少量锁定冲突
2、回滚时只有少量的更改
3、可以长时间锁定单一的行
20、MySQL的行级锁定的缺点是什么?
To:
1、比页级或表级锁定占用更多的内存
2、当在表的大部分中使用时,比页级或者表级锁定速度慢,因为你必须获取更多的锁
3、如果你在大部分数据上经常进行GROUP BY操作或者必须经常扫描整个表,比其它锁定明显慢很多
4、用高级别锁定,通过支持不同的类型锁定,你可以很容易地调节应用程序,因为其锁成本小于行级锁定
21、MySQL的优化方式?
To:
1、开启查询缓存,优化查询
2、当只要一行数据时使用limit 1
3、为搜索字段建索引
4、垂直分表
22、MySQL的水平分表和垂直分表的区别是什么?
To:
1、水平分表:比如(QQ的登录表。假设QQ的用户有100亿,如果只有一张表,每个用户登录的时候数据库都要从这100亿中查找,会很慢很慢。如果将这一张表分成100份,每张表有1亿条,就小了很多)这就是水平分表
2、垂直分表:表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。
23、什么情况下应不建或者少建索引?
To:
1、表记录太少
2、经常插入、删除、修改的表
3、数据重复且分布平均的表字段
4、经常和主字段一块查询但主字段索引值比较多的表字段
24、为什么非主键索引结构叶子节点存储的是主键值?
To:
一是保证一致性,更新数据的时候只需要更新主键索引树,二是节省存储空间
25、为什么推荐使用整型的自增主键?
To:
一是方便查找比较,二是新增数据的时候只需要在最后加入,不会大规模调整树结构,如果是UUID的话,大小不好比较,新增的时候也极有可能在中间插入数据,会导致树结构大规模调整,造成插入数据变慢
26、索引引擎InnoDB的索引结构是什么?
To:
有两个索引结构:主键ID索引和普通索引.主键索引的叶子节点存储的是行数据的内容,普通索引的叶子节点存储的是主键的值
主键索引和普通索引的区别在于,主键索引查询语句的时候,只需要一次主键索引树的查找记录行即可返回数据行;普通索引查找时需要查找两个索引树的结构,先查找记录的主键值,然后再根据主键索引查找对应的记录行返回数据
27、最左前缀匹配规则是什么?
To:
在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引(联合索引就是可以以一定顺序引用多列)的最左边开始匹配
由于索引的底层是一颗B+树,那么联合索引的底层也是一颗B+树,只不过联合索引的B+树节点中存储的是键值.由于构建一颗B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建

28、mysql一级缓存存在哪里,Mysql中一级缓存二级缓存区别?
To:
一级缓存:也被称为本地缓存,一级缓存一直是开启的;与数据库同一次会话期间查询到的数据会放在本地缓存中,如果需要获取相同的数据,直接从缓存中获取,不会再查数据库
二级缓存:也被称为全局缓存,基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制: 1、一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中
2、如果会话关闭了,一级缓存中的数据会被保存带到二级缓存.新的会话查询信息就会参照二级缓存
29、什么情况下索引会失效?
To:
1、如果条件中有or会导致索引失效
2、like查询是以%开头

30、如何让用sql语句实现乐观锁和悲观锁?
To:
乐观锁实现:
select (value, version) from task where id = #{id}
update task set value = new value,version=versionValue+1 where id=#{id} and version=versionValue
悲观锁实现:
start transaction;//开启事物(手动)
select status from t_goods where id = 1 for update;
insert into t_orders(id,goods_id)values(null,i);
update t_goods set status=2;
commit;//提交事物

31、mysql事务如何保证持久化的?
TO:
MySQL事务的持久化通常通过两种方式来保证:日志和缓冲区。
日志:MySQL使用两种日志来保证事务的持久化。一种是redo日志,记录了事务执行过程中对数据所做的修改操作。当MySQL发生崩溃等异常情况时,可以通过redo日志将数据恢复到事务执行前的状态。另一种是binlog日志,记录了事务执行的SQL语句。binlog日志主要用于主从复制和恢复数据等操作。
缓冲区:MySQL使用缓冲区来提高事务执行的性能。缓冲区中的数据在事务执行过程中不会立即写入磁盘,而是在事务提交时才进行写入。这样可以减少磁盘I/O操作,提高性能。同时,MySQL还使用了WAL(Write-Ahead Logging)机制来保证事务的持久化。WAL机制要求在事务提交前,必须将redo日志写入磁盘。
综上所述,MySQL通过日志和缓冲区的方式来保证事务的持久化。日志记录了事务执行的过程,缓冲区提高了事务执行的性能。这些机制保证了即使在MySQL发生异常崩溃等情况下,数据也可以被恢复到事务执行前的状态,从而保证了数据的一致性和可靠性。

--------------------------------------------------------------------------------JVM--------------------------------------------------------------------------------------------
1、双亲委派模型。 向上委托 向上委托
程序运行类加载器--------->扩展类加载器--------->引导类加载器
1.1:怎么打破双亲委派模型?
TO:
1.自定义类加载,重写loadclass方法
因为双亲委派的机制都是通过这个方法实现的,这个方法可以指定类通过什么类加载器来进行加载,所以如果改写他的加载规则,相当于打破双亲委派机制
2.使用线程上下文类
双亲委派模型的第二次“破坏”是由这个模型自身的缺陷所导致的,双亲委派很好的解决了各个类加载器的基础类统一问题,基础类之所以“基础”,是因为他们总被用户代码所调用

2、垃圾回收算法
TO:
标记-清楚算法:速度快,会造成内存碎片
标记-复制算法:不会有内存碎片,需要占用双倍的内存空间
标记-整理算法:速度慢,没有内存碎片

3、什么是内存泄漏?什么是内存溢出?
TO:
内存溢出:程序申请内存时,没有足够空间存放
内存泄漏:程序运行后,没有被释放占用的内存空间,垃圾回收器无法进行回收,时间长了,就导致内存泄漏,堆积一定程度就会产生内存溢出
资源未关闭,长时间也会导致内存泄漏
4、类加载过程
java程序运行------->javac编译.class文件------>链接(验证、准备、解析)------->初始化------->使用-------->卸载

5、创建线程池的方法(7种)
TO:
1⃣️:Executors.newFixedThreadPool
创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待
2⃣️:Executors.newCachedThreadPool
创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后回收,若线程数不够,则新建线程

	3⃣️:Executors.newSingleThreadExecutor
	创建单个线程数的线程池,它可以保证先进先出的执行顺序
	4⃣️:Executors.newScheduledThreadPool
	创建一个可以执行延迟任务的线程池

	5⃣️:Executors.newSingleThreadScheduledExecutor
	创建一个单线程的可以执行延迟任务的线程池

	6⃣️:Executors.newWorkStealingPool
	创建一个抢占式执行的线程池(任务执行顺序不确定)[JDK1.8添加]

	7⃣️:ThreadPoolExecutor
	最原始的创建线程池的方式,它包含7个参数可供设置

6、JVM的内存模型
TO:

			javac
main.java---------->class file------------>类加载子系统(ClassLoader)
													|
													|
	垃圾回收器										|
		|											|
		|											|
		|											|	

| 线程隔离(公有)数据区 | | 线程隔离(私有)数据区 |
| – | | |
| |堆| | | ------- --------- |
| – | | |虚拟机栈| |本地方法栈| |
| ----- | | ------- --------- |
| |元空间| | | |程序计数器| |
| ----- | | --------- |


^	 |	   											|    ^
|	 |												|	 |
|	 |												|	 |
|	 |<-----------Java内存部分(运行时数据区)---------->|	 |
|														 |
 -------->执行引擎						本地接口<---------
			 ^						       ^  |
			 |							   |  |
			 |							   |  |
			  -----------------------------    -------->本地方法库------->C/C++语言

7、程序计数器是干嘛的?
TO:
是用来存放下一个指令所在单位的地址的地方

8、程序计数器是怎么工作的?具体流程是什么?
TO:
在程序开始执行前,必须将它的起始地址,即程序的一条指令所在的内存单元地址送入PC,因此程序计数器(PC)的内容即是从内存提取的第一条指令的地址,当执行指令时,CPU将自动修改PC的内容,即每执行一条指令PC
增加一个量,以此循环,执行每一条数据
9、程序计数器指向哪里?
TO:
指向下一个所在单位的地址
10、虚拟机栈里面有什么东西?具体存的什么?
TO:
它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回
1⃣️、局部变量,它是相比于成员变量来说的
2⃣️、基本数据类型变量VS引用类型变量(类、数组、接口)
11、虚拟机栈里面存代码吗?
TO:
不存,它存储执行代码所需要的数据.当一个方法被调用时,虚拟机会为该方法分配一个栈桢,用于存储方法的局部变量和操作数栈.随着方法的执行,栈桢会被不断压入和弹出虚拟机栈,因此,虚拟机栈可以看作是
方法调用的执行环境
12、redis 内存淘汰机制是什么?
TO:
六种内存淘汰机制
1⃣️:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
2⃣️:从已设置过期时间的数据集中挑选将要过期的数据淘汰
3⃣️:从已设置过期时间的数据集中任意选择数据淘汰
4⃣️:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个最常用的)
5⃣️:从数据集中任意选择数据淘汰
6⃣️:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错
4.0版本的redis后增加以下两种
1⃣️:从已设置过期时间的数据集中挑选最不经常使用的数据淘汰
2⃣️:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key

---------------------------------------------------------------------------Spring相关------------------------------------------------------------------------------------------
1、Spring Boot的主要执行流程
TO:
1、创建 Spring Application 实例,调用 run 方法,同时将启动入口类作 为参数传递进去,由此开始了 Spring Boot 内部相关核心组件以及配置的 启动和加载;
2、通过 Spring Factories Loader 加载 META-INF/spring.factories 文 件,获取并创建 SpringApplicationRunListener 对象;
3、然后由 SpringApplicationRunListener 来发出 starting 消息;
4、创建参数,并配置当前 SpringBoot 应用需要使用的 Environment 实 例;
5、完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息;
6、创建 Spring 的应用上下文实例:ApplicationContext,初始化该实例 并设置应用环境配置实例:Environment,同时加载相关的配置项;
7、由 SpringApplicationRunListener 发出 contextPrepared 消息,告 知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;
8、将各种 Bean 组件装载入 Spring 的 IO 容器/应用上下文: ApplicationContext 中,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;
9、重新刷新 Refresh Spring 的应用上下文实例:ApplicationContext, 完成 IOC 容器可用的最后一步;
10、由 SpringApplicationRunListener 发出 started 消息,完成最终的 程序的启动;
11、由 SpringApplicationRunListener 发出 running 消息,告知程序已 成功运行起来了。
2、Spring中的单例bean是单例模式吗?
TO:
Spring中的单例Bean也是一种单例模式,只不过范围比较小,范围是beanName,一个beanName对应同一个Bean对象,不同beanName可以对应不同的Bean对象(就算事同一个类也是可以的)
3、Spring的Bean的实例化和Bean的初始化有什么区别?
TO:
Spring在创建一个Bean对象时,会先创建出来一个Java对象,会通过反射来执行类的构造方法从而得到一个Java对象,而这个过程就是Bean的实例化
得到Java对象后,会进行依赖注入,依赖注入之后就会进行初始化了
4、Spring容器启动流程是怎么样子的?
TO:
1、在创建Spring容器,也就是启动Spring时
2、首先会进行扫描,扫描得到所有的BeanDefinition对象,并存在一个Map中
3、然后筛选出非懒加载的单例BeanDefinition进行创建Bean
4、利用BeanDefinition创建Bean就是Bean的创建生命周期,这期间包括了合并BeanDefinition、推断构造方法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中
5、单例Bean创建完了之后,Spring会发布一个容器启动事件
6、Spring启动结束
5、Spring 如何解决循环依赖的问题?
To:
即2个或以上bean互相持有对方,最终形成闭环,这就是循环依赖
Spring的循环依赖的场景:构造器的循环依赖;setter的循环依赖
如何检测是否有循环依赖?可以Bean在创建的时候给其打个标记,如果递归调用回来发现正在创建中的话,即可说明循环依赖
解决循环依赖如下:使用三级缓存,调整配置文件,将构造函数注入方式改为属性注入方式即可
这三级缓存分别指:
singIetonFactories:单例对象工厂的cache
earlySingletonObjects:提前曝光的单例对象的Cache
singLetonObjects:单例对象的cache
6、Spring事务失效的原因有哪些?
To:
数据库引擎不支持事务、没有被Spring管理、方法不是public、自身调用、异常被吃、异常抛出类型不对
7、Spring Bean的作用域:
singleton:在Spring Ioc容器中仅存在一个Bean实例,Bean以单例方式存在
prototype:每次从容器中调用Bean时,都返回一个新的实例
request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session:同一个HTTP Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境
8、spring的五种通知类型是什么,使用场景是什么?
To:
前置通知:记录日志(方法将被调用)
环绕通知:控制事物,权限控制
后置通知:记录日志(方法已经成功调用)
异常通知:异常处理,控制事务
最终通知:记录日志(方法已经调用,但不一定成功)
9、springAop的jdk代理和cglib的代理是怎么选择的?
To:
默认使用JDK动态代理,这样便可以代理所有的接口类型;如果被代理对象没有实现任何接口,就默认的是使用CGLIB代理.如果需要强制使用CGLIB,就指定proxy-target-class=“true”或者基于注解@EnableAspectJAutoProxy(proxyTargetClass=true)
10、如何将spring bean默认的单例变成多例?
To:
在bean上的scope属性值定为prototype
11、Spring注解@Resource和@Autowired区别对比是什么?
To:
@Autowired注解是按照类型(byType)装配依赖对象;@Resource默认是按照byName自动注入
@Resource装配顺序:
1⃣️:如果同时指定了name和type,则从spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2⃣️:如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常
3⃣️:如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常
4⃣️:如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
12、如何保证消息不被重复消费?(如何保证消息消费时的幂等性)
To:
如果你拿个数据要写库,你先根据主键查一下,如果这个数据都有了,就别插入,就update一下
如果你拿到这个消息做Redis的Set的操作,无论set几次,结果都一样,set操作本就算是幂等性
如果都不是上面这两种,那就准备一个第三方介质,来做消费记录.以Redis为例,需要让生产者发送每条数据的时候,里面加上一个全局唯一的id,只要消费过该消息,就将id和消息数据写入redis,消费者开始消费前,先根据这个id去查redis中有没有消费记录,消费过了,就别处理,保证别重复处理相同的消息即可
13、Spring AOP代理是什么?
To:
Spring AOP默认为AOP代理使用标准的JDK动态代理.这使得任何接口可以被代理.Spring AOP也可以使用CGLIB代理,如果业务对象没有实现任何接口,那么默认使用CGLIB
AOP(面向切面编程)主要的实现技术有Spring AOP和AspectJ
AspectJ的底层技术就是静态代理,用一种AspectJ支持的特定语言编写切面,通过一个命令来编译,生成一个新的代理类,该代理类增强了业务类,这是在编译时增强
JDK动态代理模式只能代理接口而不能代理类

14、spring中bean的生命周期?
To:
1、spring容器从XML文件中读取bean的定义,并实例化bean
2、spring根据bean的定义填充所有的属性
3、如果bean实现了BeanNameAware接口,Spring传递bean的ID到setBeanName方法
4、如果bean实现了BeanFactoryAware接口,Spring传递beanfactory给setBeanFactory方法
5、如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法调用它们
6、如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法
7、如果有BeanPostProcessors和bean关联,这些bean的postProcessAfterInitialization()方法将被调用
8、如果bean实现了DisposableBean,它将调用destroy()方法
15、spring事务管理的方式有几种?
To:
1、编程式事物:在代码中硬编码
2、声明式事物额:在配置文件中配置,分为基于XML的声明式事物和基于注解的声明式事物
16、spring事物中的隔离级别有哪几种?事物特性有哪几种?
To:
事物特性(4种):
1、原子性:强调事物的不可分割
2、一致性:事物的执行的前后数据的完整性保持一致
3、隔离性:一个事物执行的过程中,不应该受到其他事物的干扰
4、持久性:事物一旦结束,数据就持久到数据库
事物的隔离级别(5种):
1、default默认的隔离级别,使用数据库默认的事物隔离级别(可重复读)
2、未提交读:脏读,不可重复读,虚读都有可能发生
3、已提交读:避免脏读.但是不可重复读和虚读有可能发生
4、可重复读:避免脏读和不可重复读,但是虚读有可能发生
5、串行化的:避免以上所有读问题

17、Spring的ioC是什么?ioC表示控制反转,那么
1、什么是控制?控制了什么?
2、什么是反转?
TO:
这就是控制:
1⃣️:控制对象的创建
2⃣️:控制对象内属性的赋值
这就是反转:
只需要定义类,以及定义哪些属性需要Spring来赋值
18、Spring Boot的自动配置有什么作用?
TO:
自动配置的作用主要有以下几个方面:
减少配置量:Spring Boot的自动配置能够根据应用所引入的依赖,自动配置需要的Bean,从而减少了手动配置的工作量,提高了开发效率。
提高开发效率:自动配置可以帮助开发者快速搭建应用,让开发者更加关注业务逻辑的实现。
避免人为错误:自动配置能够避免人为错误,例如配置错误、依赖缺失等。
统一标准:Spring Boot的自动配置能够提供一致的配置方式,让开发者更容易理解和维护。
总之,Spring Boot的自动配置能够提高开发效率、降低开发成本、减少出错率,是Spring Boot的重要特性之一。
19、Spring Boot的自动配置是如何实现的?
TO:
Spring Boot的自动配置是通过Spring Framework中的条件化配置机制来实现的。条件化配置机制是Spring Framework提供的一种机制,它可以根据应用的环境和需要,对Bean进行自动配置或者排除。
Spring Boot的自动配置基于以下两个核心组件:
Starter依赖:Starter依赖是一种特殊的依赖,它可以引入一组相关的依赖,从而快速搭建一个具有特定功能的应用。Starter依赖中包含了一系列的自动配置类,这些类可以根据不同的条件来自动配置相应的Bean。
条件化配置:SpringBoot的自动配置是基于条件化配置实现的。条件化配置通过@Conditional注解实现,它可以根据应用的环境和需要,判断是否需要自动配置相应的Bean。条件化配置可以通过配置文件、系统属性等方式进行配置。
Spring Boot的自动配置过程大致如下:
应用启动时,Spring Boot会扫描所有的Starter依赖,并加载其中的自动配置类。
自动配置类中通过@Conditional注解判断当前应用的环境和需要,决定是否需要进行自动配置。
如果需要自动配置,则自动配置类会根据一定的规则,自动配置相应的Bean,并将其注册到Spring容器中。
如果不需要自动配置,则自动配置类不会进行任何操作。
总之,Spring Boot的自动配置是通过Starter依赖和条件化配置机制实现的,它可以根据应用的环境和需要,自动配置相应的Bean,从而提高开发效率和降低开发成本。
20、Spring boot的自动配置类和Spring配置类有啥区别?
TO:
Spring Boot的自动配置类和Spring配置类的区别在于它们的作用和加载方式不同。
Spring配置类是常规的Spring Bean配置类,通过在类上加上@Configuration注解,然后在类中使用@Bean注解定义Bean。这些配置类需要手动的被加载到Spring容器中,一般是通过在Application类中使用@Import注解或者@ComponentScan注解来加载。
而Spring Boot的自动配置类是被Spring Boot框架自动加载到Spring容器中的,它们也是使用@Configuration注解定义的Bean配置类,但是它们通过条件化注解来判断是否需要自动配置相应的Bean。自动配置类一般是在Starter中定义的,当引入Starter依赖时,自动配置类会被自动加载到Spring容器中。
因此,Spring Boot的自动配置类和Spring配置类的主要区别在于:
加载方式不同:Spring配置类需要手动加载到Spring容器中,而Spring Boot的自动配置类是被Spring Boot框架自动加载到Spring容器中的。
条件化配置:Spring Boot的自动配置类通过条件化配置来判断是否需要自动配置相应的Bean,而Spring配置类没有这个功能。
作用不同:Spring配置类是用来定义Bean的配置类,而Spring Boot的自动配置类是用来自动配置Bean的配置类。
总之,Spring Boot的自动配置类和Spring配置类虽然都是使用@Configuration注解定义的Bean配置类,但是它们的作用和加载方式有所不同,开发者需要根据具体的需求选择使用哪种类型的配置类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值