Java面试题库

Java面试题库

小颜同学 Lv4

一,基础

1、Java都有哪些数据类型?基本数据类型有哪些?分别占多少字节?多少位?引用数据类型又有哪些?

Java的数据类型:基本数据类型和引用数据类型
基本数据类型:byte(1),short(2),int(4),long(8),float(4),double(8),char(2),boolean(1)
引用数据类型:类,接口,数组

2、Java语言的几大特性是什么?分别怎么理解?(封装、继承、多态的好处)

继承、封装、多态

继承:
继承是从已有类得到继承信息新创类的过程。
提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。

封装:
通常认为分装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
面向对象的本质就是将现实世界描绘成一系列的完全自治、封闭的对象。
在类中编写的方法就是对实现细节的一种封装,编写的一个类就是对数据和数据操作的封装。
可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。

多态:
多态性是指允许不同子类型的对象对同一消息做出不同的响应。
简单地说就是用同样的对象引用调用同样的方法,但做了不同的事情。
多态性分为编译时的多态性和运行时的多态性。
如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:
当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的。
方法重载(overload)实现的是编译时的多态性(也就是前绑定),而方法的重写(override)实现的是运行时的 多态性(也称为后绑定)。
运行时的多态时面向对象最精髓的东西,要实现多态需要做两件事:
1)、方法重写(子类继承父类并重写父类中已有的或抽象的方法)
2)、对象造型(用父类引用引用子类对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)

3、Java的权限修饰符有哪些?都能加在哪些地方?分别代表什么意义?

Java的权限修饰符有4种:
public、protected、default、private
都能加在哪些地方:
类、方法、成员变量
分别代表的意义:
public:公共的,任意位置都能访问
protected:当前类、同一个包下、子类都能访问,其他包下不能访问
default:当前类、同一个包下可访问,子类和其他包下不能访问
private:私有的,只能当前类中访问

4、什么是重写?什么是重载?

重写:
重写发生在子类和父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型;
比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常
重载:
重载发生在一个类中,同名的方法如果有不同的参数列表,则视为重载

5、final关键字能加在哪些地方?分别代表什么?

属性:属性不可变
方法:方法不可覆盖
类:被其修饰的类不可继承

6、static关键字能加在哪些地方?分别代表什么?

成员变量:使其变成静态变量,该类的所以实例都将共享此变量
方法:使其变成静态方法,类加载后,便可以直接调用此方法,而不需要一个该类的实例
代码块:类加载时,会执行这一段代码

7、接口中可以有哪些成员?抽象类呢?接口和抽象类又有什么区别?(注意JDK1.8接口中是可以出现非抽象方法的:default方法、静态方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
接口:
在接口类中,成员变量必须是常量,也就是final修饰的
接口中的方法默认都是 public abstract 都是抽象的
抽象类:
抽象方法(包含)
成员变量(可包含,子类可以直接继承)
成员方法(可包含,子类可以直接继承,提高子类的功能)
常量(可包含,子类可以直接继承)
静态方法(可拥有,子类可以直接继承,抽象类的类名也可以直接调用)
构造方法(可包含,因为抽象类中可能包含成员变量,成员变量需要初始化和二次赋值)
区别:
1、接口中不能定义构造器;抽象类中可以定义构造器
2、接口中方法全部都是抽象方法;抽象类中可以有抽象方法和具体方法
3、接口中的成员都是 public 的;抽象类中的成员可以是 private、默认、protected、public
4、接口中定义的成员变量实际上都是常量;抽象类中可以定义成员变量
5、接口中不能有静态方法;抽象类中可以包含静态方法
6、一个类可以实现多个接口;一个类只能继承一个抽象类

8、Java异常体系是什么? 运行时异常和检查(编译)异常有什么区别?常见的运行时异常有哪些?

异常体系:
Thorwable类是所以异常和错误的父类
两个直接子类为 Error和 Exception ,分别表示错误和异常。
其中异常类 Exception 又分为运行时异常和编译时异常
运行时异常和检查(编译)异常的区别:
编译时异常:
是Java要求必须处理的,如果程序在编译时期未处理,该程序编译时就会发生错误无法编译,try…catch或throw抛出。
运行时异常:
是代码在运行是才会出现的异常,编译时不需要try…catch。
如:除数是0,数组角标越界,其产生频繁,处理麻烦,若显示声明或者捕获将会对程序的可读性和运行效率影响很大。
所以由系统自动检测并将它们交给缺少的异常处理程序。如果有处理要求也可显示捕获。

9、== 和 equals的异同?

1
2
3
4
5
6
7
==:
== 是运算符,如果比较的对象是基本数据类型,则比较的是数值是否相等;
如果比较的是引用数据类型,则比较的是对象的地址值
equals:
equals 是方法,用来比较两个对象的内容是否相等
equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,
则比较的是引用类型的变量所指向的对象的地址值

10、&与&&、|与||的区别?

&:左右两边的布尔值都是true,整个表达式的值才是true
&&:如果左边的表达式的值为false,右边的表达式会被直接短路掉,不会进行运算,整个表达式的值就是false
|:左右两边的布尔值只要有一个为true,整个表达式的值就是true
||:如果左边的表达式的值为true,右边的表达式会被直接短路掉,不会进行运算,整个表达式的值就是true

11、String可以修改本身吗?为什么?

不可以
字符串内部其实就是一个使用final关键字定义的char[]数组,数组长度一档声明则不可改变
字符串一旦声明则不可改变,变的只是引用变量所指向的对象

12、StringBuffer和StringBuilder的区别是什么?

StringBuilder实在单线程环境下使用的,因为它的使用方法都没有被 synchronized 修饰,因此理论上它的效率比 StringBuffer 高

13、valueOf和toString的区别?

1
2
3
toString:变换的值是 null 的情况,则会抛出 NullPointerException 异常
valueOf:会首先对转换的对象进行非空判断,如果为 null 则返回 "null" 字符串,
以至于不抛出 NullPointerException 异常

14、大量字符串用 “+” 号进行拼接效率高吗?为什么?应该用什么替代?为什么?

不高
大量字符串用 “+” 号进行拼接,每次拼接一次都会创建一个 StringBuilder 对象,时间和空间开销太大
大量字符串拼接可以使用 StringBuilder 中的 append() 方法代替
append( )方法其实是创建了一个新的数组,扩大了长度,将需要添加的字符串给复制到这个新的数组中

15、创建一个类的实例都有哪些办法?

1、new 关键字。工厂模式是对这种方式的包装
2、类实现克隆接口,克隆一个实例
3、用该类的加载器,newinstance()方法。反射,反射使用实例:Spring的依赖注入、切面编程中的动态代理
4、sun.misc。Unsafe类,allocateInstance()方法创建一个实例
5、实现序列化接口的类,通过IO流反序列化读取一个类,获得实例

16、Java集合的体系是什么样的?

Java集合的体系分为两大部分:Collection 和 Map
Collection 接口下常见的子类有 List、Set 接口
List 接口下有 ArrayList、LinkedList 等实现类
Set 接口下有 HashSet、LinkedHashSet、TreeSet 等实现类
Map 接口下有 HashMap、TreeMap、Hashtable 等实现类

17、Set和List分别有哪些特点?Set去重的原理?

List 中存储的数据是有顺序,并且允许重复;Set 中存储的数据是无序的,且不允许有重复
Set的去重是通过 hash 和 eq 结合实现的
当两个变量的哈希值不相同时,就认为这两个变量不同
当两个变量哈希值一样时,调用 eq 方法,放返回值为 true 时,去除一个;返回 false 时,不去重

18、ArrayList底层原理是什么?扩容原理?

构造方法:
空参构造:new 一个空参 ArrayList 的时候,系统内部使用了一个 new Object[0]数组
带参构造1:传入一个 int 值,该值作为数组的长度值。如果该值小于0,则抛出一个运行时异常。
如果等于0,则使用一个空数组。如果大于0,则创建一个长度为该值的新数组
带参构造2:如果调用构造方法时传入了一个 Collection 的子类,那么先判断该集合是否为null,为null则抛出空指针异常。
如果不是null则将该集合转为数组a,然后将该数组赋值为成员变量array,将该数组的长度作为成员变量size。
add 方法:
1、首先将成员变量 array 赋值给局部变量 a,将成员变量 size 赋值给局部变量 s
2、判断集合长度 s 是否等于数组的长度(如果集合的长度已经等于数组的长度了,说明数组已满,该重新分配新数组了),
重新分配数组的时候需要计算新分配内存的空间大小,如果当前的长度小于 MIN_CAPACITY_INCREMENT/2 ,
(这个常量值是12,除以2就是6,也就是如果当前集合长度小于6)则分配12个长度,如果集合长度大于6则分配当前长度s的一半
3、将新添加的 object 对象作为数组的 a[s] 个元素
4、修改集合长度 size 为 s+1
5、modCotun++,该变量是父类中声明的,用于记录集合修改的次数,
记录集合修改的次数是为了在用迭代器迭代集合时避免并发修改异常,或者说用于判断是否出现并发修改异常的
6、return true,这个返回值意义不大,因为一直返回true,除非报了一个运行时异常
remove 方法:
1、先将成员变量 array 和 size 赋值给局部变量 a 和 s
2、判断形参 index 是否大于等于集合的长度,如果成立则抛出运行时异常
3、获取数组中角标为 index 的对象 result,该对象作为方法的返回值
4、调用 System 的 arraycopy 方法,将删除的元素后面的所有元素全部往前移一位
5、因为删了一个元素,而且集合整体向前移动了一位,因此要将集合最后一个元素置为 null,否则就会内存泄漏
6、重新给成员变量 array 和 size 赋值
7、记录修改次数
8、返回删除的元素
clear 方法:
如果集合长度不等于0,则将数组所有的值都为 null,然后将成员变量 size 设置为0,最后将修改记录加1

19、LinkedList底层原理是什么?和ArrayList的区别是什么?

LinkedList底层原理:
LinkedList 的底层是通过双向链表实现的。
链表的单元是节点,链表由多个节点构成,每个节点都包含三个部分,
头节点指向上一个节点的尾节点,中间节点指向该节点,尾节点指向下一个节点的头节点
和ArrayList的区别:
1、数据结构实现:ArrayList是动态数组的数据结构实现;LinkedList是双向链表的数据结构实现
2、随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,
因为LinkedList是线性的数据存储方式,使用需要移动指针从前向后一次查找
3、增加和删除效率:在非首尾的增删操作,LinkedList要比ArarryList的效率高,因为ArrayList增删操作要影响数组内其他数据的角标
综合来说,在需要频繁读取集中的元素时,更推荐使用ArrayList,而在增删操作比较多时,更加推荐使用LinkedList

20、HashMap的底层原理是什么?扩容原理?

HashMap的底层采用了一个数组,该数组的默认大小为16且每一个元素都是一个链表的头节点,
每当添加一个元素,就先计算元素Key的hash值,以此确定存放在数组的位置,
如果出现了同一hash值的元素时,这时新元素就会挂在老元素的下面,形成链表,
当链表的长度太长,大于8时,链表就会转换为红黑树,就能提高查找的效率
扩容阈值:
当前容量 * 加载因子 = 阈值,默认的加载因子为0.75
扩容的机制:
当数组中的元素达到阈值时,第一次扩容即16 * 0.75 = 12时,就会触发扩容机制,扩大到元素组的2倍
加载因子:
- 加载因子的大小决定了HashMap的数据密度。
- 加载因子越大HashMap的数据密度也大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入的次数增多,性能下降。
- 加载因子越小,就越容易触发扩容,数据密度也就越小,发生碰撞的几率越小,数组中的链表就越短,查询和插入时比较的次数也越小,
性能会更高,但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预留大一点的空间。
- 所以会将加载因子设置为0.7-0.75,此时平均检索长度接近于常数。

21、concurrentHashMap原理是什么?

底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

22、JDK8对于HashMap做了哪些优化?

JDK8之前采用的是数组加链表,JDK8之后采用的是数据加链表加红黑树

23、什么是socket?什么是IO/NIO/BIO/AIO?区别是什么?

socket:
socket是一个接口,在用户进程与TCP/IP协议之间充当中间人,完成TCP/IP协议的书写
IO:
在Java中,用流的方式完成IO。
所有IO都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。
流I/O用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
NIO:
NIO不需要为每个连接开启一个线程,而是统一由Selector管理,当连接没有IO操作时,不需要阻塞线程等待数据,
只有当Selector检测到呢个Channel有有效的IO请求时,再为其开启线程操作,节省线程的开销,操作结束后返回结果,故为同步非阻塞。
BIO:
在BIO通信模式下,服务端每收到一个链接(socket),就会创建专门的线程(serverssocket)响应该连接。
这个连接会一直存在等待读取发来的数据,这个过程会阻塞搜在线程,不能做别的事,直到操作结束后返回结果值,因此这是同步阻塞。
BIO模式下,服务端连接多个客户端时,会开启多个线程响应连接。
AIO:
没有线程阻塞,与AIO不同的是NIO会主动轮询操作系统数据是否准备完毕,而AIO则是等待系统主动通知,再去读取数据

24、什么是反射?可以用来干嘛?列举一下反射应用场景?什么是暴力反射?

反射:
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁
Java 中的反射首先是能够获取到 Java 中要反射类的字节码,获取字节码有三种方法:
1、Class.forName(className) 2、类名.class 3、this.getClass()
然后将字节码中的方法,变量,构造函数等映射成相应的Method,Filed,Constructor等类,这些类中提供了丰富的方法供使用
反射的作用:
1、在运行时判定任意一个对象的所属类
2、在运行时构造任意一个类的对象
3、在运行时判定任意一个类所有具有的成员变量和方法
4、在运行时调用任意一个对象的方法
5、生成动态代理
应用场景:
反射最重要的用途就是开发各种通用框架。
很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,
它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
暴力反射:
反射里的Constructor,Field,Method三个类都有一个 getDeclaredXxx 方法,
可以不受权限控制的获取类的构造函数,字段,方法,如果想要私有构造函数创建对象,字段赋值,方法调用的话,
会自动的访问类的isAccessable,默认的是false,
所以,想要访问类中的私有成员的时候,就要调用setAccessable()方法,将其改为true,这样,就可以对类中的私有成员进行操作了

25、算法了解过吗?冒泡排序、选择排序、快排原理?

冒泡排序:
1、比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3、针对所有的元素重复以上的步骤,除了最后一个;
4、重复步骤1~3,直到排序完成。
选择排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,再放到已排序序列的末尾。
以此类推,直到所有元素均排序完毕
快速排序:
1、从数列中挑出一个元素,称为 “基准”(pivot);
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

26、JDK1.8的新特性有哪些?(lamda表达式、stream流、函数式接口、接口中默认方法、方法引用等等)

Lambda表达式,函数式接口,方法引用和构造器调用,Stream API,接口中的默认方法和静态方法,新时间日期API

27、IO流体系

1、按流向分:
输入流:程序可以从内存中读取数据的流
输出流:程序可以向磁盘中写入数据的流
2、按数据传输单位分:
字节流:以字节为单位传输数据的流
字符流:以字符为单位传输数据的流

28、如何实现分布式主键自增?

Redis和IdWork工具类

二、JVM

1、JDK和JRE的区别是什么?

JDK:开发工具
JRE:运行时环境

2、JVM内存模型是什么样的?

1、线程隔离私有数据区
1、程序计数器
2、栈
1、线程栈
2、本地方法栈
2、线程数据共享区
1、堆
1、新生代区
2、老年代区
2、方法区

3、JVM双亲委派加载机制,为什么JVM这么做?有违反双亲委派的例子吗?

双亲委派加载机制:
加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,
如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类
为什么JVM这么做:
1、可以防止核心API库被随意篡改
2、避免类的重复加载:当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次,保证被加载类的唯一性
违反双亲委派的例子:
服务供应商接口 SPI,常见的 SPI 有 JDBC、JNDI、JAXP 等

4、类的加载流程是什么样的,每个阶段解释一下

1、加载:
通过一个类的全限定名获取该类的二进制流;
将该二进制流的静态存储结构转化为方法区运行时数据结构;
在内存中生成该类的Class对象,作为该类的数据访问入口;
2、链接:
验证:验证的目的是为了确保Class文件的字节流中的信息不会危害到虚拟机;
准备:准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。
准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
解析:主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
3、初始化:
初始化是类加载的最后一步,真正开始执行类中定义的Java程序代码

5、JVM的GC的主要区域以及各自的GC机制是什么样的?

GC的主要区域:
堆内存
各自的GC机制:
新生代区:Minor GC
1、当Eden区满了时,触发一次MG,即:
还存活的对象从Eden或者Survivor From/Survivor To区中用copy复制算法移至Survivor To/Survivor From区中
Eden中剩下的待回收的对象进行回收,且存活对象的MG次数+1,当对象上的MG次数大于15次时,该对象将会移至老年代区
2、JVM每次只会使用Eden和其中的一块Suivivor区域来为对象服务,所以无论什么时候,总是有一块Survivor区域是空闲的
因此,新生代实际可用的内存空间为90%的新生代空间,所以这种回收机制是比较浪费内存的,优点是简单高效
老年代区:Full GC
当老年代没有内存空间容纳新进来的对象时,触发一次FG,即:
1、首先stop the world:暂停GC回收线程外的所有线程;
2、然后使用标记-清除/整理算法,标记出

6、JVM的GC算法都有哪些?

1、确定哪些是垃圾的算法:
1、引用计数法:
每被引用一次,引用次数+1, 弊端:无法解决对象相互循环引用而无法被回收问题
2、GC Root可达性算法:
从根节点出发,给所有可达对象做标记(解决对象相互循环引用而无法被回收问题)
Java中可作为GC Root的对象有:
1、虚拟机栈中引用的对象(本地变量表)
2、方法区中静态属性引用的对象
3、方法区中常量引用的对象
4、本地方法栈中引用的对象(Native对象)
2、最终回收垃圾的算法
1、标记-清除:效率较高,但最后得到的内存空间不连续
2、标记-整理:最后得到的内存空间是连续的,但整理压缩耗费性能,效率较低
3、复制算法:将内存空间分为两块,每次只使用其中的一块,回收时,将存活的对象复制到另一块中,简单高效,但是浪费内存空间
针对以上的回收算法的利弊,引出分代算法,新生代区使用复制算法,老年代区使用标记-清除/标记-整理算法

三、Spring & SpringBoot

1、说下对SpringIOC的理解,怎么理解控制反转?IOC容器中Bean的生命周期?

IOC:
是spring的核心,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在对象内部直接控制
控制反转:
所有的类都会在spring容器中登记,告诉spring你是什么,你需要什么,然后spring会在系统运行到适当的时候,把你要的东西主动给你,
同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转
生命周期:
1、通过构造器或工厂方法创建Bean的实例
2、为Bean的属性赋值或对其他Bean的引用
3、调用Bean的初始化方法
4、使用Bean
5、当容器关闭时,调用Bean的销毁方法

2、Spring如何解决IOC中的循环依赖问题?

三级缓存。
比如说:
实例化A的时候,先将A创建(早期对象)放入一个池子中。
这个时候虽然属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在
populateBean 方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B了,又走一遍bean的创建过程(创建B)。
同样也会把B的早期对象放入缓存中。
当B又走到 populateBean 方法的时候,发现依赖了A,
又去创建A,但是这个时候去创建A,发现我们在缓存能找到A(早期对象)。
就可以把B的A属性赋值了,这个时候B就初始化完成了。
现在回到A调用的populateBean方法中。
返回的就是B对象了,对A的B属性进行赋值就可以了。

3、说下对SpringAOP的理解、有哪些通知?使用场景有哪些?(底层原理:两种动态代理)

AOP 面向切面编程:
将与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于可操作性和可维护性
有哪些通知:
1、前置通知:在目标方法执行之前执行执行的通知
2、环绕通知:在目标方法执行之前和之后都可以执行额外代码的通知
3、后置通知:在目标方法执行之后执行的通知
4、异常通知:在目标方法抛出异常时执行的通知
5、最终通知:最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成
使用场景有哪些:
事务、日志、缓存

4、说下SpringMvc的流程(从访问一个URL到得到页面结果的具体流程:DispatcherServlet的职责流程)

1、用户发送请求至前端控制器DispatcherServlet
2、前端控制器DispatcherServlet收到请求调用HandlerMapping处理器映射器
3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet
4、DispatcherServlet调用HandlerAdapter处理器适配器
5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
6、Controller执行完成返回ModelAndView
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、ViewReslover解析后返回具体View
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
11、DispatcherServlet响应用户

5、对Spring声明式事务的理解?Spring的事务隔离级别?Spring事务传播行为?

理解:
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的
Spring的事务隔离级别:
默认、读未提交、读已提交、可重复读、串行化
Spring事务传播行为:
1、required(默认属性)
如果存在一个事务,则支持当前事务,如果没有事务,则开启一个新的事务
2、Mandatory
支持当前事务,如果当前没有事务,就抛出异常
3、Never
以非事务方式执行,如果当前存在事务,则抛出异常
4、Not_supports
以非事务方式执行操作,如果当前存在事务,把当前事务挂起
5、requires_new
新建事务,如果当前存在事务,则把当前事务挂起
6、Supports
支持当前事务,如果当前没有事务,就以非事务方式执行
7、Nested
支持当前事务,新增Savepoint点,与当前事务同步提交或者回滚

6、什么情况下会让spring事务失效

1、发生自调用,类里面使用this调用本类中的方法,此时这个this不是代理对象,所以事务失效
2、方法不是public的
3、数据库不支持事务
4、没有被spring管理
5、异常被try、catch,事务不会回滚

7、Spring有哪些核心注解?分别的作用?

1
2
3
4
5
6
7
8
@Controller:Web控制器模式注解
@Service:服务模式注解
@Configuration:配置类模式注解
@Component:通用组件模式注解
@Autowired: Bean依赖注入,支持多种依赖查找方式
@Bean:替换XML元素<bean/>
@Import:限定@Autowired依赖注入范围(导入对应的 @Configuration 标识类)
@ComponentScan:扫描制定package下标注Spring模式注解的类

8、Spring和SpringBoot的关系?

Spring Boot不是一门新技术。从本质上来说,Spring Boot就是Spring,它做了一些对Spring Bean的默认配置。
它消除了设置 Spring 应用程序所需的 XML配置,为更快,更高效的开发生态系统铺平了道路。

9、SpringBoot的自动装配原理是什么?

@EnableAutoConfiguration 这个注解通过 @springbootapplication 这个注解被间接的标记在了springboot的启动类上,
在springapplication.run(…)的内部就会执行selectimports()方法,
找到所有javaconfig自动配置类的全限定类名对应的class,然后将所有的自动配置类加载到spring容器中

10、SpringBoot的核心注解是哪个?详细说下

核心注解是@SpringBootApplication,它主要由 @SpringBootConfiguration,@EnableAutoConfiguration、@ComponentScan 这三个构成
@SpringBootConfiguration:
里面就只有一个 @Configuration 主要注解,也就是把该类变成一个配置类所以 @SpringBootConfiguration 就相当于 @Configuration
@EnableAutoConfiguration:
是由 @AutoConfigurationPackage 和 @Import(EnableAutoConfigurationImportSelector.class) 这两个组成的
@AutoConfigurationPackage :
是自动配置包,包括了一个@Import注解,给容器导入了自动配置包的注册器
AutoConfigurationPackages.Registrar.class:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import(AutoConfigurationImportSelector.class):
导入自动配置导入选择器组件
AutoConfigurationImportSelector.class:自动配置导入选择器,主要是从类路径下的META-INF/spring.factories中获取资源
@ComponentScan:扫描包,该注解默认会扫描该类所在的包下所有的配置类

11、SpringBoot项目的启动加载流程大概说下

第一部分:
SpringApplication初始化模块,配置一些基本的环境变量、资源、监听器、构造器
第二部分:
实现了应用具体的启动方案,包括流程的监听模块,加载配置环境模块以及上下文环境模块
第三部分:
自动化配置模块,这个模块是实现SpringBoot的自动装配

12、SpringBoot项目读取配置文件的方式有几种?

1
2
@Value
@ConfigurationProperties

13、如何自定义SpringBoot starter?

1、引入 SpringBoot 自动化配置依赖
2、创建配置属性类
3、创建 Starter 包的服务类
4、创建自动配置类
5、配置 spring.factories

四、Mysql & Mybatis?

1、什么是索引?

本质是帮助MySQL高效获取数据的数据结构

2、Mysql的数据结构是什么(mysql索引的数据结构)?为什么用这种结构?(如何提高磁盘IO效率)

Mysql的数据结构:
MySql中主要应用的索引数据结构为B+Tree
为什么用这种结构:
由于数据存储于物理磁盘,所以要尽量减少从磁盘IO数据的次数
IO次数取决于B+Tree的高度
B+Tree把真实的数据放到叶子节点,数据项占的空间越小,数据项的数量越多,树的高度越低

3、Mysql的数据IO查找流程是什么样的?

1、对于聚集索引(主键索引)来说,也就是通过主键查询,一次查询就能查询到具体的行数据信息;
2、对于非聚集索引来说(唯一索引、普通索引、全文索引),
如果需要查询的数据列在索引中,如A+B联合索引,根据A去查询B,则通过一次查询就能直接拿到索引上的数据,也就是覆盖索引现象;
如果需要查询的数据不在索引中,则需要先去普通索引树中进行第一次查找得到行数据的主键值,
然后通过主键值去主键索引树中第二次搜索得到真实数据,这种需要二次查询的现象叫做回表查询

4、B+tree和Btree由什么组成?他们的异同?

异同:
1、B+tree是Btree的变体
2、在Btree的基础上增加了叶子节点的顺序访问指针,B+Tree提高了顺序访问的性能
3、Btree每个节点的指针上限为2d+1,B+Tree每个节点的指针上限为2d
4、B+Tree非叶子节点只存储索引值,叶子节点存储真实数据,Btree所有节点上都存储数据

5、Mysql两种存储引擎(InnoDB和Mysiam)的区别?这两种引擎B+tree的叶子结点和非叶子结点分别存储的什么?

区别:
1、InnoDB支持事务和外键,MySIAM不支持
2、InnoDB支持行锁,MySIAM只支持表锁
3、InnoDB的真实数据和索引都存储在同一个文件中,MMySIAM存储在两个文件中
4、InnoDB不支持全文索引,MySIAM支持
InnoDB:
叶节点data域保存了完整的数据记录
MySIAM:
叶节点的data域存放的是数据记录的地址

6、Mysql索引有哪些类型?什么场景使用哪种索引?

普通索引、唯一索引、主键索引、联合索引、全文索引

7、如何进行Mysql优化?(sql优化层面和服务器优化层面)

SQL层面:
1、尽量避免使用select *
2、规范sql语句大小写,sql时有缓存的,避免每次都需要解析
3、使用exsits代替in
4、mysql sql解析执行过程是从右到左,from后面能过滤掉更多数据的基础表放在后面,where后面能过滤掉更多数据的查询条件放在后面
5、查询条件中用相同类型去查询,比如避免数值列用字符串查询条件
6、合理使用索引
7、explain命令进行sql慢查询排查
服务器优化层面:
1、读写分离:主节点写,从节点读
2、分库:根据业务或者其他维度把数据存放到不同数据库
3、分表:
1、水平分表:字段都一样,分多张表存放不同时间范围或不同维度的数据,如实时数据表、历史数据表
2、垂直分表:将不同字段放在多张表,使用外键关联
4、常用分库分表中间件:阿里的Cobar及开源社区基于Cobar维护的Mycat

8、Sql调优你会从何入手(措施)?

explain命令进行sql慢查询排查:
1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行
2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
SIMPLE:表示简单杳询,其中不包括连接查询和子查询;
PRIMARY:表示主查询,或者最外层的查询语句;
SUBQUERY:子查询中的第一个SELECT语句;
DERIVED:导出表的SELECT (FROM语句的子查询);
UNION:表示连接查询的第2个或后面的查询语句;
UNION RESULT:连接查询的结果;
3、table:正在访问哪个表
4、partitions:匹配的分区
5、type:访问的类型,效率从快到慢:
NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL
6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
7、key:实际使用到的索引,如果为NULL,则没有使用索引
8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好
11、filtered:查询的表行占表的百分比
12、Extra:包含不适合在其它列中显示但十分重要的额外信息

9、Mysql中如何合理使用索引?有哪些会使索引失效的情况?

何合理使用索引
1、为合适的列添加索引(主键、唯一索引、组合索引)
2、尽量建立联合索引,也省空间成本
3、尽量使用覆盖索引
4、避免会使索引失效的操作
会使索引失效的情况:
1、索引列有null值不走索引
2、使用is null或is not null不走索引
3、各种负向查询not ,not in, not like ,<> ,!= ,!> ,!< 不会使用索引
4、like将%放左边不走索引
5、查询条件的数据类型做了隐式转换
6、使用in或union代替or,or两侧有非索引列就不会走索引
7、尽量保持索引列干净,不在索引列上使用函数转换、运算
8、联合索引要遵循最左匹配原则
9、使用比较运算或between会使联合索引从使用比较运算的下一个索引处断开

10、Mysql如何排查慢查询(哪个关键字)?分别会列出来哪些信息项?

explain命令进行sql慢查询排查:
1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行
2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
SIMPLE:表示简单杳询,其中不包括连接查询和子查询;
PRIMARY:表示主查询,或者最外层的查询语句;
SUBQUERY:子查询中的第一个SELECT语句;
DERIVED:导出表的SELECT (FROM语句的子查询);
UNION:表示连接查询的第2个或后面的查询语句;
UNION RESULT:连接查询的结果;
3、table:正在访问哪个表
4、partitions:匹配的分区
5、type:访问的类型,效率从快到慢:
NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL
6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
7、key:实际使用到的索引,如果为NULL,则没有使用索引
8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好
11、filtered:查询的表行占表的百分比
12、Extra:包含不适合在其它列中显示但十分重要的额外信息

11、事务的特性是什么?Mysql事务隔离级别有哪几种?分别会产生什么问题?Mysql默认隔离级别是什么?Oracle呢?

事务的特性:
原子性、一致性、隔离性、永久性
Mysql事务隔离级别:
读未提交、不可重复读、可重复读、串行化
分别会产生什么问题:
读未提交:一个事物读到了另一个事务尚未提交的数据,不符合事务的隔离性
不可重复读:同一个事务中针对同一行记录两次读出来的结果不一样,原因就是第二次读到了其他事务修改提交的数据
可重复读:同一个事务中针对同一范围内的数据两次读出来的结果不一样,原因就是第二次读到了其他事务新增提交的数据
mysql默认隔离级别:
可重复读,但是一般会设置为不可重复读,因为在实际业务中常常是,一个事务中需要读到别的事务提交修改的数据
oraclel默认隔离级别:
不可重复读

12、Mysql的行锁、表锁,悲观锁、乐观锁?

1
2
3
4
5
6
7
8
9
行锁:
访问数据库的时候,锁定整个行数据,防止并发错误。
表锁:
访问数据库的时候,锁定整个表数据,防止并发错误。
乐观锁:
乐观的认为本次事务操作数据不会有别的事务干扰,操作数据前不进行加锁,只是预先记录版本号,真正修改数据时再进行比对,
如果版本号没变则修改数据,版本号变了则表面别的事务在本次事务过程中修改了数据,本次事务不修改数据。
悲观锁:
悲观的认为本次事务一定会有别的事务干扰,操作数据前必须先加锁

13、Mysql的vachar和char的区别?

vachar:
长度为可变的,实际使用多少空间就占多少空间。
char:
列长度固定,为创建表时声明的长度,长度值范围是1到255,当char值未填满指定长度时,其他空间会用空格进行填充,检索CHAR值时需删除尾随空格。

14、什么是内连接(inner join)、外连接(left join)?

内连接:
只显示左右两表中匹配条件的行,在结果表中删除与其他被连接表中没有匹配行的所有行
左外连接:
把左边表的数据全部取出来,而右边表的数据有相等的,显示出来,如果没有,显示NULL

15、平时Mysql的sql练习要练到位!!

牛客网,练习SQL语句

16、Mybatis底层的原理?一级缓存和二级缓存是什么?

Mybatis底层的原理:
动态代理
一级缓存:
Mybatis中sqlSession对象的缓存,当执行查询以后,查询的结果会同时存入到SqlSession提供的一块区域中,该区域的结构是一个Map,
当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,的话直接拿出来用,
当SqlSession对象消失时,mybatis的一级缓存也就消失了,
同时一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit(),close等方法时,就会清空一级缓存。
二级缓存:
Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存,但是其中缓存的是数据而不是对象

17、mybatis #{}和${}的区别?

#{}方式能够很大程度防止sql注入
${}的方式无法防止Sql注入

18、Mysql存储过程、存储函数、触发器分别用来干嘛的?创建语法是什么?

存储过程:
存储过程把经常使用的SQL语句或业务逻辑封装起来,预编译保存在数据库中,当需要时从数据库中直接调用,省去了编译的过程。
提高了运行速度同时降低网络数据传输量
存储函数:
参数可以有多个,也可以没有参数,必须有且只有一个返回值
触发器:
触发器的执行不是由程序调用,也不是由手工启动,而是由事件来触发、激活从而实现执行

19、union和unionAll有什么区别?

Union:
对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序
在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果
Union All:
对两个结果集进行并集操作,包括重复行,不进行排序
如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了

20、创建表、删除表、更新表字段语句?

创建表:
create table 表名
删除表:
drop table 表名
更新表字段:
修改字段名称:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 数据类型;
修改字段数据类型:ALTER TABLE 表名 MODIFY 字段名 新数据类型;

21、mysql左外连接语句的写法?

Select A.name,B.name from A Left Join B on A.id=B.id;

22、听过InnoDB的Mvcc技术吗?说下是什么?

Mvcc:
多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,
取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能

23、Java实现动态代理有哪些方式?区别是什么?

Java实现动态代理有哪些方式:
jdk动态代理、cglib动态代理
区别:
jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。
还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。
如果没有上述前提,jdk动态代理不能应用。
由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

五、多线程

1、创建线程的方式有哪些?相比继承Thread类,实现Runable接口的好处是什么?

创建线程的方式:
1、实现Runnable接口
2、继承Thread类
好处:
1、适合多个相同程序代码的线程去处理同一个资源
2、可以避免由于Java的单继承性带来的局限性
3、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的

2、线程的状态有哪些?

1
2
3
4
5
1、新建状态
2、就绪状态
3、运行状态
4、阻塞状态
5、死亡状态

3、run()和start()方法有哪些区别?

run():
是来完成实际的业务逻辑,当run()方法结束后,此线程就会终止
start():
是用来开启一个线程的,使线程处于就绪状态,即可以被JVM来调度执行

4、实现线程间通讯的方法有哪些?

1、wait():
让线程处于冻结状态,被wait的线程会被存储到线程池中
2、notify():
唤醒线程池中的一个线程
3、notifyAll():
唤醒线程池中的所有线程

5、wait、notify、notifyAll分别的作用是什么?可以用在同步代码块之外吗?为什么?

分别的作用:
wait():
让线程处于冻结状态,被wait的线程会被存储到线程池中
notify():
唤醒线程池中的一个线程
notifyAll():
唤醒线程池中的所有线程
可以用在同步代码块之外吗:
不行
为什么:
因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程

6、Sleep和Wait的区别?

1
2
3
4
5
6
7
8
9
10
sleep():
1、属于Thread类,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态
2、sleep方法没有释放锁
3、sleep必须捕获异常
4、sleep可以在任何地方使用
wait:
1、属于Object,一旦一个对象调用了wait方法,必须采用notify()和notifyAll()方法唤醒该进程
2、wait方法释放了锁
3、wait不需要捕获异常
4、wait、notify、notifyAll只能在同步控制方法或者同步控制块中使用

7、什么是线程安全问题?什么情况下会产生?如何解决?

线程安全问题:
多个线程同时访问共享数据时可能会出现问题,称为线程安全问题
什么情况下会产生:
当多线程访问共享数据时,由于CPU的切换,导致一个线程只执行了关键代码的一部分,还没执行完此时另一个线程参与进来,导致共享数据发生异常
如何解决:
通过线程同步机制synchronized + 锁来解决线程安全问题

8、什么是死锁?如何防止产生死锁?

什么是死锁:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
如何防止产生死锁:
1、设置超时时间
2、使用JUC包提供的并发类,而不是自己设计锁
3、尽量降低锁的粒度
4、尽量使用同步代码块,而不是同步方法
5、给线程其有意义的名字
6、避免的嵌套
7、分配锁资源之前先看能不能回收回来资源
8、专锁专用

9、Synchronized关键字的底层原理是什么?

1
2
3
4
5
6
7
当一个线程第一次运行到 synchronized 代码,获取到了 myObject 对象的 monitor 的锁,然后计数器就会加1
然后第二次运行到 synchronized 代码,会再次获取 myObject 对象的 monitor 的锁,这个就是重入加锁了,然后计数器会再次加1,变成2
这个时候,其他的线程运行到第一次 synchronized 代码,会发现 myObject 对象的 monitor 锁的计数器是大于0的,
就意味着被别人给加锁了,然后此时线程就会进入 block 阻塞状态,什么都干不了,就是等待获取锁
如果第一个线程出了 synchronized 修饰范围的话,就会有一个 moninorexit 的指令,此时,在底层获取锁的线程就会对那个对象
的 monitor 的计数器减1,如果有多次重入加锁,就会对应多次减1,直到最后,计数器是0
然后,后面 block 阻塞的线程,会再次尝试获取锁,但是只有一个线程可以获取锁

10、Synchronized可以用在哪些地方?分别的锁对象是什么?

1
2
3
1、同步代码块:锁对象为括号中的对象
2、同步方法:锁对象为当前对象this
3、静态同步方法:锁对象为class字节码文件对象

11、Synchronized和JUC下Lock锁的异同?

1、Lock是JUC包下提供的封装好的锁,是类的概念,而synchronized是一个虚拟机层面的关键字。
2、Lock显示的加锁和解锁,且解锁要在finally代码块中,否则可能会死锁,而synchronized为隐式的上锁和解锁。
3、Lock锁提供了尝试获取锁和设置获取锁时间的机制,可返回取锁状态,当获取不到锁的时候也可以选择放弃取锁,
而synchronized无法判断返回取锁状态,取锁不成功只能阻塞,没有Lock灵活。
4、Lock锁阻塞可被打断,而synchronized阻塞不可被打断。
5、Lock可实现可重入、可公平锁,而synchronized是可重入、非公平锁。
6、Lock可以很灵活的根据线程角色类型去创建Condition监视器对象,调用await()、signal()、signalAll()进行线程通讯调度,
而synchronized使用Object对象本身作为监视器对象去调用wait() 、notify()、notifyAll()进行线程通讯调度。
7、Lock提供了更丰富的锁分类,如读锁、写锁,可以更细粒度的关注线程安全问题。

12、Synchronized是公平锁还是非公平锁?获取不到锁时会阻塞吗?

非公平,会阻塞

13、Synchronized在JDK1.6做了什么优化?

自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁

13、同步代码块中执行完wait/notify/notifyAll后会立马释放锁吗?

不会,必须等其所在的同步代码块执行完才会释放锁

14、Lock锁有哪些实现?分别的特点是什么?

1、ReentrantLock
2、ReentrantReadWriteLock类中的静态内部类ReadLock(读-写锁)
3、ReentrantReadWriteLock类中的静态内部类WriteLock(读-写锁)

15、JUC下Lock的监视器对象是哪个类?与Synchronized的监视器有什么异同?

1
2
3
4
5
JUC下Lock的监视器对象:
Condition
异同:
Lock可以很灵活的根据线程角色类型去创建Condition监视器对象,调用await()、signal()、signalAll()进行线程通讯调度,
而synchronized使用Object对象本身作为监视器对象去调用wait() 、notify()、notifyAll()进行线程通讯调度。

16、什么是线程可重入?Synchronized具备吗?Lock呢?

重入锁就是一个线程能否获取一个已经由它自己持有的锁。如果可以,就是可重入锁,否则为不可重入锁。它的作用就是能够避免重复获取的时候出现死锁
synchronized 是可重入锁
Lock 是可重入锁

17、什么是AQS?

是一个用于构建锁和同步容器的队列同步器,它是整个JUC包下Lock体系的核心,
如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore都是基于它来实现的,
它解决了在实现同步容器时设计的大量细节问题,
它的核心构成部分为:使用一个 先进先出的FIFO的队列存储排队等待锁的线程,使用一个用volatile修饰的int类型的state同步状态来记录
当前是否有线程持有锁,0表示没有线程获得锁,1表示有,上锁state就加1,释放锁就对应减1,有重入锁现象,这个值就大于1,然后需要逐级去释放。

18、什么是CAS?什么是CAS的ABA问题?如何解决?

CAS:
CAS其实就是乐观锁的一种实现方式
ABA问题:
1、线程1读取出指定内存地址的数据A,加载到寄存器,此时读取出来的原值不仅将作为要被计算的值A,还会作为比较值A。
2、此时线程1的cpu被线程2抢占了,线程2也从同样的内存地址中读取了同样的数据A,线程2还比线程1先执行完,线程2产生了新数据B,
并且遵守了CAS原理把新数据B存入该内存地址。 (这个时候内存的值由A被该为B)
3、还没完,线程2执行完之后,线程1又没抢过其它线程,此时cpu被线程3抢占,之后步骤和第 2 步一样,线程3从同样的内存地址中读取了数据B,
线程3还比线程1先执行完,线程3产生了新数据A(没错,与一开始的A相等,但值相等并不意味着此A就是彼A,已经被替换了),
并且遵守了CAS原理把新数据A存入该内存地址。(这个时候内存的值由B又变为A)
4、这个时候线程1执行完了,要遵守CAS原理存入数据,然后比较值A是原来的A(简称原A),而执行内存地址中的A是被替换过的了,
但原A的值与内存中的A值是相等的,根据CAS,线程1会把新的执行结果存入该内存地址
在实际业务中,两个数的值相等,但这两个数并不是同一个数
解决方案:
加个版本号,多一步比较版本号即可解决

19、你了解JUC下的哪些工具类,分别有什么作用?(CountdownLatch、Cyclicbarrier、Simephore)

CountdownLatch:
利用它可以实现类似计数器的功能
Cyclicbarrier:
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行
Simephore:
信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可

20、说下volatile关键字,有什么作用?原理是什么?

是一个变量类型修饰符,被voltile修饰的变量具有以下特性:
可见性:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
实现(缓存共享协议):
对于用volatile形容的变量,线程写入本地内存中的同时会将数据立即刷新到主内存中。
他线程读取该变量时,发现被volatile修饰,会将本地变量值置为无效,然后从主内存中读取。
有序性:
禁止进行指令重排序。为提高执行效率,在不影响最终执行结果的前提下,代码在编译成字节码的时候有可能进行指令重新
排序,这在单线程情况下是没有问题的,但是在多线程的情况下会出现问题。volatile修饰的变量则可以避免这个问题。
不保证原子性:
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。关于volatile 原子性可以理解为把对volatile
变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。

21、说下ThreadLocal,有什么作用?有哪些主要方法,实现原理是什么?为什么会有内存泄漏问题?如何解决?

ThreadLocal:
ThreadLocal是除了加锁这种同步方式之外的另一种可以规避出现多线程安全问题的思路。
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,
在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题
主要方法:
get()、set()、remove()
实现原理:
每个线程都有属于自己的一个ThreadLocalMap,可通过Thread获得,这个map存储着以threadLock对象为key、以设置的值为value的键值对。
调用get或者set还有remove方法都是操作这个map
内存泄漏:
对于线程池里面不会销毁的线程, 里面总会存在着<ThreadLocal, LocalVariable>的强引用,
因为 final static 修饰的 ThreadLocal 并不会释放,
而 ThreadLocalMap 对于 Key 虽然是弱引用, 但是强引用不会释放, 弱引用当然也会一直有值,
同时创建的 LocalVariable 对象也不会释放, 就造成了内存泄露;
解决方案:
为了避免出现内存泄露的情况, ThreadLocal 提供了一个清除线程中对象的方法, 即 remove,
其实内部实现就是调用 ThreadLocalMap 的 remove 方法:

22、说下线程池的几大核心参数?分别有什么作用?有几种默认的线程池?他们的7个核心参数为什么要那么设置?

线程池的七大核心参数:
1、核心线程数:
没达到核心线程数时,会创建新的线程。当达到核心线程数时,任务会进去队列
2、最大核心线程数:
当达到核心线程数时,会去创建额外的线程来执行任务,最多不超过最大线程数
3、存活时间:
当任务处理完成,额外的线程存活一段时间后,会自行销毁
4、存活时间单位:
空闲等待时间的单位
5、阻塞队列:
利用什么队列来存放任务,有界队列、无界队列等
6、线程池工厂:
线程创建工厂
7、拒绝策略:
分为四大策略:
1、AbortPolicy(默认策略):直接抛出异常,阻止系统正常运行
2、CallerRunsPolicy 策略:等线程池处理不了的任务,谁提交的任务,就给谁拿回去,让其自己执行
3、DiscardPolicy 策略:直接抛弃策略,异常也不会抛,什么都不做
4、DiscardOldestPolicy 策略:丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务
有几种默认的线程池:
1、newCachedThreadPool:
可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
2、newFixedThreadPool:
指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中
3、newSingleThreadExecutor:
单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
4、newScheduleThreadPool:
定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行

23、单例模式写法有哪几种?(懒汉和饿汉式)那么懒汉式中保证线程安全的写法是什么?为什么要用双重检查模式?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
单例模式写法:
1、懒汉式
直到使用前才会创建实例
2、饿汉式
类加载的时候就创建实例
懒汉式中保证线程安全:
在 getInstance() 方法添加 synchronized 关键字,可以解决线程安全问题
为什么要用双重检查模式:
第一次校验:
由于单例模式只需要创建一次实例,如果后面再次调用getInstance方法时,则直接返回之前创建的实例,
因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。
如果不加第一次校验的话,那跟上面的懒汉模式没什么区别,每次都要去竞争锁。
     第二次校验:
如果没有第二次校验,假设线程t1执行了第一次校验后,判断为null,这时t2也获取了CPU执行权,也执行了第一次校验,判断也为null
接下来t2获得锁,创建实例。
这时t1又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。
结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建。

六、Redis & 多级缓存

1、Redis是一个什么样的数据库?读写速度怎么样?

非关系型型数据库
Redis将数据存储在内存上,避免了频繁的IO操作

2、Redis有哪些数据类型,分别的特点?在你们项目中常见的应用场景有哪些?请列举


数据类型:
1、string(字符串):
一个key对应一个value
2、hash(哈希):
是一个键值对集合
3、list(列表):
简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)
4、set(集合):
string类型的无序集合,且不允许重复的成员
5、zset(有序集合):
string类型元素的集合,且不允许重复的成员
不同的是每个元素都会关联一个double类型的分数
redis正是通过分数来为集合中的成员进行从小到大的排序
zset的成员是唯一的,但分数(score)却可以重复
项目有哪些应用场景:
1、计数器:
对 string 进行自增自减运算,从而实现计数器功能
redis 内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。如每日登录次数计数
2、热点数据缓存:
将热点数据放到内存中。如首页排行榜数据,具有很大访问频次,使用zset可以实现基于score分数排序
3、会话缓存:
用redis统一存储多台应用服务器的会话信息
当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,
从而更容易实现高可用性以及可伸缩性
4、取数据交集、并集:
基于 redis set 的特性可以对共同好友进行很方便的查询
5、分布式事务锁的使用:
基于 set lock requestId nx ex time 模式可以很方便编写分布式事务锁

3、Redis的持久化机制是什么样的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
1)、什么是RDB?RDB的持久化机制(Save、bgSave)?
RDB:
把内存中的所有数据都记录到磁盘中。
当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
快照文件称为RDB文件,默认是保存在当前运行目录。
save:
save命令会导致主进程执行RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。
bgSave:
bgSave命令执行后会开启独立进程完成RDB,主进程可以持续处理用户请求,不受影响。

2)、触发RDB的时间点是什么?
save:关机时会save一次,会阻塞主线程,影响效率。
bgsave:与配置有关:save m n,当m秒内有n个key发生修改会触发,后台会fork出一个子线程完成快照

3)、RDB的优缺点分别有哪些?
优点:
由于快照是二进制的,所以加载速度很快
缺点:
由于快照的时间间隔比较长,所以有可能会丢失大量数据

4)、什么是AOF?分为几个阶段?(命令写入缓冲区、同步至日志文件、文件重写)
AOF:
将redis数据以 操作 + 数据的命令形式存在日志文件中(appendonly.aof),默认是关闭的
三个阶段:
1、命令写入缓冲区
2、文件命令同步(刷盘策略)
3、文件重写

5)、AOF命令同步至日志文件分为哪几种?
1、always
2、everyseconds
3、no

6)、AOF文件重写时间点?重写做了什么?
重写的时机:
1、文件大小超过64M
2、跟上次文件大小相比,增长比例超过100%,也就是原来的2
重写干了什么:
删除没用的命令、合并多条命令以缩小文件大小

7)、AOF的优缺点分别有哪些?
优点:
数据实时性高,不易丢失数据
缺点:
加载速度慢,文件体积大

8)、AOF和RDB同时开启会优先使用哪种进行数据恢复?
AOF

9)、RDB-AOF混合模式是什么?优点有哪些?
RDB-AOF混合模式:
是在AOF重写阶段创建一个同时包含RDB数据和AOF数据的AOF文件,其中RDB数据位于AOF文件的开头,
他存储了服务器开始执行重写操作时Redis服务器的数据状态(RDB 快照方案),
重写操作执行之后的Redis命令,则会继续 append 在AOF文件末尾,一般这部分数据都会比较小。
优点:
这样在Redis重启的时候,则可以先加载RDB的内容,然后再加载AOF的日志内容,这样重启的效率则会得到很大的提升,
而且由于在运行阶段 Redis 命令都会以 append 的方式写入AOF文件,保证了数据的实时性和安全性

4、Redis主从结构能解决什么问题?

提高并发读写效率

5、Redis主从同步具体流程是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1)、建立连接
2)、数据同步(全量同步、增量同步)【runid运行ID、offset偏移量、复制积压缓冲区】
1、全量同步具体场景有哪些?流程?如何避免全量同步次数(全量同步非常耗时)
具体场景:
1、slave节点第一次连接master节点时
2、slave节点断开时间太久,repl_baklog中的offset已经被覆盖时
流程:
1、从服务器向主服务器发送SYNC请求
2、主服务器创建快照,并将快照生成期间产生的写命令存储到缓冲区
3、主服务器向从服务器发送同步快照命令
4、从服务器加载解析快照
5、主服务器将从服务器同步快照期间产生的写命令存储到缓冲区
6、主服务器向从服务器发送缓冲区中存储的命令
7、从服务器加载缓冲
8、完成,主服务器向从服务器同步写操作命令
如何避免全量同步次数:
增大复制缓冲区的配置

2、增量同步具体场景有哪些?流程?
具体场景:
slave节点断开又恢复,并且在repl_baklog中能找到offset时
流程:
Slave完成初始化后开始正常工作时,Master每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行
3)、命令传播

6、如何优化主从同步效率?

1
2
3
4
5
6
1)、从尽量避免全量同步的方面入手(安全重启使runid不发生变化、调大复制积压缓冲区)
安全重启使runid不发生变化、调大复制积压缓冲区

2)、避免slave从结点太多造成复制风暴(使用树状拓补结构)
1.主节点分散多机器(将master分散到不同机器上部署)
2.还有我们可以采用高可用手段(slave晋升master)

7、Redis的故障恢复依靠什么机制?哨兵机制的主要工作范围、工作流程和作用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Redis的故障恢复机制:
哨兵机制
工作范围:
监控、故障转移、通知
工作流程:
1、每个 Sentinel 进程每秒向整个集群中的主服务器,从服务器以及其他 Sentinel 进程发送一个 PING 命令
2、如果一个实例距离最后一次有效回复 PING 命令的时间超过所指定的值, 则这个实例会被 Sentinel 进程标记为主观下线
3、如果一个主服务器被标记为主观下线,则正在监视这个主服务器的所有 Sentinel 进程要每秒确认主服务器的确进入了主观下线状态
4、当有足够数量的 Sentinel 进程在指定的时间范围内确认主服务器进入了主观下线状态, 则主服务器会被标记为客观下线
5、每个 Sentinel 进程会以每 10 秒向集群中的所有主服务器、从服务器发送 INFO 命令,更新redis主从最新的一个拓补情况
6、当主服务器被 Sentinel 进程标记为客观下线时,Sentinel 进程向下线的主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次
7、若没有足够数量的 Sentinel 进程同意主服务器下线,主服务器的客观下线状态就会被移除。
若主服务器重新向 Sentinel 进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
作用:
1、监控:3个心跳
10秒一次:更新redis主从最新的一个拓补情况
3秒一次:用于Sentinel哨兵节点之间交流对redis数据节点的看法
1秒一次:用于发出ping,检测redis节点是否还存活(主观下线、客观下线)
2、故障转移:当对主节点进行客观下线后,选举出一个新的主节点,并且将其他节点连接上新的主节点,最后将原来的master标记为从结点
3、通知:用于master发生了变动,将变化推送给客户端

8、什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
缓存雪崩:
Redis挂掉了,请求全部走数据库。
对缓存数据设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库
解决方案:
1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
2、如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中
3、允许的话,设置热点数据永远不过期
4、要保证redis的高可用,可以使用主从+哨兵或redis cluster,避免服务器不可用
5、使用redis的持久化RDB+AOF组合策略,防止缓存丢失并且可以快速恢复数据

缓存击穿:
缓存中没有但数据库中有的数据,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,
引起数据库压力瞬间增大,造成过大压力
解决方案:
1)、设置热点数据不过期;
2)、第一时间去数据库获取数据填充到redis中,但是这个过程需要加锁,防止所有线程都去读取数据库,
一旦有一个线程去数据库获取数据了,其他线程取锁失败后可设置一个合理睡眠时间之后再去尝试去redis中获取数据;
缓存穿透:
缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为 -1 的数据或id为特别大不存在的数据。
这时的用户很可能是攻击者,攻击会导致数据库压力过大
解决方案:
1、接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
2、从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null
缓存有效时间可以设置短点,设置太长会导致正常情况也没法使用。这样可以防止攻击用户反复用同一个id暴力攻击;
3、引入布隆过滤器,过滤一些异常的请求。

9、Redis是单线程的,为什么读写效率还那么高?

基于非阻塞的IO多路复用机制的线程模型

10、Redis的线程模型是什么样的?(典型的NIO,非阻塞式IO)

基于非阻塞的IO多路复用机制的线程模型,单线程

11、Redis过期数据的删除策略是什么?有哪些?

定时删除、惰性过期、定期过期

12、Redis的数据淘汰策略是什么?有哪些?

1
2
3
4
5
6
7
8
9
10
11
12
13
1、volatile-lru:
从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
2、volatile-ttl:
从已设置过期时间的数据集中挑选将要过期的数据淘汰
3、volatile-random:
从已设置过期时间的数据集中任意选择数据淘汰
4、allkeys-lru:
从数据集中挑选最近最少使用的数据淘汰
5、allkeys-random:
从数据集中任意选择数据淘汰
6、no-enviction(驱逐):
禁止驱逐数据

13、Redis的慢查询如何排查?

Redis 慢查询可通过配置两个参数进行:
slowlog-log-slower-than:设置慢查询预设的超时阈值,单位是微秒
slowlog-max-len:表示慢查询日志存储的条数

14、如何正确使用Redis的分布式事务锁?(Zookeeper也可以实现分布式锁)

1
2
3
正确使用redis分布式事务锁需要保证两个原子性:
1、上锁和设置过期时间需要保证原子性;
2、 判断锁ID是否为自己所有和解锁需要保证原子性

15、Redis的双写一致性如何保证?

1
2
3
4
采用延时双删策略:
1、先淘汰缓存
2、再写数据库
3、休眠1秒,再次淘汰缓存

16、项目搭建多级缓存的好处是什么?实现多级缓存的流程是什么?(加分项)

好处:
可以避免缓存雪崩(缓存失效,大量请求直达DB),提高系统的可用性
流程:
1、反向代理nginx将请求负载均衡到业务nginx集群
2、接着业务Nginx读取本地缓存(本地缓存可以使用Lua),如果本地缓存命中则直接返回,
使用应用Nginx本地缓存可以提升整体的吞吐量,降低后端的压力,尤其应对热点问题非常有效
3、如果Nginx本地缓存没有命中,则会读取相应的分布式缓存(如Redis缓存,另外可以考虑使用主从架构来提升性能和吞吐量),
如果分布式缓存命中则直接返回相应的数据(并会写到Nginx本地缓存)
4、如果分布式缓存也没有命中,则会回源到Tomcat集群,在回源到Tomcat集群时也可以使用轮询和一致性哈希作为负载均衡算法
5、在Tomcat中,首先读取本地堆缓存,如果有则直接返回(并会写到Redis集群)
6、如果所有的缓存都没有命中只能查询DB或相关服务获取相关数据并返回
7、返回的数据异步写到Redis

17、Redis的hash槽一共有多少个?数据是如何进行入槽的?如果实现动态扩容?

1
2
3
4
5
6
7
8
9
10
11
个数:
16384
数据进行入槽方式:
对Key的有效部分进行运算产生hash值,再拿hash对16384进行取余,余数是多少,所属的槽就是哪个
实现动态扩容方式:
1、重新分配哈希槽
2、输入要分配多少个哈希槽(数量)
3、输入指定要分配哈希槽的节点ID
4、选择需要分配的哈希槽来源,分配哈希槽有两种方式:
1、将所有节点用作哈希槽的源节点
2、在指定的节点拿出指定数量的哈希槽分配到目标节点

七、Rabbitmq

1、Rabbitmq消息模式有哪些?你们用的哪种?(5种)

1
2
3
4
5
6
7
8
9
10
1、简单模式:
单发送单消费
2、工作模式:
单发送多接收
3、发布、订阅模式:
生产者端发送消息,多个消费者同时接收所有的消息
4、路由模式:
生产者按 routing key 发送消息,不同的消费者端按不同的 routing key 接收消息
5、Topic:
生产者端不只按固定的 routing key 发送消息,而是按字符串“匹配”发送,消费者端同样如此

2、Rabbitmq如何保证mq消息可靠性?(3大方面)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1、保证消息投递到MQ不丢:
开启生产者消息确认机制
1)、开启 ConfirmCallBack 回调:消息投递至交换机的回调(成功、失败、异常)
2)、开启 ReturnCallBack 回调:消息从交换机未路由到队列的回调
2、保证消息到达MQ之后不丢(MQ宕机了也不影响):
消息持久化(三者默认就是持久化的)
1)、交换机的持久化
2)、队列的持久化
3)、消息的持久化
3、保证消费者不丢(必须至少消费一次):
开启消费者消息确认机制
1)、none:没有确认机制,如果出现异常也会直接丢弃
2)、manual:手动确认
1、可以自己灵活的控制消息的确认和拒收
2、注意:开启这个机制后一定要记得手动回执,否则将造成消息大量堆积问题
3)、auto:自动确认
优点:
出现异常返回nack,正常执行返回ack,无需编写业务代码、无侵入(原理:AOP)
缺点:
当消费消息出现异常返回nack时会一直反复投递,解决:开启消费者重试机制(有一个最大重试次数)
消费者重试机制:
消息消费失败,消费者会自己发起重试,重试间隔和最大重试次数可灵活配置
当超过最大重试次数时,消费者对于消息会有几种执行策略:
1、直接丢弃:该消息从MQ删除
2、放回队列:将消息放回至队列,然后投递给消费者,消费者进行重试,如此循环往复
3、投递至指定的交换机(常用):将消息投递至专用的交换机,绑定队列后进行精准消费

3、Rabbitmq如何实现延时消息?(2种)

1
2
3
4
5
6
7
8
9
1、利用死信交换机实现延时投递
原理:
消息超时未被消费会被投递至死信队列
设置超时:
1、针对消息设置延时时间
2、针对队列设置延时时间
2、利用延时交换机插件实现延时投递
原理:
交换机延时路由,到达指定时间会被路由至队列

4、什么是死信队列?什么样的消息会进入死信队列?

1
2
3
4
5
6
7
死信队列:
当消息在一个队列中变成一个死信之后,如果配置了死信队列,它将被重新publish到死信交换机,
死信交换机将死信投递到一个队列上,这个队列就是死信队列
什么样的消息会进入死信队列
1、超时过期的消息
2、被拒绝或者nack的消息
3、队列放不下的消息

5、Rabbitmq如何解决消息堆积问题?(3种思路)

1
2
3
4
5
6
7
1、针对同一队列增加多个消费者进行消息消费
2、针对同一消费者开启多个线程进行消息消费
3、加大队列的存储容量:
惰性队列
1)、原理:直接将消息写到磁盘
2)、优点:存储容量大,减少从内存刷消息数据至磁盘的次数,性能更稳定
3)、缺点:性能取决于磁盘IO

6、如何保证消息的幂等性?(从业务层面进行判断)

1
2
3
4
1、数据要写mysql,先根据主键查一下,如果这数据都有了,就别插入了,update一下
2、如果是写redis,那没问题了,反正每次都是set,天然幂等性
3、让生产者发送每条数据的时候,里面加一个全局唯一的id,然后你这里消费到了之后,先根据这个id去数据库查一下,之前消费过吗?
如果没有消费过,就处理,然后这个id写到数据库。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可

八、微服务

1、概念问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1)、什么是微服务?解决微服务各种问题都用了哪些组件?
微服务:
一种良好的分布式架构方案
1、优点:拆分粒度更小、服务更独立、耦合度更低
2、缺点:架构非常复杂,运维、监控、部署难度提高
组件:
1、Nacos:
服务注册中心和配置中心
2、Ribbon:
服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
3、Feign:
基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
4、Hystrix:
发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
5、Gateway:
如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务

2)、什么是单体架构、什么是分布式架构、什么是微服务架构?
单体架构:
将业务的所有功能集中在一个项目中开发,打成一个包部署
优点:
架构简单
部署成本低
缺点:
耦合度高(维护困难、升级困难)
分布式架构:
根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务
优点:
降低服务耦合
有利于服务升级和拓展
缺点:
服务调用关系错综复杂
微服务的架构:
给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案

3)、微服务有哪些特点?
单一职责:
微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
自治:
团队独立、技术独立、数据独立,独立部署和交付
面向服务:
服务提供统一标准的接口,与语言和技术无关
隔离性强:
服务调用做好隔离、容错、降级,避免出现级联问题

2、远程调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
   1)、什么是Feign,用来做什么的?Feign底层调用是怎么实现的?底层协议是什么?优势是什么?
Feign:
Feign是一个声明式WebService客户端。
使用Feign能让编写Web Service客户端更加简单,它的使用方法就是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准
作用:
使编写 java http 客户端变得容易,封装了Http调用流程,更适合面向接口化的编程习惯
底层调用实现流程;
1、基于面向接口的动态代理方式生成实现类
2、根据Contract协议规则,解析接口类的注解信息,解析成内部表现
3、基于RequestBean,动态生成Request
4、使用Encoder将Bean转换成Http报文正文(消息解析和转码逻辑)
5、拦截器负责对请求和返回进行装饰处理
6、日志记录
7、基于重试器发送HTTP请求
8、发送Http请求
底层协议:
Https协议
优势:
1.feign 采用的是基于接口的注解
2.feign 整合了 ribbon,具有负载均衡的能力
3.整合了 Hystrix,具有熔断的能力

2)、服务间调用,其中一个服务宕机了,这个时候怎么做呢?
1、超时处理
2、舱壁模式(隔离)
3、熔断,降级

3)、Ribbon是什么?负载均衡策略有哪些?底层原理是什么?默认是哪种?
Ribbon:
主要功能是提供客户端的软件负载均衡算法
负载均衡策略:
1、随机策略:
随机选择server
2、轮询策略:
按照顺序选择server(ribbon默认策略)
3、重试策略:
在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server
4、最低并发策略:
逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server
5、可用过滤策略:
过滤掉一直失败并被标记为 circuit tripped 的 server,过滤掉那些高并发链接的 server(active connections超过配置的阈值)
6、响应时间加权重策略:
根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低
7、区域权重策略:
综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,
剔除不可用的Zone中的所有server

4)、Ribbon是如何实现轮询的?如果让你自己实现轮询,如何实现?
原理:
rest 接口的第几次请求数 % 服务器集群总数量 = 实际调用服务器的下标,每次服务重启,rest接口计数从1开始

5)、Feign和Ribbon的关系是什么?
Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端

6)、你们项目中如何使用Feign的(Feign的最佳实践)
将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

7)、Feign远程调用时的日志级别有哪些?
1、NONE,无记录(DEFAULT)
     2、BASIC,只记录请求方法和URL以及响应状态代码和执行时间
     3、HEADERS,记录基本信息以及请求和响应标头
     4、FULL,记录请求和响应的头文件,正文和元数据

8)、如何优化Feign的调用性能?
1、配置连接池
2、设置合理的日志级别(basic级别就ok了,强烈不建议使用full)

7)、Feign的默认超时时间是多久?重试次数是几次?
默认超时时间:
1
重试次数:
1

8)、Dubbo服务注册与发现的原理?(官方原理图)
1、服务器容器负责启动、加载、运行服务提供者
2、服务提供者在启动时,向注册中心注册自己提供的服务
3、服务消费者在启动时,向注册中心订阅自己需要的服务
4、注册中心返沪服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
5、服务消费者从提供者地址列表中基于负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

9)、Dubbo负载均衡策略有哪些?默认是哪种?
1、Random LoadBalance(默认):
随机,按权重设置随机概率
2、RoundRobin LoadBalance:
轮询,按公约后的权重设置轮询比例
3、LeastActive LoadBalance:
最少活跃调用数,相同活跃数的随机
4、ConstantHash LoadBalance:
一致性 Hash,相同参数的请求总是发到同意提供者

10)、Dubbo支持哪些通信协议?默认是哪种?一般用哪种协议?有什么好处?
Dubbo支持9种通信协议:
1、dubbo 协议
2、rmi 协议
3、hessian 协议
4、http 协议
5、webservice 协议
6、thrift 协议
7、memcached 协议
8、redis 协议
9、rest
默认是哪种:
dubbo 协议 (默认)
一般用哪种协议:
dubbo 协议
好处:
底层是TCP,效率快

11)、注册中心挂了影响服务调用吗?为什么?
不会
因为启动dubbo时,消费者会从注册中心拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用

12)、Dubbo启动检查如何设置?多版本支持如何设置?
启动检查:
可以通过配置信息 check="false" 关闭检查
多版本支持:
@DubboService(version = “2.0.0”)
@DubboReference(version = "2.0.0")

13)、Dubbo的默认超时时间是多久?重试次数是几次?
默认超时时间:
1
默认重试次数:
2

14)、Dubbo进行服务注册和发现的核心注解是哪个?
@DubboService
@DubboReference

15)、Dubbo服务如何进行监控和管理?
dubbo-admin 监控中心

3、注册中心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
3、配置中心1)、Eureka
1、eureka是属于什么体系的技术(Spring Cloud)
Spring Cloud
2、eureka技术体系有哪些角色?(服务端用作注册中心,客户端用作微服务)
服务端用作注册中心,客户端用作微服务
3、eureka的自我保护机制是什么?什么时候开启?为什么开启?开启了会发生什么?
自我保护机制:
某时刻某个微服务不能用了,eureka不会立刻清理,而是对该微服务进行保存
开启时间:
当EurekaServer节点在短时间内丢失过多客户端时候(可能发生了网络分区故障)
为什么开启:
默认情况下,如果EurekaServer在一定时间内没有收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90s)。
但是当网络分区故障发生(延时、卡顿、拥挤)时候,微服务与EurekaServer之间无法正常通信,
以上行为可能变得非常危险了,因为微服务本身其实是健康的。此时本不应该注销这个微服务的
开启了会发生什么:
一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务

4、eureka作为注册中心的原理是什么?心跳检测某个服务是否健康的原理详细说下?
原理:
1、服务提供者启动后将注册到注册中心,提供IP, 名字,什么服务等信息,
2、服务消费者作为客户端注册到注册中心后,拉取注册中心的服务列表,在通过负载均衡调用对应的服务提供者。
3、注册中心可以建立集群,生成多台eureka,注册中心为了监测各个服务的心跳,将在每 30S 向所注册的服务发起请求判断
4、服务是否挂掉,如果挂掉90S后将会将服务从注册中心剔除。
一个服务可以监测多台服务实例,从而可实现均衡负载。
心跳检测:
在应用启动后,节点们将会向Eureka Server发送心跳,默认周期为30秒,
如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,
Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

5、eureka集群是属于AP还是CP?(AP)
AP
2)、Nacos
1、nacos是属于什么体系的技术(Spring Cloud Alibaba)
Spring Cloud Alibaba

2、nacos作为注册中心的原理是什么?
服务注册时在服务端本地会通过轮询注册中心集群节点地址进行服务注册,
在注册中心上,即Nacos Server上采用了Map保存实例信息,当然配置了持久化的服务会被保存到数据库中,
在服务的调用方,为了保证本地服务实例列表的动态感知,Nacos与其他注册中心不同的是,采用了 Pull/Push同时运作的方式

3、nacos如何确定唯一的一个服务?(通过namespace、group、service、集群唯一确定一个服务)
通过namespace、group、service、集群唯一确定一个服务

4、nacos中namespace、group分别的作用是什么?
namespace:
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置
group:
Nacos中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串对配置集进行分组,从而区分 Data ID 相同的配置集

5、nacos和eureka的异同有哪些?
相同点:
1、都支持服务注册和服务拉取
2、都支持服务提供者心跳的方式做健康检测
不同点:
1、nacos支持服务端主动检测提供者状态:
临时实例采用心跳模式,非临时实例采用主动检测模式(一般情况下都使用临时实例,主动检测消费的服务器资源较大,服务器压力大)
2、临时实例心跳不正常会被剔除,非临时实例则不会被剔除
3、nacos支持服务列表变更的消息推送模式,服务列表更新及时
4、nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式

6、nacos的临时节点和非临时节点有什么区别?
非临时实例挂掉后不会被nacos剔除,而是等待他重连

7、nacos集群是属于AP还是CP?(AP或CP)
nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式

3)、Zookeeper
1、Zookeeper的内部结构是什么?
ZooKeeper维护和操作一个小型的数据节点,这些节点被称之为znode,采用类似于文件系统的层级树状结构进行管理

2、使用Zookeeper作为分布式事务锁的原理是什么?
锁分为两种:
共享锁(读锁)和排他锁(写锁)
读锁:
当有一个线程获取读锁后,其他线程也可以获取读锁,但是在读锁没有完全被释放之前,其他线程不能获取写锁。
写锁:
当有一个线程获取写锁后,其他线程就无法获取读锁和写锁了。
zookeeper 有一种节点类型叫做临时序号节点,它会按序号自增地创建临时节点,这正好可以作为分布式锁的实现工具。

3、Zookeeper集群属于AP还是CP?(CP)
CP

3、配置中心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1)、实现配置中心都可以使用哪些技术?
1、Spring Cloud Config
2、Apollo
3、Nacos

2)、使用nacos作为配置中心,如何实现热更新?
1、在 @Value 注入的变量所在的类上添加注解 @RefreshScope
2、使用 @ConfigurationProperties(prefix = "pattern") 注解

3)、nacos作为配置中心,为什么需要用到bootstrap文件?
得知nacos地址、配置文件id

4)、远程配置文件和本地配置文件属性加载优先级是什么样的?
远程 > 本地
高优先级的会覆盖低优先级的重复的配置内容

5)、使用配置中心的好处是什么?能解决什么问题?
好处:
将配置文件集中管理
解决的问题:
可以在配置变更时,及时通知微服务,实现配置的热更新

4、服务保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
1)、Hystrix
1、hystrix是属于什么体系的技术?(SpringCloud)
SpringCloud

2、hystrix可以用来干嘛?(服务熔断降级)
服务熔断降级

3、hystrix默认的触发熔断策略是什么?(5分钟之内服务调用异常比例达到一半或者失败次数超过20次)
5分钟之内服务调用异常比例达到一半或者失败次数超过20次

4、hystrix的隔离是基于什么?(线程池隔离【低扇出】)
线程池隔离(低扇出)

2)、Sentinel
1、sentinel可以用来干嘛?(限流、隔离、熔断、降级)
限流、隔离、熔断、降级

2、什么是微服务雪崩现象?如何解决微服务雪崩问题?
微服务雪崩:
微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况
解决方案:
1、超时处理
2、船壁模式(隔离)
3、熔断,降级

3、sentinel的限流模式有哪些?分别的运用场景是什么?
1、直接:
对当前资源限流
2、关联:
比如用户支付时需要修改订单状态,同时用户要查询订单。
查询和修改操作会争抢数据库锁,产生竞争。
业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
3、链路:
阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流

4、sentinel的限流效果有哪些?分别的运用场景是什么?
1、快速失败:
QPS超过阈值时,拒绝新的请求
2、warm up:
QPS超过阈值时,拒绝新的请求;
QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机
3、排队等待:
请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝
4、热点参数:
分别统计参数值相同的请求,判断是否超过QPS阈值

5、sentinel支持对热点参数进行限流吗?
支持

6、实现微服务调用隔离有两种方式(信号量隔离和线程池隔离),区别是什么?sentinel是使用的哪种?
区别:
1、信号量隔离:
不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。
特点:基于计数器模式,简单,开销小;高扇出
2、线程池隔离
给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果
特点:基于线程池模式,有额外开销,但隔离控制更强;低扇出
sentinel是使用的哪种:
信号量隔离

7、什么是熔断?熔断的原理是什么?什么时候会触发sentinel的熔断?断路器的三种状态是哪些?是怎样进行切换的?
概念:
熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),
对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误
熔断的原理:
断路器
什么时候会触发sentinel的熔断:
1、慢调用
2、异常比例或者异常数
断路器的三种状态是哪些,是怎样进行切换的:
1、关闭:默认就是关闭的
2、半开:开启后到达指定(可配置)的时间,开启半开状态,尝试接收请求
如果成功:状态置为关闭
如果失败:状态置为开启
3、开启:当触发配置的阈值,会开启

8、什么是降级?如何实现降级?
概念:
当微服务调用失败,客户端走降级逻辑
如何实现降级:
1、FallBackClass
2、FallBackFactoryClass(优势:可以获取服务端抛出的异常)

9、sentinel授权规则是用来干什么的?
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式:
1、白名单:来源(origin)在白名单内的调用者允许访问
2、黑名单:来源(origin)在黑名单内的调用者不允许访问

10、sentinel的规则持久化方式有哪些?一般使用哪种?
sentinel的规则持久化方式:
1、原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
2、pull模式
3、push模式
一般使用哪种:
push模式

5、网关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1)、网关有什么作用?在你们项目中用网关来干嘛了?
1、权限控制:
网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
2、路由和负载均衡:
一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。
当然路由的目标服务有多个时,还需要做负载均衡。
3、限流:
当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

2)、网关的核心技术点有哪些?
1、断言工厂
2、过滤器工厂
3、全局过滤器
4、解决跨域

3)、网关的路由是用来干嘛的?分为哪几种?
路由的作用:
一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务
分为哪几种:
静态路由、动态路由

4)、网关的过滤器是用来干嘛的?分为哪几种?
网关的过滤器作用:
可以对进入网关的请求和微服务返回的响应做处理
分为哪几种:
1、路由过滤器
2、请求头过滤器
3、默认过滤器

5)、网关局部过滤器和全局过滤器的区别有哪些?
局部过滤器:
拦截经过网关的特定服务的请求
全局过滤器:
无差别拦截所有经过网关 的请求

6)、网关中局部过滤器、默认过滤器、全局过滤器的执行顺序是什么?
默认过滤器 > 局部过滤器 > 全局过滤器

7)、加入网关后,访问一个链接,你们项目的执行流程是什么?
1、客户端发送请求
2、进行权限校验,如果是登录或者注册操作则直接放行
3、校验通过生成该用户的token,校验失败拦截
4、路由匹配到某个微服务

8)、定义全局过滤器需要实现哪几个接口?
GlobalFilter

6、分布式事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
1)、什么是本地事务?什么是分布式事务?
本地事务:
无需跨越多个服务或者数据源的单体事务,一般由spring控制即可(声明式事务处理:AOP)
分布式事务:
指一个业务跨越多个服务或者数据源,每个事务叫做分支事务,要保证所有分支事务要么成功,要么失败

2)、什么是CAP定理?为什么必须保证P?为什么在保证P的前提下只能保证C或者A其中一个?
CAP定理:
Consistency(一致性)、Availability(可用性)、Partition tolerance (分区容错性),这三个指标不可能同时做到
为什么必须保证P:
在分布式系统中,系统间的网络不能100%保证健康,一定会有故障的时候,而服务有必须对外保证服务。因此Partition Tolerance不可避免
为什么在保证P的前提下只能保证C或者A其中一个:
如果此时要保证一致性,就必须等待网络恢复,完成数据同步后,整个集群才对外提供服务,服务处于阻塞状态,不可用。
如果此时要保证可用性,就不能等待网络恢复,那 node01、node02、node03 之间就会出现数据不一致。

3)、什么是BASE理论?
BASE理论是对CAP的一种解决思路,包含三个思想:
1、Basically Available(基本可用):
分布式系统在出现故障时,允许损失部分可用性,即保证核心可用
2、Soft State(软状态):
在一定时间内,允许出现中间状态,比如临时的不一致状态
3、Eventually Consistent(最终一致性):
虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致

4)、seata解决分布式事务的三个角色以及分别的作用什么?
1、TC (Transaction Coordinator) - 事务协调者:
维护全局和分支事务的状态,协调全局事务提交或回滚
2、TM (Transaction Manager) - 事务管理器:
定义全局事务的范围、开始全局事务、提交或回滚全局事务
3、RM (Resource Manager) - 资源管理器:
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

5)、seata解决分布式事务的四种模式
1、XA
2、AT
3、TCC
4、SAGA

6)、XA模式特点、原理以及应用场景?优缺点有哪些?
特点:
强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入 (CP)
原理:
1)、第一阶段:
1、TM:
1、开启全局事务
2、调用分支事务
2、TC:
接收开启全局事务的请求
3、RM:
1、将分支事务注册到TC服务
2、执行本地sql,但是,重点:不提交事务!!!!
3、将本地事务状态报告至TC服务
2)、第二阶段:
1、TM:
等第一阶段所有分支事务执行完,发起提交/回滚全局事务的命令
2、TC:
接收全局事务提交/回滚请求,核查所有分支事务的状态,对RM发起提交/回滚的命令
3、RM:
提交或者回滚当前的分支事务(依赖于数据库)
优点:
强一致性、无代码侵入、实现简单
缺点:
强依赖于关系型数据库实现回滚、性能比较差

7)、AT模式特点、原理以及应用场景?优缺点有哪些?有可能会出现什么问题?如何解决?
特点:
同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷,属于最终一致(AP)
原理:
1)、第一阶段:
1、TM:
1、开启全局事务
2、调用分支事务
2、TC:
接收开启全局事务的请求
3、RM:
1、将分支事务注册到TC服务
2、执行本地sql,重点:提交事务!!!!
3、执行sql前后,生成快照:undo_log
4、将本地事务状态报告至TC服务
2)、第二阶段:
1、TM:
等第一阶段所有分支事务执行完,发起提交/回滚全局事务的命令
2、TC:
接收全局事务提交/回滚请求,核查所有分支事务的状态,对RM发起提交/回滚的命令
3、RM:
提交或者回滚当前的分支事务(依赖于undo_log快照数据)
提交:删除快照数据
回滚:根据快照进行数据恢复
优点:
1、一阶段完成直接提交事务,释放数据库资源,性能比较好
2、利用全局锁实现读写隔离
3、没有代码侵入,框架自动完成回滚和提交
缺点:
1、两阶段之间属于软状态,属于最终一致
2、框架的快照功能会影响性能,但比XA模式要好很多
可能出现的问题:
脏写:当全局事务1提交修改的数据后,此时全局事务2又过来修改了这条数据
后续阶段二全局事务1需要利用快照进行回滚,将全局事务2的所有修改进行了覆盖
解决方案:
seata内部提供了全局锁的概念(需要在seata server新增一张全局锁的表)
但是全局锁有可能导致死锁(内部通过限制获取全局锁的次数来解决:30次/10ms)

8)、重点:TCC模式特点、原理以及应用场景?优缺点有哪些?有可能会出现什么问题?什么是空回滚和业务悬挂,如何解决?
特点:
与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。属于最终一致(AP)
原理:
1、T: Try,进行资源的检测和预留
2、C:Confirm,对资源进行确认操作(业务执行和提交)
3、C:Cancle,对资源进行回滚操作(预留资源的释放)
优点:
1、一阶段完成直接提交事务,释放数据库资源,性能好
2、相比AT模型,无需生成快照,无需使用全局锁,性能最强
3、不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
缺点:
1、有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
2、软状态,事务是最终一致
3、需要考虑Confirm和Cancel的失败情况,做好幂等处理
可能出现的问题:
1、空回滚
问题描述:
当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。
在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,则就是空回滚。
解决办法:
执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。
2、业务悬挂
问题描述:
对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能
confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。
解决办法:
执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

9)、SAGA模式特点、原理以及应用场景?优缺点有哪些?
概念:
Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。
原理:
在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。
如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,
使分布式事务回到初始状态。
优点:
1、事务参与者可以基于事件驱动实现异步调用,吞吐高
2、一阶段直接提交事务,无锁,性能好
3、不用编写TCC中的三个阶段,实现简单
缺点:
1、软状态持续时间不确定,时效性差
2、没有锁,没有事务隔离,会有脏写

九、Docker

1、什么是Docker?优点是什么?有哪些核心概念?

概念:
Docker是一个快速交付应用、运行应用的技术
优点:
1、可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
2、运行时利用沙箱机制形成隔离容器,各个应用互不干扰
3、启动、移除都可以通过一行命令完成,方便快捷
核心概念:
解决大型项目依赖关系复杂,不同组件依赖的兼容性问题:
1、Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
2、Docker应用运行在容器中,使用沙箱机制,相互隔离
解决开发、测试、生产环境有差异的问题:
Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行

2、镜像操作命令有哪些?

拉取、推送、查看、查看所有、删除、删除所有、制作镜像、导出镜像、加载镜像

3、容器操作命令有哪些?

查看所有、查看正在运行的容器、删除、强制删除、创建容器、创建并运行、
启动容器、停止容器、重启容器、暂停容器、恢复容器、进入容器

4、数据卷操作命令有哪些?

创建数据卷、查看单个数据卷详情、查看数据卷列表、删除数据卷、删除未使用的数据卷、创建容器时挂载数据卷

5、docker如何自定义镜像?docker file的语法是什么样的?

自定义镜像:
1、准备基础镜像和tar包
2、创建Dockerfile
3、使用Dockerfile创建镜像
docker file的语法:
保留字指令要大写,后边有空格,后边必须有内容。 比如:FROM scratch
指令从上往下依次执行

6、docker compose是干嘛的?语法是什么样的?

作用:
调用docker服务的API负责实现对docker容器集群的快速编排,即通过一个单独的yaml文件,来定义一组相关的容器来为一个项目服务
语法:
一份标准配置文件应该包含 version、services、networks 三大部分,其中最关键的就是 services 和 networks 两个部分

十、ElasticSearch

1、什么是ES?由什么语言编写?和Lunce的关系?什么是ELK?

概念:
1)、是一款分布式、高性能、高扩展,支持海量数据分析、搜索、计算的搜索引擎
2)、基于Java语言编写,发起的请求时基于Json风格的符合RestFull风格的DSL语句
3)、前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES
4)、ELK:是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3
5)、Solr:是ES的一款竞品,2016被ES反超,主流成为ES
由什么语言编写:
Java
和Lunce的关系:
前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES
什么是ELK:
是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3

2、ES的核心概念有哪些?什么是索引?什么是文档?文档格式是什么?什么是映射?什么是DSL?

核心概念:
1)、倒排索引
对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,
再根据id找到相应的文档(涉及到两次的Btree查询)
2)、索引 – index
同一类型文档的集合,相当于mysql的表
3)、映射 – mapping
对索引结构的约束,相当于mysql的schema(约束)(表结构)
4)、文档 – document
Json格式的数据,相当于mysql的row(行)
5)、字段 – field
一个个的字段,相当于mysql的列
6)、DSL语句
Json风格的符合restful风格的请求语句

3、什么是倒排索引?倒排索引建立过程?

倒排索引:
对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,
再根据id找到相应的文档(涉及到两次的Btree查询)
倒排索引建立过程:
1、将每一个文档的数据利用算法分词,得到一个个词条
2、创建表,每行数据包括词条、词条所在文档id、位置等信息
3、因为词条唯一性,可以给词条创建索引,例如hash表结构索引

4、ES有哪些数据类型?keyword和text有什么区别?

数据类型:
1、字符串:
text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
2、数值:
long、integer、short、byte、double、float、
3、布尔:
boolean
4、日期:
date
5、对象:
object
keyword和text有什么区别:
text可分词,keyword不可分词

5、重要:说说用户输入框输入查询条件 进行ES搜索的底层原理过程

1、如果用户输入条件“华为手机”进行搜索。
2、对用户输入内容分词,得到词条:“华为”、“手机”。
3、拿着词条在倒排索引中查找,可以得到包含词条的文档id:1、2、3。
4、拿着文档id到正向索引中查找具体文档。

6、ES分词器适合在什么字段上使用?分词器在ES中的使用场景有哪些?(建立倒排索引时对文档分词和用户搜索时对搜索条件分词)

建立倒排索引时对文档分词和用户搜索时对搜索条件分词

7、你们分词器用的哪种?为什么要自定义拼音分词器?为什么搜索时不能用拼音分词器?

用的哪种:
IK分词器
为什么要自定义拼音分词器:
要实现根据字母做补全,就必须对文档按照拼音分词。
默认的拼音分词器会将每个汉字单独分为拼音,而我们希望的是每个词条形成一组拼音,需要对拼音分词器做个性化定制,形成自定义分词器
为什么搜索时不能用拼音分词器:
为了避免搜索到同音字,搜索时不要使用拼音分词器

8、ES有哪些查询类型,分别用在什么场景?如何实现复合查询?要给指定的数据进行加分如何实现?

查询类型:
1、查询所有:
查询出所有数据,一般测试用。例如:match_all
2、全文检索(full text)查询:
利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
match_query
multi_match_query
3、精确查询:
根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
ids
range
term
4、地理(geo)查询:
根据经纬度查询。例如:
geo_distance
eo_bounding_box
5、复合(compound)查询:
复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
bool
function_score
如何实现复合查询:
1、根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
2、根据过滤条件,过滤文档
3、符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
4、将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。
给指定的数据进行加分如何实现:
算分函数:
可以简单粗暴,直接给固定的算分结果,weight

9、给指定的数据进行加分如何实现?如何实现高亮?

给指定的数据进行加分如何实现:
1、排序
2、分页
3、高亮
如何实现高亮:
1、给文档中的所有关键字都添加一个标签,例如标签
2、页面给标签编写CSS样式

10、ES有哪些聚合查询?

1、桶(Bucket)聚合:
用来对文档做分组
2、度量(Metric)聚合:
用以计算一些值,比如:最大值、最小值、平均值等
3、管道(pipeline)聚合:
其它聚合的结果为基础做聚合

11、ES如何实现自动补全查询

1、修改索引库结构,设置自定义拼音分词器
2、修改索引库的需要补全的属性,使用自定义分词器
3、索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器
4、给实体类doc添加suggestion字段
5、重新导入数据到索引库

12、如何自定义分词器?

1、创建索引库时,在settings中配置,可以包含三部分
2、character filter:
在tokenizer之前对文本进行处理。例如删除字符、替换字符
3、tokenizer:
将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
4、filter:
将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等

13、如何实现es与mysql的数据同步?

1、同步调用:
1、ES微服务对外提供接口,用来修改elasticsearch中的数据
2、操作数据库微服务在完成数据库操作后,直接调用微服务提供的接口,
2、异步通知:
1、操作数据库微服务对mysql数据库数据完成增、删、改后,发送MQ消息
2、ES微服务监听MQ,接收到消息后完成elasticsearch数据修改
3、监听binlog
1、给mysql开启binlog功能
2、mysql完成增、删、改操作都会记录在binlog中
3、ES微服务基于canal监听binlog变化,实时更新elasticsearch中的内容

14、es集群节点有哪些类型?分别的职责是什么?

1、备选主节点(master eligible):
主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求
2、数据节点(data):
存储数据、搜查、聚合、CRUD
3、ingest:
数据存储之前的预处理
4、coordinating:
路由请求到其他节点,合并其他节点处理的结果,返回给用户

15、什么是es脑裂问题?

1、一个集群中,主节点与其它节点失联
2、此时,node2和node3认为node1宕机,就会重新选主
3、当node3当选后,集群继续对外提供服务,node2和node3自成集群,node1自成集群,两个集群数据不同步,出现数据差异
4、当网络恢复后,因为集群中有两个master节点,集群状态的不一致,出现脑裂的情况

十一、压测、高并发性能优化

1、jemiter压测工具使用
2、多级缓存(nginx共享字典、redis缓存、tomcat进程缓存)
3、数据库主从读写分离(mycat)
4、发布:
开发环境、测试环境用的shell脚本自动发布(包括从git上拉取代码、打包编译、启动)
预上线、线上环境用的jenkins持续集成
5、提交代码之前需要做什么?

  • 标题: Java面试题库
  • 作者: 小颜同学
  • 创建于: 2023-05-28 11:05:48
  • 更新于: 2024-02-07 14:23:20
  • 链接: https://www.wy-studio.cn/2023/05/28/Java面试题库/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论
此页目录
Java面试题库