当前位置:   article > 正文

Java面试题总结_getaway面试题

getaway面试题

目录

技术面试题

一、Java基础

1、JVM、JRE和JDK的关系

2、Final、static

3、静态方法为什么不能调用非静态成员?

4、静态方法和实例方法有何不同?

5、重载和重写的区别

6、== 和 equals() 的区别

7、hashCode() 与 equals()

8、为什么重写 equals() 时必须重写 hashCode() 方法?

9、包装类型的常量池技术

10、面向对象和面向过程的区别

11、成员变量与局部变量的区别有哪些?

12、一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗。

13、构造方法有哪些特点?是否可被 override?

14、封装

15、继承

16、多态(11.7)

17、深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

18、String、StringBuffer、StringBuilder 的区别?String 为什么是不可变的?

19、字符串常量池的作用了解吗?

20、泛型(你的项目中哪里用到了泛型?)

21.什么是序列化?什么是反序列化?

23.既然有了字节流,为什么还要有字符流?

24.程序计数器(pc寄存器)

25.栈

26.线程共享(20221109)

27.Java对象的组成

28.栈上分配

29.确认垃圾是否回收算法

30.垃圾回收的算法(11.10)

31.java常见的数据结构

32.Json的转换器

二、javaWeb

1.jsp和sevlet的区别

三、数据库

(一)mysql

(二)redis

四、linux

1.linux常用指令

2.docker run 和 docker start有什么区别

3.容器和虚拟机的区别

五、框架部分

(一)mybatis

(二)spring

(三)SpringBoot

(四)SpringCloud

(五)Vue

六、其他

(一)版本控制工具

(二)多线程

(三)高并发

(四)登录

(五)网络

(六)jvm

人事面试题

1.为什么从上家公司离职


技术面试题

一、Java基础

1、JVM、JRE和JDK的关系

JVM Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

JRE Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等

JDK Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

2、Final、static

Final

1) 类不能被继承

(2) 子类中,方法不能被重写,JVM会尝试将其关联。

(3) 变量为常量,编译阶段存储在常量池中。

Static

(1) 修饰的类可以直接通过类来调用,而不需要new

(2) 修饰的方法块,JVM会有限加载。

(3) 修饰的变量,分配在内存堆上,引用时指向同一个地质,而不会重新分配内存空间。

3、静态方法为什么不能调用非静态成员?

静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。

在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。

4、静态方法和实例方法有何不同?

在外部调用静态方法时,可以使用 类名.方法名 的方式,也可以使用 对象.方法名 的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象

静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法)

5、重载和重写的区别

重载:参数不同其余相同

重写:子类重写父类方法,返回值类型是父类的返回值或他的子类,权限修饰符不能比父类更严格

6、== 和 equals() 的区别

  • 对于基本数据类型来说,== 比较的是值。

  • 对于引用数据类型来说,== 比较的是对象的内存地址。

  • equals默认比的是地址值,String可以用equals比较是因为String重写了equals方法所以比的是值

7、hashCode() 与 equals()

hashCode() 的作用是获取哈希码,该方法通常用来将对象的内存地址转换为整数之后返回。

如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)

如果两个对象的hashCode 值相等并且equals()方法返回 true,我们才认为这两个对象相等。

8、为什么重写 equals() 时必须重写 hashCode() 方法?

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

java规定: 如果两个对象的hashCode()相等,那么他们的equals()不一定相等。 如果两个对象的equals()相等,那么他们的hashCode()必定相等。

9、包装类型的常量池技术

Java 基本类型的包装类的大部分都实现了常量池技术。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

10、面向对象和面向过程的区别

两者的主要区别在于解决问题的方式不同:

  • 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。

  • 面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。

另外,面向对象开发的程序一般更易维护、易复用、易扩展。

11、成员变量与局部变量的区别有哪些?

成员变量:类中方法外

堆内存

对象创建时存在,对象销毁时销毁

有默认值

局部变量:方法中、参数

栈内存

方法入栈是存在,方法出栈是销毁

无默认值

12、一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗。

构造方法的主要作用是对类对象的初始化工作

如果一个类没有声明构造方法,会默认有一个无参构造

13、构造方法有哪些特点?是否可被 override?

方法名与类名一致

没有返回值

在实例化对象是自动调用

不可被重写,因为方法名必须和类名一致,形成冲突

14、封装

封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。

15、继承

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有

  2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

  3. 子类可以实现或重写父类的方法。

16、多态(11.7)

父类引用指向子类对象

访问成员属性:编译看左,运行看左

访问成员方法:编译看左,运行看右

访问静态属性:编译看左,运行看左

17、深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。

  • 深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

  • 引用拷贝: 两个不同的引用指向同一个对象。

18、String、StringBuffer、StringBuilder 的区别?String 为什么是不可变的?

String 类中使用 final 关键字修饰字符数组来保存字符串,StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 finalprivate 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。

String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

19、字符串常量池的作用了解吗?

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

20、泛型(你的项目中哪里用到了泛型?)

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

Java 的泛型是伪泛型,这是因为 Java 在运行期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。

泛型一般有三种使用方式: 泛型类、泛型接口、泛型方法。

常用的通配符为: T,E,K,V,?

  • ? 表示不确定的 Java 类型

  • T (type) 表示具体的一个 Java 类型

  • K V (key value) 分别代表 Java 键值中的 Key Value

  • E (element) 代表 Element

  • 你的项目中哪里用到了泛型?

  • 可用于定义通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型

21.什么是序列化?什么是反序列化?

序列化:把java对象转换成二进制字节流持久化到硬盘

反序列化:将硬盘中的二进制字节流转换成java对象写入内存

22.Java 中 IO 流分为几种?

  • 按照流的流向分,可以分为输入流和输出流;(inputstream,outputstream)

  • 按照操作单元划分,可以划分为字节流( InputStream 和OutputStream, )和字符流( Reader和 Writer);

  • 其余分为包装流

23.既然有了字节流,为什么还要有字符流?

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。从而提高使用字符流的效率。

24.程序计数器(pc寄存器)

就是一个指针,指向方法区中的方法字节码

前提条件:线程只负责干活,不负责思考跟记录

作用:当前线程正在执行的字节码的行号

25.栈

栈的本质:先进后出的数据结构,栈的速度比堆快

堆是先进先出,堆能动态的分配大小

虚拟机栈

1.局部变量表(存放方法中的局部变量) 2.操作数栈(变量运行过程中数据的中转) 3.动态链接(运行时的多态) 4.出口(出栈:正常/异常)

本地方法栈

带有native方法的,java给你提供的,c语言编写的

26.线程共享(20221109)

- 对象的实例,字符串常量池从jdk1.7之后都是在堆里 方法区(jdk1.8之前实现方式叫永久代,jdk1.8之后实现方式叫元空间) 元空间在电脑内存中:

运行时常量池(class常量池,class文件在内存中运行,最终叫运行时常量池) ​ 类的信息... ​ 字面量:int a = "abc"; abc就是字面量 ​ 引用符号:引用关系

27.Java对象的组成

对象头:gc的年龄,锁的指针,引用关系

对象实例:

数据填充:对象必须是8字节的倍数,不够就进行填充

28.栈上分配

是不是所有的对象实例都在堆中?不是

方法逃逸:默认开启

变量没有逃出方法的范围

栈上分配,将一个对象拆分为基本数据类型

29.确认垃圾是否回收算法

可达性算法

引用计数 Reference Counting

引用计数,就是记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。 很明显,引用计数最大的优势是暂停时间短

优点

  • 可即刻回收垃圾

  • 最大暂停时间短

  • 没有必要沿指针查找, 不要和标记-清除算法一样沿着根集合开始查找

缺点

  • 计数器的增减处理繁重

  • 计数器需要占用很多位

  • 实现繁琐复杂, 每个赋值操作都得替换成引用更新操作

  • 循环引用无法回收

30.垃圾回收的算法(11.10)

一、 标记-清除算法 Mark-Sweep GC

如字面意思 mark-sweep 分为两个阶段:

  1. 标记阶段:从根集合出发,将所有活动对象及其子对象打上标记

  2. 清除阶段:遍历堆,将非活动对象(未打上标记)的连接到空闲链表上

优点

实现简单, 容易和其他算法组合

缺点

  • 碎片化, 会导致无数小分块散落在堆的各处

  • 分配速度不理想,每次分配都需要遍历空闲列表找到足够大的分块

  • 与写时复制技术不兼容,因为每次都会在活动对象上打上标记

二、标记-压缩 Mark-Compact

和“标记-清除”相似,不过在标记阶段后它将所有活动对象紧密的排在堆的一侧(压缩),消除了内存碎片, 不过压缩是需要花费计算成本的。如下图过程,标记后需要定位各个活动对象的新内存地址,然后再移动对象,总共搜索了3次堆。

标记-压缩算法

优点

有效利用了堆,不会出现内存碎片 也不会像复制算法那样只能利用堆的一部分

缺点

压缩过程的开销,需要多次搜索堆

三、

四、 GC 复制算法

将堆分为两个大小相同的444空间 From 和 To, 利用 From 空间进行分配,当 From 空间满的时候,GC将其中的活动对象复制到 To 空间,之后将两个空间互换即完成GC。

GC复制算法

优点

  • 优秀的吞吐量, 只需要关心活动对象

  • 可实现高速分配; 因为分块是连续的,不需要使用空闲链表

  • 不会发生碎片化

  • 与缓存兼容

缺点

  • 堆使用率低

  • 与保守式GC不兼容

  • 递归调用函数, 复制子对象需要递归调用复制函数 消耗栈

五、 保守式GC

根空间有以下几种:

  • 寄存器

  • 调用栈

  • 全局变量空间

但这些都是不明确的根, 因为调用栈里边的调用帧(call frame) 既有指针也有非指针(值类型)

保守式GC检查不明确根的基本项目:

  1. 是不是被正确对齐的值?(在32位cpu的情况下,为4的倍数)

  2. 是不是指针堆内?

  3. 是不是指着对象的开头?

有种情况是,非指正和堆里的对象地址一样;这时保守式GC “把可以的东西看做指针,稳妥处理”

保守式GC优点:GC不依赖于语言处理程序

缺点:

  • 识别指针和非指针需要成本

  • 错误识别指针会压迫堆; 可能错将非指针当做指针,然后将其作为内存地址使得对应堆中的死对象当做活对象

  • 能够使用的gc算法有限; 不能使用复制算法这类移动对象的gc算法

准确式GC

需要依赖 “语言处理程序的支援”,能基于能精确识别指针和非指针的“正确根”来执行gc

六、分代回收

出发点:大部分对象生成后马上就变成垃圾,很少有对象能活的很久

  • 新生代 = 生成空间 + 2 * 幸存区 复制算法

  • 老年代 标记-清除算法

对象在生成空间创建,当生成空间满之后进行 minor gc,将活动对象复制到第一个幸存区,并增加其“年龄” age,当这个幸存区满之后再将此次生成空间和这个幸存区的活动对象复制到另一个幸存区,如此反复,当活动对象的 age 达到一定次数后将其移动到老年代; 当老年代满的时候就用标记-清除或标记-压缩算法进行major gc

吞吐量得到改善, 分代垃圾回收花费的时间是GC复制算法的四分之一;但是如果部分程序新生成对象存活很久的话分代回收会适得其反

七、增量式GC

本来gc只是默默的在幕后回收资源的,但是如果gc任务繁重则会长时间暂停应用程序的执行, 增量式gc就是一种逐渐推进垃圾回收来控制mutator最大暂停时间的方法

三色标记算法

  • 白色: 还未搜索过的对象

  • 灰色: 正在搜索的对象

  • 黑色: 搜索完成的对象

根查找阶段: 对能直接从根引用的对象打上标记,堆放到标记栈里(白色 涂成 灰色)

标记阶段: 从标记栈中取出对象,将其子对象涂成灰色;这个阶段不是一下子处理所有的灰色对象,而只是处理一定个数,然后暂停gc

清除阶段: 将没被标记的白色对象连接到空闲链表,并重置已标记的对象标记位

优点: 缩短最大暂停时间 缺点: 降低了吞吐量

31.java常见的数据结构

数组、链表、堆、栈、队列、树、图、哈希表

32.Json的转换器

FastJson、Gson、Jackson

二、javaWeb

1.jsp和sevlet的区别

  1. jsp经编译后就变成了Servlet.

  2. jsp更擅长表现于页面显示,servlet更擅长于逻辑控制.

  3. Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到.

  4. Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。 而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。

三、数据库

(一)mysql

1.数据库三大范式

第一范式:要求一张表中的数据每一列都是不可分割的原子项数据 ​ 第二范式:消除部分依赖,要求一张表中的每一列都完全依赖于主键 ​ 第三范式:消除传递依赖,要求一张表中的每一列都和主键是直接依赖的,不是间接依赖。

三大范式冲突,不可分割,冗余

2.MySQL由哪些部分组成?

(1)Server

连接器:管理连接、权限验证

分析器:词法分析,语法分析

优化器:执行计划生成,索引的选择

执行器:操作存储引擎,返回执行结果

(2)存储引擎

存储数据,提供读写接口。

3.数据库的优化方式

sql语句、数据量大的时候加索引、redis、读写分离

数据库拆分——分库、分表

集群

*4.sql优化(20221118)

一、避免不走索引的场景

  1. 减少数据访问: 设置合理的字段类型,启用压缩,通过索引访问等减少磁盘IO

  2. 返回更少的数据: 只返回需要的字段和数据分页处理 减少磁盘io及网络io

  3. 减少交互次数: 批量DML操作,函数存储等减少数据连接次数

  4. 减少服务器CPU开销: 尽量减少数据库排序操作以及全表查询,减少cpu 内存占用

  5. 利用更多资源: 使用表分区,可以增加并行操作,更大限度利用cpu资源

总结到SQL优化中,就三点:

  • 最大化利用索引;

  • 尽可能避免全表扫描;

  • 减少无效数据的查询;

1. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:

SELECT * FROM t WHERE username LIKE '%陈%'

优化方式:尽量在字段后面使用模糊查询。如下:

SELECT * FROM t WHERE username LIKE '陈%'

2. 尽量避免使用in 和not in,会导致引擎走全表扫描。如下:

SELECT * FROM t WHERE id IN (2,3)

优化方式:如果是连续数值,可以用between代替。如下:

优化方式:如果是连续数值,可以用between代替。如下:
SELECT * FROM t WHERE id between 2 and 4

如果是子查询,可以用exists代替。

-- 不走索引
select * from A where A.id in (select id from B);
-- 走索引
select * from A where exists (select * from B where B.id = A.id);

3. 尽量避免使用 or, 导致数据库引擎放弃索引进行全表扫描。如下:

SELECT * FROM t WHERE id = 1 OR id = 3

优化方式:可以用union代替or。如下

SELECT * FROM t WHERE id = 1
   UNION
SELECT * FROM t WHERE id = 3

4. 尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。如下:

SELECT * FROM t WHERE score IS NULL

优化方式:可以给字段添加默认值0,对0值进行判断。如下:

SELECT * FROM t WHERE score = 0

5.尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。

可以将表达式、函数操作移动到等号右侧。如下:

-- 全表扫描
SELECT * FROM T WHERE score/10 = 9
-- 走索引
SELECT * FROM T WHERE score = 10*9

6. 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。如下:

SELECT username, age, sex FROM T WHERE 1=1

优化方式:用代码拼装sql时进行判断,没 where 条件就去掉 where,有where条件就加 and。

7. 查询条件不能用 <> 或者 !=

使用索引列作为条件进行查询时,需要避免使用<>或者!=等判断条件。如确实业务需要,使用到不等于符号,需要在重新评估索引建立,避免在此字段上建立索引,改由查询条件中其他索引字段代替。

8. where条件仅包含复合索引非前置列

如下:复合(联合)索引包含key_part1,key_part2,key_part3三列,但SQL语句没有包含索引前置列"key_part1",按照MySQL联合索引的最左匹配原则,不会走联合索引。

select col1 from table where key_part2=1 and key_part3=2

9. 隐式类型转换造成不使用索引

如下SQL语句由于索引对列类型为varchar,但给定的值为数值,涉及隐式类型转换,造成不能正确走索引。

select col1 from table where col_varchar=123; 

10. order by 条件要与where中条件一致,否则order by不会利用索引进行排序

-- 不走age索引
SELECT * FROM t order by age;

-- 走age索引
SELECT * FROM t where age > 0 order by age;

对于上面的语句,数据库的处理顺序是:

  • 第一步:根据where条件和统计信息生成执行计划,得到数据。

  • 第二步:将得到的数据排序。当执行处理数据(order by)时,数据库会先查看第一步的执行计划,看order by 的字段是否在执行计划中利用了索引。如果是,则可以利用索引顺序而直接取得已经排好序的数据。如果不是,则重新进行排序操作。

  • 第三步:返回排序后的数据。

当order by 中的字段出现在where条件中时,才会利用索引而不再二次排序,更准确的说,order by 中的字段在执行计划中利用了索引时,不用排序操作。

这个结论不仅对order by有效,对其他需要排序的操作也有效。比如group by 、union 、distinct等。

11. 正确使用hint优化语句

MySQL中可以使用hint指定优化器在执行时选择或忽略特定的索引。一般而言,处于版本变更带来的表结构索引变化,更建议避免使用hint,而是通过Analyze table多收集统计信息。但在特定场合下,指定hint可以排除其他索引干扰而指定更优的执行计划。

  1. USE INDEX 在你查询语句中表名的后面,添加 USE INDEX 来提供希望 MySQL 去参考的索引列表,就可以让 MySQL 不再考虑其他可用的索引。例子: SELECT col1 FROM table USE INDEX (mod_time, name)...

  2. IGNORE INDEX 如果只是单纯的想让 MySQL 忽略一个或者多个索引,可以使用 IGNORE INDEX 作为 Hint。例子: SELECT col1 FROM table IGNORE INDEX (priority) ...

  3. FORCE INDEX 为强制 MySQL 使用一个特定的索引,可在查询中使用FORCE INDEX 作为Hint。例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...

在查询的时候,数据库系统会自动分析查询语句,并选择一个最合适的索引。但是很多时候,数据库系统的查询优化器并不一定总是能使用最优索引。如果我们知道如何选择索引,可以使用FORCE INDEX强制查询使用指定的索引。

例如:

SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;

二、SELECT语句其他优化

1. 避免出现select *

首先,select * 操作在任何类型数据库中都不是一个好的SQL编写习惯。

使用select * 取出全部列,会让优化器无法完成索引覆盖扫描这类优化,会影响优化器对执行计划的选择,也会增加网络带宽消耗,更会带来额外的I/O,内存和CPU消耗。

建议提出业务实际需要的列数,将指定列名以取代select *。

2. 避免出现不确定结果的函数

特定针对主从复制这类业务场景。由于原理上从库复制的是主库执行的语句,使用如now()、rand()、sysdate()、current_user()等不确定结果的函数很容易导致主库与从库相应的数据不一致。另外不确定值的函数,产生的SQL语句无法利用query cache。

3.多表关联查询时,小表在前,大表在后。

在MySQL中,执行 from 后的表关联查询是从左往右执行的(Oracle相反),第一张表会涉及到全表扫描,所以将小表放在前面,先扫小表,扫描快效率较高,在扫描后面的大表,或许只扫描大表的前100行就符合返回条件并return了。

例如:表1有50条数据,表2有30亿条数据;如果全表扫描表2,你品,那就先去吃个饭再说吧是吧。

4. 使用表的别名

当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些友列名歧义引起的语法错误。

5. 用where字句替换HAVING字句

避免使用HAVING字句,因为HAVING只会在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。

where和having的区别:where后面不能使用组函数

6.调整Where字句中的连接顺序

MySQL采用从左往右,自上而下的顺序解析where子句。根据这个原理,应将过滤数据多的条件往前放,最快速度缩小结果集。

三、增删改 DML 语句优化

1. 大批量插入数据

如果同时执行大量的插入,建议使用多个值的INSERT语句(方法二)。这比使用分开INSERT语句快(方法一),一般情况下批量插入效率有几倍的差别。

方法一:

insert into T values(1,2); 
insert into T values(1,3); 
insert into T values(1,4);

方法二:

Insert into T values(1,2),(1,3),(1,4); 

选择后一种方法的原因有三。

  • 减少SQL语句解析的操作,MySQL没有类似Oracle的share pool,采用方法二,只需要解析一次就能进行数据的插入操作;

  • 在特定场景可以减少对DB连接次数

  • SQL语句较短,可以减少网络传输的IO。

2. 适当使用commit

适当使用commit可以释放事务占用的资源而减少消耗,commit后能释放的资源如下:

  • 事务占用的undo数据块;

  • 事务在redo log中记录的数据块;

  • 释放事务施加的,减少锁争用影响性能。特别是在需要使用delete删除大量数据的时候,必须分解删除量并定期commit。

3. 避免重复查询更新的数据

针对业务中经常出现的更新行同时又希望获得改行信息的需求,MySQL并不支持PostgreSQL那样的UPDATE RETURNING语法,在MySQL中可以通过变量实现。

例如,更新一行记录的时间戳,同时希望查询当前记录中存放的时间戳是什么,简单方法实现:

Update t1 set time=now() where col1=1; 
Select time from t1 where id =1; 

使用变量,可以重写为以下方式:

Update t1 set time=now () where col1=1 and @now: = now (); 
Select @now; 

前后二者都需要两次网络来回,但使用变量避免了再次访问数据表,特别是当t1表数据量较大时,后者比前者快很多。

4.查询优先还是更新(insert、update、delete)优先

MySQL 还允许改变语句调度的优先级,它可以使来自多个客户端的查询更好地协作,这样单个客户端就不会由于锁定而等待很长时间。改变优先级还可以确保特定类型的查询被处理得更快。我们首先应该确定应用的类型,判断应用是以查询为主还是以更新为主的,是确保查询效率还是确保更新的效率,决定是查询优先还是更新优先。下面我们提到的改变调度策略的方法主要是针对只存在表锁的存储引擎,比如 MyISAM 、MEMROY、MERGE,对于Innodb 存储引擎,语句的执行是由获得行锁的顺序决定的。MySQL 的默认的调度策略可用总结如下:

1)写入操作优先于读取操作。

2)对某张数据表的写入操作某一时刻只能发生一次,写入请求按照它们到达的次序来处理。

3)对某张数据表的多个读取操作可以同时地进行。MySQL 提供了几个语句调节符,允许你修改它的调度策略:

  • LOW_PRIORITY关键字应用于DELETE、INSERT、LOAD DATA、REPLACE和UPDATE;

  • HIGH_PRIORITY关键字应用于SELECT和INSERT语句;

  • DELAYED关键字应用于INSERT和REPLACE语句。

如果写入操作是一个 LOW_PRIORITY(低优先级)请求,那么系统就不会认为它的优先级高于读取操作。在这种情况下,如果写入者在等待的时候,第二个读取者到达了,那么就允许第二个读取者插到写入者之前。只有在没有其它的读取者的时候,才允许写入者开始操作。这种调度修改可能存在 LOW_PRIORITY写入操作永远被阻塞的情况。

SELECT 查询的HIGH_PRIORITY(高优先级)关键字也类似。它允许SELECT 插入正在等待的写入操作之前,即使在正常情况下写入操作的优先级更高。另外一种影响是,高优先级的 SELECT 在正常的 SELECT 语句之前执行,因为这些语句会被写入操作阻塞。如果希望所有支持LOW_PRIORITY 选项的语句都默认地按照低优先级来处理,那么 请使用--low-priority-updates 选项来启动服务器。通过使用 INSERTHIGH_PRIORITY 来把 INSERT 语句提高到正常的写入优先级,可以消除该选项对单个INSERT语句的影响。

四、查询条件优化

1. 对于复杂的查询,可以使用中间临时表 暂存数据;

2. 优化group by语句

默认情况下,MySQL 会对GROUP BY分组的所有值进行排序,如 “GROUP BY col1,col2,....;” 查询的方法如同在查询中指定 “ORDER BY col1,col2,...;” 如果显式包括一个包含相同的列的 ORDER BY子句,MySQL 可以毫不减速地对它进行优化,尽管仍然进行排序。

因此,如果查询包括 GROUP BY 但你并不想对分组的值进行排序,你可以指定 ORDER BY NULL禁止排序。例如:

SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;

3. 优化join语句

MySQL中可以通过子查询来使用 SELECT 语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的 SQL 操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)..替代。

例子:假设要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )

如果使用连接(JOIN).. 来完成这个查询工作,速度将会有所提升。尤其是当 salesinfo表中对 CustomerID 建有索引的话,性能将会更好,查询如下:

SELECT col1 FROM customerinfo 
LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID 
WHERE salesinfo.CustomerID IS NULL 

连接(JOIN).. 之所以更有效率一些,是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

4. 优化union查询

MySQL通过创建并填充临时表的方式来执行union查询。除非确实要消除重复的行,否则建议使用union all。原因在于如果没有all这个关键词,MySQL会给临时表加上distinct选项,这会导致对整个临时表的数据做唯一性校验,这样做的消耗相当高。

高效:

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 
UNION ALL 
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST'; 

低效:

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 
UNION 
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';

5.拆分复杂SQL为多个小SQL,避免大事务

  • 简单的SQL容易使用到MySQL的QUERY CACHE;

  • 减少锁表时间特别是使用MyISAM存储引擎的表;

  • 可以使用多核CPU。

6. 使用truncate代替delete

当删除全表中记录时,使用delete语句的操作会被记录到undo块中,删除记录也记录binlog,当确认需要删除全表时,会产生很大量的binlog并占用大量的undo数据块,此时既没有很好的效率也占用了大量的资源。

使用truncate替代,不会记录可恢复的信息,数据不能被恢复。也因此使用truncate操作有其极少的资源占用与极快的时间。另外,使用truncate可以回收表的水位,使自增字段值归零。

7. 使用合理的分页方式以提高分页效率

使用合理的分页方式以提高分页效率 针对展现等分页需求,合适的分页方式能够提高分页的效率。

案例1:

select * from t where thread_id = 10000 and deleted = 0 
order by gmt_create asc limit 0, 15;

上述例子通过一次性根据过滤条件取出所有字段进行排序返回。数据访问开销=索引IO+索引全部记录结果对应的表数据IO。因此,该种写法越翻到后面执行效率越差,时间越长,尤其表数据量很大的时候。

适用场景:当中间结果集很小(10000行以下)或者查询条件复杂(指涉及多个不同查询字段或者多表连接)时适用。

案例2:

select t.* from (select id from t where thread_id = 10000 and deleted = 0
order by gmt_create asc limit 0, 15) a, t 
where a.id = t.id;

上述例子必须满足t表主键是id列,且有覆盖索引secondary key:(thread_id, deleted, gmt_create)。通过先根据过滤条件利用覆盖索引取出主键id进行排序,再进行join操作取出其他字段。数据访问开销=索引IO+索引分页后结果(例子中是15行)对应的表数据IO。因此,该写法每次翻页消耗的资源和时间都基本相同,就像翻第一页一样。

适用场景:当查询和排序字段(即where子句和order by子句涉及的字段)有对应覆盖索引时,且中间结果集很大的情况时适用。

五、建表优化

\1. 在表中建立索引,优先考虑where、order by使用到的字段。

\2. 尽量使用数字型字段(如性别,男:1 女:2),若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。 这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

\3. 查询数据量大的表 会造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段分页进行查询,循环遍历,将结果合并处理进行展示。要查询100000到100050的数据,如下:

SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* 
FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050

\4. 用varchar/nvarchar 代替 char/nchar

尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

5.数据库的存储过程

6.SELECT语句 - 语法顺序:

1. SELECT 
2. DISTINCT <select_list>
3. FROM <left_table>
4. <join_type> JOIN <right_table>
5. ON <join_condition>
6. WHERE <where_condition>
7. GROUP BY <group_by_list>
8. HAVING <having_condition>
9. ORDER BY <order_by_condition>
10.LIMIT <limit_number>

7.SELECT语句 - 执行顺序:

FROM <表名> # 选取表,将多个表数据通过笛卡尔积变成一个表。 ON <筛选条件> # 对笛卡尔积的虚表进行筛选 JOIN<join, left join, right join...> <join表> # 指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中 WHERE <where条件> # 对上述虚表进行筛选 GROUP BY <分组条件> # 分组 <SUM()等聚合函数> # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的 HAVING <分组筛选> # 对分组后的结果进行聚合筛选 SELECT <返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外 DISTINCT # 数据除重 ORDER BY <排序条件> # 排序 LIMIT <行数限制>

8.如何发现和处理mysql的慢查询

1.通过日志我们可以获取到慢sql信息,就可以针对性的优化,Mysql提供了Explain关键字帮助我们分析sql语句的执行方式

2.

9.MyISAM与InnoDB 的区别

1. InnoDB支持事务,MyISAM不支持

2. InnoDB支持外键,而MyISAM不支持

3.Innodb不支持全文索引,而MyISAM支持全文索引

4.InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁

5.InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有

6.InnoDB是是聚集索引,MyISAM是非聚集索引

7.InnoDB不保存表的具体行数,而MyISAM用一个变量保存了整个表的行数

8.InnoDB适合于增删改,而MyISAM更适合于查询

10.union和union all的区别

Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;自动压缩多个结果集合中的重复结果

Union All:对两个结果集进行并集操作,包括重复行,不进行排序;将所有的结果全部显示出来,不管是不是重复。

(二)redis

1.项目中redis的使用

短信验证 token信息 首页不常变(分类数据、轮播图)

2.redis持久化的方式

RDB(默认开启):指定时间内,将内存中的数据集快照写入磁盘

AOF(需要手动开启):每一个收到的写的命令通过write函数追加到文件中,默认的文件名是appendonly.aof

3..穿透 击穿 雪崩

穿透: 首先radis中查询数据,如果查不到就从mysql中查询,这个时候会造成mysql瘫痪 解决方案: 第一次查询redis中没有数据,查询的时候给一个数据 击穿: 过期时间是击穿的前提条件,redis中的一个key过期了,访问到mysql,这个时候也会造成mysql瘫痪 解决方案:假设我设置的过期时间为1小时,在还剩下半个小时的时候,分两步走 第一个线程正常给用户返回数据 第二个开辟一条新的线程对这个数据重新缓存,缓存有效期1小时 雪崩: redis中的多个key过期了,访问到mysql,造成mysql瘫痪 解决方案和击穿一致

4.redis的基本数据类型和应用场景

string 是 redis 最基本的类型

使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。

hash 是一个键值(key => value)对集合。

使用场景:存储、读取、修改用户属性

list 列表是简单的字符串列表,

应用场景:1、最新消息排行等功能(比如朋友圈的时间线)

2、消息队列

set是string类型的无序集合。集合是通过hashtable实现的,

应用场景:1、共同好友

2、利用唯一性,统计访问网站的所有独立ip

3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐

zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

应用场景:1、排行榜

2、带权重的消息队列

5.redis的集群

服务器有3.5.7.9台,当一半以上的服务器没有得到回应,则认为这台服务器挂了,如果从机挂了,无所谓,如果主机挂了,从机顶上

四、linux

1.linux常用指令

tar -zcvf /home/abc.tar.gz /home/abc 打包,并用gzip压缩/解压-zxvf

ps -aux | grep 端口

lsof -i:端口

sudo 文件(提升权限)

yum 安装软件

2.docker run 和 docker start有什么区别

docker run:创建并运行容器 (docker create + docker start)

docker start:运行容器

3.容器和虚拟机的区别

docker容器无需关注系统相关部分

容器相比于虚拟机没有对硬件的集成操作

五、框架部分

(一)mybatis

1.mybatis-plus的优点(保存自动获取id,insql可直接传输字符串,分页插件的强大,代码生成器)

依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring 。 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 。 预防Sql注入:内置 Sql 注入剥离器,有效预防Sql注入攻击 。 通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD鉴于半自动与全自动之间 操作,更有强大的条件构造器,满足各类使用需求 。 多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题 。 支持热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动 支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作 支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码(生成自定义文件,避免开发重复代码),支持模板引擎、有超多自定义配置等。 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。 支持关键词自动转义:支持数据库关键词(order、key…)自动转义,还可自定义关键词 。 内置分页插件:基于 Mybatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询。 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询 。 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作。 默认将实体类的类名查找数据库中的表,使用@TableName(value=“table1”)注解指定表名,@TableId指定表主键,若字段与表中字段名保持一致可不加注解。

(二)spring

1.对IOC的理解

任何能通过DI注入的容器都是IOC容器

spring是一个ioc容器,ioc容器实际上就是个map集合,里面存的是各种对象,在项目启动的时候会读取配置文件里面的bean节点,以及添加了(controller、service、component)注解的类还是通过反射new对象放到map里。这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入

2.springMVC执行流程

\1. 用户发送请求至前端控制器DispatcherServlet

\2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。

\3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)

一并返回给DispatcherServlet。

\4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

\5. 执行处理器(Controller,也叫后端控制器)。

\6. Controller执行完成返回ModelAndView

\7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

\8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器

\9. ViewReslover解析后返回具体View

\10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

\11. DispatcherServlet响应用户

3.对AOP的理解

AOP面向切面编程:AOP编程操作的主要对象是切面

以一个功能为一个主体

面向拓展开放,面向修改关闭

作用:在不改变原有代码的情况下进行功能的增

AOP是基于代理实现的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用JDK Proxy去进行代理了,这时候Spring AOP会使用Cglib,生成一个被代理对象的子类,来作为代理

代理分态代理,则是在运行期,生成一个代理对象。

4.AOP的名词解释

1、切面(Aspect):**被抽取的公共模块,可能会横切多个对象。 在Spring AOP中,切面可以使用通用

类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。

2、连接点(Join point):**指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。

3、通知(Advice):**在切面的某个特定的连接点(Join point)上执行的动作。通知有各种类型,其中

包括“around”、“before”和“after”等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并

维护一个以连接点为中心的拦截器链。

4、切入点(Pointcut):**切入点是指 我们要对哪些Join point进行拦截的定义。通过切入点表达式,指

定拦截的方法,比如指定拦截add、search

5、引入(Introduction):**(也被称为内部类型声明(inter-type declaration))。声明额外的方法

或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,

你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

6、目标对象(Target Object):** 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把

它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被

代理(proxied) 对象。

7、织入(Weaving):**指把增强应用到目标对象来创建新的代理对象的过程。Spring是在运行时完成

织入。

5.

6.spring的启动方式

当这样的位置路径没有前缀时,从该路径构建并用于加载 Bean 定义的特定类型取决于特定应用程序上下文,并且该类型适用于特定的应用程序上下文。

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

Bean 定义是从文件系统位置加载

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");

在位置路径上使用特殊前缀或标准 URL 前缀会覆盖为加载 Bean 定义而创建的默认类型。

ApplicationContext ctx = 
    new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

与在实例化 时将 Spring XML 文件用作输入的方式大致相同,在实例化 时可以使用类作为输入。这允许完全无 XML 地使用 Spring 容器

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

7.MVC是什么

MVC是一种设计模式

model是(数据+过程),主要负责数据的存储和交互

View 表示视图,负责界面的布局和显示.(视图)

Controller 是控制器,负责模型和界面之间的交互.(处理数据和视图之间的交互)

8.spring自动装配的方式

1.@Autowirde注解/bytpe类型

2.构造注入/bytpe类型

3.@resource注解/byname类型

注入方式:set注入

策略:bytype、byname

@Autowirde是spring提供的

@resource是jdk提供的

9.依赖注入解决了什么问题

依赖注入也就是DI注入是一种设计模式,也是Spring框架的核心概念之一。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。

10.SpringMVC如何处理静态资源

Spring MVC在处理映射的静态资源时,会查看引用路径是否包含 WEB-INF 或 META-INF,如果存在则返回 null。 可以通过将 /WEB-INF/ 设置在 location 属性中,此时可以通过访问到 web.xml : 在实际开发中,发布新版本,即使服务端的 javascript、css等静态资源以发生变化,但由于客户端浏览器本身缓存管理机制问题,客户端并不会从服务端下载新的静态资源。 一个好的解决方案是 : 网页中引用静态资源的路径添加应用的发布版本号,这样发布新的部署版本时,由于版本号的变更造成网页中竟资源台路径发生更改,从而是这些静态资源成为“新资源”,因此客户端会下载这个“新资源”,而不会使用缓存中的数据

11.SpringBean的什么周期

前置增强操作、创建对象、di注入、操作、销毁

12.spring常用注解

*@Component* 组件,没有明确的角色

@Service 在业务逻辑层使用(service层)

*@Repository* 在数据访问层使用(dao层)

@Controller 在展现层使用,控制器的声明(C)

@Autowired:由Spring提供

@Resource:由JSR-250提供

@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)

@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)

@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)

@Aspect 声明一个切面(类上)

*@After* 在方法执行之后执行(方法上) *@Before* 在方法执行之前执行(方法上) *@Around* 在方法执行之前与之后执行(方法上)

@Value 为属性注入值(属性上)

@Scheduled 来申明这是一个任务springtask

*@RequestMapping* 用于映射Web请求,包括访问路径和参数(类或方法上)

@ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)

@RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)

@PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。

@RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。

(三)SpringBoot

1.springboot特有注解

启动注解 @SpringBootApplication

@SpringBootConfiguration 注解,继承@Configuration注解,主要用于加载配置文件 @ComponentScan 注解,主要用于组件扫描和自动装配 @EnableAutoConfiguration 注解,开启自动配置功能

2.SpringBoot自动配置原理

所有 Spring Boot 项目的主启动程序类上都使用了一个 @SpringBootApplication 注解,该注解是 Spring Boot 中最重要的注解之一 ,也是 Spring Boot 实现自动化配置的关键。@SpringBootApplication 是一个组合元注解,其主要包含三个注解:@SpringBootConfiguration 和 @EnableAutoConfiguration和@componentScan,其中 @EnableAutoConfiguration 注解是 SpringBoot 自动化配置的核心所在。@EnableAutoConfiguration 注解用于开启 Spring Boot 的自动配置功能, 它使用 Spring 框架提供的 @Import 注解通过 AutoConfigurationImportSelector类(选择器)给容器中导入自动配置组件。AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,AutoConfigurationImportSelector 中还包含一个静态内部类 AutoConfigurationGroup,它实现了 DeferredImportSelector 接口的内部接口 Group,AutoConfigurationImportSelector 内各方法执行顺序如下。 1.getImportGroup() 方法 AutoConfigurationImportSelector 类中 getImportGroup() 方法主要用于获取实现了 DeferredImportSelector.Group 接口的类 2.process() 方法 静态内部类 AutoConfigurationGroup 中的核心方法是 process(),该方法通过调用 getAutoConfigurationEntry() 方法读取 spring.factories 文件中的内容,获得自动配置类的集合,getAutoConfigurationEntry() 方法通过调用 getCandidateConfigurations() 方法来获取自动配置类的完全限定名,并在经过排除、过滤等处理后,将其缓存到成员变量中,在 getCandidateConfigurations() 方法中,根据 Spring Factories 机制调用 SpringFactoriesLoader 的 loadFactoryNames() 方法,根据 EnableAutoConfiguration.class (自动配置接口)获取其实现类(自动配置类)的类名的集合, 3.selectImports() 方法 以上所有方法执行完成后,AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports() 会将 process() 方法处理后得到的自动配置类,进行过滤、排除,最后将所有自动配置类添加到容器中。

3.对SpringBoot的理解

Spring Boot是一个很特别的存在,说它特别是因为它确实简化了基于Spring技术栈的应用/微服务开发过程,使得我们能够很快速地就搭建起一个应用的脚手架并在其上进行项目的开发,再也不用像以前那样使用大量的XML或是注解了,应用在这样的约定优于配置的前提下可以以最快的速度创建出来。Spring Boot从这一点来说极大解放了开发者,使得开发者从之前繁琐的配置中解脱出来,转而专注于应用的业务逻辑开发。,相比于之前的Spring MVC等传统的Web开发模式来说简化了太多太多。

4.SpringBoot的特点

自动配置、添加SpringMVC的注解、内置tomcat

(四)SpringCloud

分布式微服务

1.什么是分布式

所谓分布式,就是将一个服务拆分为多个服务,并且分散部署到不同服务器上

分布式是为了解决高并发的问题,提供可扩展性以及高可用性,

至于拆分方式,可以根据功能拆分,也可以根据业务拆分

2.什么是微服务

微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务根据业务进行拆分,主要是为了分散能力

Java微服务的框架有dubbo·,spring cloud。dubbo服务务只是微服务基础设施的一个子集。现在

微服务主流的框架是spring boot,spring cloud和容器docker

3.分布式和微服务的区别

架构不同:微服务的设计是为了不因为某个模块的升级和BUG影响现有的系统业务。微服务与分布式的细微差别是,微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。

作用不同:分布式:不同模块部署在不同服务器上,分布式主要解决的是网站高并发带来问题。微服务:各服务可独立应用,组合服务也可系统应用。

粒度不同:微服务相比分布式服务来说,它的粒度更小,服务之间耦合度更低,由于每个微服务都由独立的小团队负责,因此它敏捷性更高,分布式服务最后都会向微服务架构演化,这是一种趋势, 不过服务微服务化后带来的挑战也是显而易见的,例如服务粒度小,数量大,后期运维将会很难。

4.单体架构和微服务的优缺点

  • 上手难度

    • 微服务架构:API接口调用

    • 单体架构:数据库共享或本地程序调用

    • 结论:单体架构胜

  • 开发效率

    • 微服务架构:早期设计和沟通的工作量加大,随着项目规模和时间的推移,效率变化不大

    • 单体架构:早期工作量小,随着项目规模和时间的推移,效率大幅度下降

    • 结论:对于简单项目单体架构胜,对于复杂项目微服务架构胜

  • 系统设计(高内聚,低耦合)

    • 微服务架构:

      • 每个业务单独包装成一个微服务,数据和代码都从物理上隔离开来,实现高内聚低耦合相对容易

    • 单体架构:

      • 以包的形式对代码进行模块划分,控制得当即可实现高内聚

      • 最终都是在数据层面将整个系统耦合在一起

    • 结论:微服务架构胜

  • 系统设计(扩展性)

    • 微服务架构:独立开发新模块,通过API与现有模块交互

    • 单体架构:在现有系统上修改,与现存业务逻辑高度耦合

    • 结论:微服务架构胜

  • 需求变更响应速度

    • 微服务架构:各个微服务组件独立变更,容易实施敏捷开发方法

    • 单体架构:需要了解整个系统才可以正确修改,容易导致不相关模块的意外失败

    • 结论:微服务架构胜

  • 系统升级效率

    • 微服务架构:各个微服务组件独立升级,上手和开发效率高,影响面小

    • 单体架构:需要了解整个系统才可以正确修改,容易导致不相关模块的意外失败

    • 结论:微服务架构胜

  • 运维效率

    • 微服务架构:

      • 大系统被拆分为多个小系统,部署和运维难度加大,但可以利用Devops等方式将运维工作自动化

    • 单体架构:简单直接

    • 结论:单体架构胜

  • 代码复用性

    • 微服务架构:微服务组件可以在新项目中直接复用,包括前端页面

    • 单体架构:一般以共享库的形式复用后台代码

    • 结论:微服务架构胜

  • 硬件需求

    • 微服务架构:按需为不同业务模块伸缩资源节点,一个系统需部署多个微服务,需要启动多个运行容器

    • 单体架构:整个系统只需要一个运行容器,为整个系统分配资源

    • 结论:对于简单项目单体架构胜,对于复杂项目微服务架构胜

  • 项目成本

    • 微服务架构:项目早期和后期,成本变化曲线平缓

    • 单体架构:项目早期成本低,后期成本大

    • 结论:对于简单项目单体架构胜,对于复杂项目微服务架构胜

  • 非功能需求

    • 微服务架构:为单独的微服务按需调优,甚至更换实现方式和程序语言

    • 单体架构:为整个系统调优,牵―发而动全身

    • 结论:微服务架构胜

5.什么是SOA

ESB消息总线,用于调度、路由、管理所有的服务

SOA 的概念,意为面向服务的架构,其核心特征就是以松耦合、粗粒度的服务单元来构建软件,基于ESB构建的这个架构就是SOA分布式架构

6.SOA优缺点

SOA的优点:

  1. 简单化系统的开发: 由于soa具有组合性,可以利用现有的SOA资源,根据同样的开放标准,在不受平台限制的基础上,可以直接利用现有的资源进行组合,让后在按照自己的客户需求,进行进一步的开放。

  2. 面向企业商业流程 SOA是基于服务的构造,所以开放的出发点,就是如何解决企业流程中出现的问题。

  3. 更好的适应性和扩展性 由于soa的组件性,和优良的扩展性以及其组件性等待特征,SOA可以根据不同的需求,进行重新的组合和构造。

  4. 互用性

  5. 对系统的升级,分布,和维护有个更多的优化

  6. 简化了提供,寻找和使用服务的过程

  7. 通过共同资源的利用,减少了开支

SOA的缺点:

  1. 减低了系统的性能

  2. 在向标准化过度的转换过程,增加了简介费用

  3. 很多没有太多意义的文件型信息

  4. 对商业流程的计划要求甚高

7.微服务和SOA的区别

SOA中心化:数据的流通都必须经过中心节点

微服务去中心化:有节点了自由选择中心、自由决定中心

微服务的注册中心和SOA的ESB区别

  • ESB:垄断,类似于中介

  • 注册中心:只负责记录,跟返回地址,类似于114

SpringCloud基础面试题

1.springcloud作用

springcloud为微服务开发提供的一站式解决方案

也就是说springcloud要解决的问题是微服务架构实施中存在的一些问题,比如注册发现问题、网络问题、统一认证安全授权问题、负载均衡问题、链路追踪问题

2.SpringBoot和SpringCloud的关系

SpringBoot、SpringCloud、SpringCloudAlibaba之间的关系?

  • Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务

  • spring cloud可以快速的实现微服务组件开发

  • Spring Cloud是微服务的一站式解决方案

  • Spring Cloud很大的一部分是基于Spring Boot来实现,必须基于Spring Boot开发

总结:可以单独使用Spring Boot开发项目,但是Spring Cloud离不开 Spring Boot

3.SpringCloud和SpringCloudAlibaba的关系

总结:SpringCloud部分组件闭源,不再更新,阿里基于SpringCloud整合其中组件

4.SpringCloud和Double的区别

5.CAP

CAP定理:三者不可同时获得,P一定要满足

  • 一致性(C):

    • 在分布式系统中的所有数据备份,在同一时刻是否同样的值

      • 所有节点在同一时间的数据完全一致,越多节点,数据同步越耗时

  • 可用性(A):

    • 负载过大后,集群整体是否还能响应客户端的读写请求

      • 服务一直可用,而且是正常响应时间

  • 分区容错性(P):

    • 分区容错性,就是高可用性一个节点崩了,并不影响其它的节点

      • 100个节点,挂了几个,不影响服务,越多机器越好

分布式系统中P,肯定要满足,所以我们只能在一致性和可用性之间进行权衡

如果要求一致性,则选择zookeeper,如金融行业

如果要求可用性,则Eureka,如教育、电商系统

SpringCloud组件相关面试题

1.Eureka

服务注册中心,解决了服务的发现和注册

2.什么是自我保护

如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制

3.心跳

应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

4.Ribbon

负载均衡,解决了调用服务器的选择问题

5.openfeign

管理模块与模块之间的调用关系以及负载均衡

6.ribbon的负载均衡策略

负载均衡的策略有7中,默认的策略是区域权重

其余还有:轮询、区域权重、加权轮询、重试、随机、最少连接数、响应时间

7.ribbon和openfeign的区别

openfeign内置了ribbon

openfeign是注解加接口,使得开发像在调用本地方法一样,openfeign封装了http的调用

ribbon需要手动调用http请求

8.feign和openfeign的区别

Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用接口,就可以调用服务注册中心的服务。

openFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中.

9.服务降级是怎么产生的

服务降级是在服务器压力剧增的情况下,根据当前的业务情况及流量对一些服务和页面有策略地进行降级,以释放服务器资源并保证核心任务的正常运行.

10.hystrix

熔断器,解决了服务器雪崩问题

在10秒内访问超过20个,异常超过50%是进行熔断,用户每次的访问都会执行一个降级的方法

11.熔断的工作流程

开启:用户访问时直接访问降级的方法

关闭:用户可以正常访问

半开:每五秒会放进来一个请求,如果没有出现异常,熔断关闭

12.雪崩是怎么形成的

最下游服务响应时间过长,大量请求阻塞,大量线程不会释放,导致服务器资源耗尽,最终导致上游服务甚至整个系统瘫痪

13.雪崩形成原因及解决方案

提供方出现异常 硬件故障、程序bug、缓存击穿、用户大量请求

调用方出现异常 同步等待造成的资源耗尽

重试加大请求流量 用户重试、逻辑代码重试

解决方案:

服务熔断 切断对下游服务的调用

服务降级 整体资源不够用时,先将一些不重要的服务停掉

服务限流

14.getaway

网关:反向代理,鉴权,熔断,流控、路径重写、日志监控

gateway核心:路由、断言、锅炉器

15.断言

一组规则,返回值true和false

16.getaway的工作流程

客户端向springcloud getaway发出请求,handlermapping根据url到webhandler确定请求与路由匹配,则会将其发送到webhandler,处理程序会将所有实例和特定于路由的实例添加到过滤器,过滤器可以在发送代理请求之前和之后运行逻辑

15.config

统一配置管理

server端:提供配置文件的存储,以接口的形式将配置文件的内容提供出去

client端:通过接口获取配置数据并出示化自己的应用

16.bus

自动更新配置信息

17.nacos

nacos替代了springcloud中的eureka、config、bus组件

功能特性:服务的发现与健康检查

动态配置管理

动态DNS服务

18.sentinel是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳

19.nacos的自我保护机制

保护阈值:可以设置为0-1之间的浮点数,它其实是⼀个⽐例值(当前服务健康实例数/当前服务总实例 数)

⼀般流程下, nacos是服务注册中⼼,服务消费者要从nacos获取某⼀个服务的可⽤实例信息,对于服 务实例有健康/不健康状态之分, nacos在返回给消费者实例信息的时候,会返回健康实例。这个时候在 ⼀些⾼并发、⼤流量场景下会存在⼀定的问题

如果服务A有100个实例, 98个实例都不健康了,只有2个实例是健康的,如果nacos只返回这两个健康 实例的信息的话,那么后续消费者的请求将全部被分配到这两个实例,流量洪峰到来, 2个健康的实例 也扛不住了,整个服务A 就扛不住,上游的微服务也会导致崩溃,,,产⽣雪崩效应

保护阈值的意义在于 当服务A健康实例数/总实例数 < 保护阈值 的时候,说明健康实例真的不多了,这个时候保护阈值会被触 发(状态true)

nacos将会把该服务所有的实例信息(健康的+不健康的)全部提供给消费者,消费者可能访问到不健康 的实例,请求失败,但这样也⽐造成雪崩要好,牺牲了⼀些请求,保证了整个系统的⼀个可用

20.SpringCloud的核心组件

  • Eureka

  • Ribbon

  • Hystrix

  • Zuul

  • Spring Cloud Config

事务

1.什么是事务

访问并更新数据库中各种数据项的一个程序执行单元 通俗一点说就是,一手交钱一手交货,要么钱货两清,要么终止交易

2.什么是本地事务

通常我们使用关系型数据库来控制事务(是数据库本身的能力),数据库事务,应用程序主要靠关系型数据库来控制事务,通常单体架构中,数据库跟应用程序是在一起的(同一个服务器),所以基于关系型数据库的事务,又被称为本地事务

3.spring如何接管本地事务

使用:1.配置文件 2.注解 核心是阻止了原来返回的connection,返回了自己的connection

4.事务的ACID

原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。(要么同时成功要么同时失败) 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。(总和不变) 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。(A给B转钱,B给C转钱互不影响) 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。(事务一旦提交就不能更改了)

5.CAP

C:一致性:从数据库必须等待主数据库数据同步完成,才能给客户响应(过程阻塞) A:可用性:我现在可以先返回就的数据给用户(从故武器没有获取主服务器最新的数据) P:分区容错性:高可用,分布式系统中的任何一个节点挂了,都不影响其他节点 典型应用场景 CP:银行取钱 AP:准备发货中,支付中

6.BASE理论

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使西永达到最终一致性

BASE理论是Basically Available,基本可用,Soft State,软状态,状态可以有一段时间不同步,Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致。

7.什么是分布式事务

百度百科 - 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上 通俗来说:需要远程协助才能操作的事务,称之为分布式事务

8.分布式事务常见解决方案

2pc/3pc tcc at mq sage

二阶段提交(英文缩写:2pc) 在分布式系统中,每个节点虽然可以知晓自己的操作成功或失败,但不知晓其他节点的成功或失败 第一阶段请求阶段 1 协调者(Coordinator,即事务管理器)会向事务的参与者(Cohort,即本地资源管理器)发起执行操作的 CanCommit 请求,并等待参与者的响应. 2 参与者接收到请求后,会执行请求中的事务操作,记录日志信息(包含事务执行前的镜像),同时锁定当前记录。参与者执行成功,则向协调者发送“Yes”消息,表示同意操作;若不成功,则发送“No”消息,表示终止操作。 3 当所有的参与者都返回了操作结果(Yes 或 No 消息)后,系统进入了提交阶段。 第二阶段提交阶段 协调者会根据所有参与者返回的信息向参与者发送 DoCommit 或 DoAbort 指令 若协调者收到的都是“Yes”消息,则向参与者发送“DoCommit”消息,参与者会完成剩余的操作并释放资源,然后向协调者返回“HaveCommitted”消息; 如果协调者收到的消息中包含“No”消息,则向所有参与者发送“DoAbort”消息,此时发送“Yes”的参与者则会根据之前执行操作时的回滚日志对操作进行回滚,然后所有参与者会向协调者发送“HaveCommitted”消息; 协调者接收到“HaveCommitted”消息,就意味着整个事务结束了。 二阶段提交的实现 - XA

三阶段提交,也叫三阶段提交协议(英文缩写3pc) 是在计算机网络及数据库的范畴下,使得一个分布式系统内的所有节点能够执行事务的提交的一种分布式算法。三阶段提交是为解决两阶段提交协议的缺点而设计的。

TCC:try,confirm,cancel tcc补偿机制 try:预处理 - 完成所有的业务检查(一致性),预留必须业务资源(准隔离性)锁定资源 confirm:确认(如果成功了,执行confirm) cancel:撤销(若果失败了,执行cancel) 缺点:1.补偿代码复杂 2.因为补偿需要保证幂等性 - 乐观锁 优点:不需要一致阻塞 - 快

9.seate AT 与XA的区别

本质上都是2pc,但是XA是在数据库,AT是在业务层 AT多了一个TC TM负责决议,TC负责通知RM,RM负责执行

MQ基本面试题

1.什么是MQ

MQ(message queue),从字面上来说,本质是个队列,是先进先出的数据结构,只不过内存中存放的内容是message而已,还是一种跨进程的通信机制,用于上下游的传递消息

2.为什么要使用MQ

流量消峰、应用解耦、异步处理 即时响应的不需要用MQ,像物流信息等可以使用MQ

3.常用的MQ

ActiveMQ 优点:单机吞吐量万级,时效性 ms 级,可用性高,基于主从架构实现高可用性,消息可靠性较低的概率丢失数据 缺点:官方社区现在对 ActiveMQ 5.x 维护越来越少,高吞吐量场景较少使用

Kafka 优点: 性能卓越,单机写入 TPS 约在百万条/秒,最大的优点,就是吞吐量高。 时效性 ms 级可用性非常高 kafka 是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 消费者采 用 Pull 方式获取消息, 消息有序, 通过控制能够保证所有消息被消费且仅被消费一次 有优秀的第三方Kafka Web 管理界面 Kafka-Manager 在日志领域比较成熟,被多家公司和多个开源项目使用 缺点: 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 社区更新较慢;

RocketMQ RocketMQ 出自阿里巴巴的开源产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一 些改进。被阿里巴巴广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog 分发等场景 优点: 单机吞吐量十万级,可用性非常高 分布式架构,消息可以做到 0 丢失 MQ 功能较为完善,还是分布式的,扩展性好 支持 10 亿级别的消息堆积,不会因为堆积导致性能下降,源码是 java 我们可以自己阅读源码 缺点: 支持的客户端语言不多,目前是 java 及 c++,其中 c++不成熟 社区活跃度一般,没有在 MQ 核心中去实现 JMS 等接口,有些系统要迁移需要修改大量代码

RabbitMQ 2007 年发布,是一个在 AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一 优点: 由于 erlang 语言的高并发特性,性能较好;吞吐量到万级 MQ 功能比较完备,健壮、稳定、易 用、跨平台、支持多种语言 如: Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等 支持 AJAX 文档齐全;开源提供的管理界面非常棒,用起来很好用,社区活跃度高;更新频率相当高 缺点: 商业版需要收费,学习成本较高

RabbitMQ

1.AMQP的核心(rabbitmq的组件)

虚拟主机(virtual host)或(vhost) 一组交换机、队列和绑定器被称为 虚拟主机(vhost) RabbitMQ server 可以说就是一个消息队列服务器实体(Broker) Broker当中可以有多个用户,而用户只能在虚拟主机的粒度进行权限控制,所以RabbitMQ中需要多个虚拟主机 每一个RabbitMQ服务器都有一个默认的虚拟主机

交换机(exchange) 它指定消息按什么规则,路由到哪个队列。它可以被理解成具有路由表的路由程序。(发送消息的实体) 交换机可以存在多个,每个交换机在自己独立的进程当中执行,因此增加多个交换机就是增加多个进程,可以充分利用服务器上的CPU核以便达到更高的效率。

队列(queue) 队列是消息载体,每个消息都会被投入到一个或多个队列 试图创建一个已经存在的队列,RabbitMQ会直接忽略这个请求(接收消息的实体)。

绑定器(bind) 作用:把exchange和queue按照路由规则绑定起来 将交换器和队列连接起来,并且封装消息的路由信息

2.程序中连接与消息使用的两个关键概念

连接(Connection) 与RabbitMQ Server建立的一个连接 由ConnectionFactory创建 每个connection只与一个物理的Server进行连接,此连接是基于Socket进行连接的 AMQP一般使用TCP

通道 (Channel) 消息通道(主要进行相关定义,发送消息,获取消息,事务处理等) 在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务

3.七种工作模式

简单模式:一个生产者,一个消费者 work模式:一个生产者,多个消费者,每个消费者获取到的消息唯一 订阅模式:一个生产者发送的消息会被多个消费者获取 路由模式: 发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key topic模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词

RPC模式:使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器

4.Exchange的类型

Direct:直接交换机通过消息上的路由键直接对消息进行分发,相当于精确匹配,一对一 Topic:这个交换机会将路由键和绑定上的模式进行通配符匹配,相当于模糊匹配,一对多 Fanout:交换机会将消息发送到所有和它进行绑定的队列上,广播,群发 Headers:消息头交换机使用消息头的属性进行消息路由,相当于模糊匹配(like header%),一对多

5. ACK

默认情况下,消费者接收到消息的时候(但是通常接到消息就处理了),就会进行自动应答(ACK)

如果一个消费者处理消息时间长,或者异常了,所以需要手动Ack

6. Publisher Confirms

不仅仅是防止发送方到mq的消息丢失,还可以通过异步的方式来提高效率

1.为了消息不丢失,持久化,持久化是同步的,可以通过confirm的异步确认提高效率

7. Confirms 三种方式

1.单个确认(同步) 2.批量确认(同步) 3.异步确认

8.开启消息持久化速率慢的解决方案

不开启持久化是异步的效率快,但一般要开启持久化,否则会导致消息丢失

开启持久化是同步的导致速率变慢

大部分消息队列在开启持久化的同时开启事务变为异步,提高速率

但rabbitMQ使用Confirm变为异步处理,提高效率

9,生产者生产之后怎么放到队列里面的

生产者投递消息到exchange 上,exchange会把消息路由到指定的queue上,(根据某种路由规则去路由的)

消费者只需要监听mq然后去消费就可以了

消费者确认接收到的消息之后就 rabbitMQ从队列中删除相应己经被确认的消息

10.rabbitmq 的使用场景有哪些?

  • 跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。(就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。)

  • 多个应用之间的耦合(由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送方和接收方同时在线。在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以作为集成的方法。)

  • 应用内的同步变异步(比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。)

  • 消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。

  • 应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。

  • 跨局域网,甚至跨城市的通讯(CDN行业),比如北京机房与广州机房的应用程序的通信。

11.要保证消息持久化成功的条件有哪些?

  1. 声明队列必须设置持久化 durable 设置为 true.

  2. 消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。

  3. 消息已经到达持久化交换器。

  4. 消息已经到达持久化队列。

以上四个条件都满足才能保证消息持久化成功。

12.为什么会出现消息重复?

消息重复的原因有两个:1.生产时消息重复,2.消费时消息重复。

生产时消息重复

由于生产者发送消息给MQ,在MQ确认的时候出现了网络波动,生产者没有收到确认,实际上MQ已经接收到了消息。这时候生产者就会重新发送一遍这条消息。

生产者中如果消息未被确认,或确认失败,我们可以使用定时任务+(redis/db)来进行消息重试。

消费时消息重复

消费者消费成功后,再给MQ确认的时候出现了网络波动,MQ没有接收到确认,为了保证消息被消费,MQ就会继续给消费者投递之前的消息。这时候消费者就接收到了两条一样的消息。

由于重复消息是由于网络原因造成的,因此不可避免重复消息。但是我们需要保证消息的幂等性。

13、了解死信队列

当消息达到过期时间还没有被消费,那么那个消息就成为了一个 死信 消息。

14、mq重复消费

解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性;

  • 在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;

  • 在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。

这个问题针对业务场景来答分以下几点:

    1. 如果消息是做数据库的insert操作,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

    2. 如果消息是做redis的set的操作,不用解决,因为无论set几次结果都是一样的,set操作本来就算幂等操作。

    3. 如果以上两种情况还不行,可以准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

(五)Vue

1.vue的生命周期

(1)创建对象实例 --初始化

(2)编译模板

(3)挂载

(4)data内容发生改变时更新

(5)销毁

beforeCreate( 创建前 )

在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el 和 data 并未初始化,因此无法访问methods, data, computed等上的方法和数据。

created ( 创建后 )

实例已经创建完成之后被调用,在这一步,实例已完成以下配置:数据观测、属性和方法的运算,watch/event事件回调,完成了data 数据的初始化,el没有。 然而,挂在阶段还没有开始, $el属性目前不可见,这是一个常用的生命周期,因为你可以调用methods中的方法,改变data中的数据,并且修改可以通过vue的响应式绑定体现在页面上,,获取computed中的计算属性等等,通常我们可以在这里对实例进行预处理,也有一些童鞋喜欢在这里发ajax请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的,因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个方法发请求,建议在组件路由钩子beforeRouteEnter中完成

beforeMount

挂在开始之前被调用,相关的render函数首次被调用(虚拟DOM),实例已完成以下的配置: 编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。

mounted

挂在完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次。

beforeUpdate

在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程

updated(更新后)

在由于数据更改导致地虚拟DOM重新渲染和打补丁只会调用,调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,然后在大多是情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器端渲染期间不被调用

beforeDestroy(销毁前)

在实例销毁之前调用,实例仍然完全可用,

  1. 这一步还可以用this来获取实例,

  2. 一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件

destroyed(销毁后)

在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用

2.vue父子组件传值

父 -> 子 props

子 -> 父

1、子组件主动触发事件将数据传递给父组件。

2、子组件中绑定ref,且定义一个父组件可直接调用的函数,父组件注册子组件后绑定ref,调用子组件的函数获取数据。

3.获取兄弟节点

js

var chils= s.childNodes; //得到s的全部子节点   var par=s.parentNode; //得到s的父节点   var ns=s.nextSibling; //获得s的下一个兄弟节点   var ps=s.previousSibling; //得到s的上一个兄弟节点   var fc=s.firstChild; //获得s的第一个子节点   var lc=s.lastChild; //获得s的最后一个子节点

jq

jQuery.prev()`,返回上一个兄弟节点,不是所有的兄弟节点 jQuery.prevAll(),返回所有之前的兄弟节点 jQuery.next(),返回下一个兄弟节点,不是所有的兄弟节点 jQuery.nextAll(),返回所有之后的兄弟节点 jQuery.siblings(),返回所有兄弟节点,不分前后

4.jq选择器

标签选择器

id选择器

类选择器

子代选择器

后代选择器

兄弟选择器

过滤选择器

$("li:first") //第一个li

$("li:last") //最后一个li $("li:even") //挑选下标为偶数的li $("li:odd") //挑选下标为奇数的li $("li:eq(4)") //下标等于 4 的li(第五个 li 元素) $("li:gt(2)") //下标大于 2 的li $("li:lt(2)") //下标小于 2 的li $("li:not(#runoob)") //挑选除 id="runoob" 以外的所有li

5.ajax请求如何同步

修改async:false,//同步

6.如何回显数据

原生:for循环创建th,hr标签,通过.text()方法设置标签的值

vue:通过v-for循环,使用v-model进行数据绑定

六、其他

(一)版本控制工具

1.java 版本控制工具:svn,git

SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而工作的时候,用的都是自己的电脑, 所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己做完的活推送到中央服务器。 集中式版本控制系统是必须联网才能工作,对网络带宽要求较高。

Git是分布式版本控制系统,没有中央服务器,每个人的电脑就是一个完整的版本库,工作的时候不需要联网了,因为版本都在自己电脑上。

应用场景

Git适用于各类开源代码的版本控制管理(本身就是为Linux内核的版本控制而生),而SVN适用于含有大量二进制文件的各类项目整体的版本控制管理(老牌的版本控制管理工具)。

2.git和svn的区别

Git和SVN,两者没有孰优孰好,在不同的应用场景有着各自的优势。两者的部分差异在于:

Git为分布式的,可以不使用服务器,在本地进行操作;SVN为集中式的,使用时必须连接服务器。 Git将内容按元数据存储,本地Git目录就是个完整的版本库;SVN则是把内容汇总提交至服务器中统一存储。 Git分支为不同版本镜像的指针,使用较为简便;SVN的分支相当于将整个仓库拷贝一份使用。 Git没有全局版本号信息,使用SHA-1哈希算法标记不同版本;SVN使用连续的版本号记录版本信息。 Git各个用户对仓库拥有完成的访问权限;SVN可以设定仓库访问的用户及对应的读写权限。 … 总体来说在实际应用中,Git适用于各类开源代码的版本控制管理(本身就是为Linux内核的版本控制而生),而SVN适用于含有大量二进制文件的各类项目整体的版本控制管理(老牌的版本控制管理工具)。

3.git的区域

远程仓库

位于托管代码的服务器,远程仓库的内容能够被分布在多个地点的处于协作关系的本地仓库修改。比起本地仓库,远程仓库通常旧一些,因此本地仓库修改完之后需要同步到远程仓库。

本地仓库

位于自己的机器,本地仓库保存了被提交过的各个版本,比起工作区和暂存区的内容,它更旧一些。

首先是 git commit 同步 index 的目录树到本地仓库,然后通过 git push 同步本地仓库到远程仓库。

暂存区

位于.git目录下的index文件,暂存区会记录 git add 添加文件的相关信息(文件名、大小),不保存文件实体,通过 id 指向每个文件的实体。

使用 git status 可以查看暂存区的状态,暂存区标记了当前工作区中那些内容是被 git 管理的,当完成某个需求或者功能后需要提交代码,第一步就是通过 git add 先提交到暂存区。

工作区

即进行开发改动的地方,是当前看到的,内容也是最新的,平常开发就是拷贝远程仓库中的分支,基于该分支进行开发,在开发的过程就是在工作区的操作。

4.git的常用命令

未暂存区域转到暂存区域

  • git add files

暂存区提交到本地仓库

  • git commit -m

直接从未暂存区提交到本地仓库

  • *git commit -am*

  • 经测试,对已跟踪的文件可以正确执行,而对于未跟踪文件(即新增文件)则会出错

本地库回退到暂存区

  • *git reset –soft* hash**值

  • *git reset –soft* origin/master

  • 一般回退到暂存区的文件作排查用,不要直接修改,不然会同时出现在暂存区和未暂存区(其实即使修改了也木有太大关系)

本地库回退到未暂存区

  • *git reset –mixed* hash**值

  • *git reset –mixed* origin/master

  • 一般回退到未暂存状态就是为了进一步的修改

本地库回退到文件初始状态(即此版本的)

  • *git reset –hard* hash**值

  • 注意这里,通常先执行一次fetch,保证本地版本是origin的最新版本,然后再回退。(最厉害的是,这么操不会有冲突,直接让文件变成和origin保持一致)

    • git fetch origin

    • git reset –hard origin/master

    • 特别注意:这么操作会使你对文件的修改全部消失,还原成最初状态。

  • (针对上一条情况衍生讲解)通常在推送到origin时,先要pull,然后再推送,一般是修改提交了的文件和pull来的同一个文件产生冲突(所以建议修改代码前,一定先要pull)

    • git pull

    • git push origin master

暂存区回退到未暂存区

  • *git reset –* files

  • git rest

    • 撤销所有暂存区的文件

未暂存区回退到文件初始状态

  • git checkout – files

暂存区回退到文件初始状态

  • git checkout head – files

(二)多线程

1.什么进程?

程序是静态的,进程就是程序的一次动态执行,是系统进行资源分配和调度的一个单位,当程序进入内存运行时,即为进程。

2.什么是线程?

执行的最小单位,就是一个进程内的一个顺序执行流

3.什么是多线程?

每一个进程内不止一个线程

4.线程的生命周期?

创建,就绪,运行,阻塞,死亡

运行后要么阻塞,要么死亡

阻塞后重新进入就绪阶段

5.runnable和callable的区别

callable:可以抛出异常,异步计算结果。可以通过futureTask的get方法获取结果

6.守护线程

常见的是GC,通常是线程执行完毕,守护线程完毕,生命周期是跟随其他线程

7.synchornized

线程拥有锁,锁的是对象

8.synchornized实现原理?

synchornized可以修饰方法跟代码块 ​ 修饰方法:通过ACC_SYNCHORDIZED标记来实现同步,方法级的同步时隐式的,同步方法在常量池中会有一个ACC_SYNCHORDIZED的标识,当线程进入某个方法的时候,会判断有没有这个标识,如果有会获得一个监视器锁(monitorenter),运行结束释放锁,有其他线程在这个过程中访问该方法会被阻塞,方法出异常时,这个锁会被释放 ​ 修饰代码块:每个对象都维护着一个被锁次数的计数器,未被锁的对象计数器为0,当线程获取锁(执行monitorenter后),计数器自增1,当同一线程再次获取该对象的锁,计数器继续自增,当同一对象释放锁(执行monitorexit)的时候,计数器自减1,当计数器为0的时候,其他线程可以获取锁.

9.为什么有了synchornized还要用lock?

synchornized的关键字有两种情况会释放锁,第一是线程执行完方法或代码块,第二个是方法抛出异常,我们无法手动去释放锁,但是lock可以

10.偏向锁,自旋锁,轻量锁,重量锁

1.偏向锁:当程序没有竞争,取消同步操作,发生竞争时,升级为轻量锁 ​ 2.轻量锁:(对象的头信息markword中存放了锁的状态和线程持有的索)如果当前线程用cas将markword锁记录(Lock Record)指针替换成功,获取轻量锁,如果失败尝试自旋,成功获取轻量锁,失败膨胀为重量锁 ​ 3.自旋锁:默认10次,JDK1.8变成自适应自旋,会根据上一个线程自旋的时间的状态来自动增减对该锁的自旋次数,(相当于循环获取锁) ​ 4.重量锁:竞争失败后,阻塞

11.死锁

由于两个或两个以上的线程互相持有对方所需要的资源,导致这些线程处于等待状态。 当然死锁的产生是必须要满足一些特定条件的: 1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。 3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用 4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

12.如何避免死锁:

加锁顺序 ​ 加锁时限 ​ 死锁检测

13.线程的控制?

sleep和wait的区别:sleep Thread的方法,休眠期间线程不会释放锁,wait是Object的方法,等待会释放锁,唤醒后才可以去竞争锁

14.现在有线程T1 T2 T3,你如何确保T2线程在T1之后执行,并且T3线程在T2之后执行?

t1.join t2.join

15.实现多线程的方法:

1.Thread 2.Runnable 3.Callable

16.Java中的volatile变量是什么,实现原理

可修饰成员变量,只能确保变量的可见性,原理是变量强制写入主内存

17.什么是ThreadLocal变量

根据名字我们可以知道这是一个线程本地变量,每一个线程都回copy一个副本,可以为创造对象减少不必要的开销(单例),但是不能保证线程安全(每个线程的结果可能是不一样的)

18.Future有什么用?

Future是一个接口,有一个实现类FutureTask,用于对Runnnable或Callable任务的执行结果进行取消,查询,获取结果等操作,注意的是获取结果(get())是阻塞的

19.什么是线程池?为什么要用它

就是管理线程的,因为线程的创建需要资源跟时间,不能无限的去创建,所以需要用到线程池管理

20.线程池的参数?

1.int corePoolSize:核心线程数(默认开启的线程数) ​ 2.int maxnumPooolSize:最大线程数 ​ 3.long keepAliveTime:空闲线程存活时间 ​ 4.TimeUnit unit:存活时间单位 ​ 5.BlockingQueue<Runnable> workQueue:阻塞队列(有心的请求来临时核心线程数用完,会进入队列,队列满了会开辟新的线程,数量不能超过最大线程数) ​ 6.ThreadFactory threadFactory:线程工厂(创建线程) ​ 7.RejectedExecutionHandler hander:拒绝策略(当最大线程数已满,阻塞队列已满时对于新请求的拒绝策略)

21.线程池的拒绝策略有哪些?

一共四种: ​ 1.AbortPolicy直接抛出异常 ​ 2.DisCardPolicy舍弃任务 ​ 3.DisCardOlddestPolicy舍弃队列靠前的任务 ​ 4.CallerRunsPolicy谁提交的谁执行

22.线程池的执行流程?

如果核心线程数有空闲,那么分配线程,如果没有进入队列,创建新的线程,执行队列的任务,如果核心线程数跟队列都满了,采用拒绝策略.

23.volatile关键字

不能保证原子性,但可以保证内存可见性,基本的核心思想就是,当我们修改完成后,将值强制写入主内存,

JVM会尽力保证内存可见性,

24.CountDownLatch和CyclicBarrier的区别

  • countdownlatch放行条件=设定的计数器,CyclicBarrier放行条件=设定的线程数

  • countdownlatch计数器-1,通过手动调用,countDown(),阻塞主线程

  • CyclicBarrier是自己先执行,执行完后等待其他线程执行,阻塞的是自己

  • CyclicBarrier多了一个恢复之后可执行的参数

25.多个线程之间如何通信

await就是加入AQS的内部conditionObject实现类的等待队列里。

可使用signal唤醒该队列的第一个线程节点,使用signalAll唤醒队列里所有的线程节点

(三)高并发

1.秒杀

1.活动页面并发量大,服务器压力大,使用Thymeleaf页面静态化

2.并发量大,访问数据库造成数据库压力大,使用redis缓存

3.使用redis缓存出现缓存击穿,使用双重检测锁

4.库存

4.1 解决超卖问题

1.使用分布式锁解决超卖问题,缺点:性能较差

2.通过redis的原子操作解决超卖问题

将商品的id和库存放入redis缓存中,防止数据库压力过大

在用户下单时下单时去redis中查询,如果没有去mysql查询,使用setnx放入redis,id为key,库存为value

对redis库进行减的操作,通过decrby key numbers减库存,获取返回值如果<0,再用过incrby将减去的库存加回去,返回库存不足,返回结果>=0,说明库存足够

4.2 解决回流问题

使用分布式任务调度,每30秒进行订单查询,五分钟未支付进行取消订单

5.每个用户只能购买一份

用户下单后使用setnx,经userId放入redis缓存中,后面前进行判断

6.订单支付

可使用ribbitMQ来进行异步处理

并发量小可不使用MQ

7.防止机器抢票

对同一用户限流:设置一分钟可以访问5次 对同一ip限流:设置一分钟可以访问5次 对接口限流 加验证码同样能限制用户的访问频次,但好处是不会存在误杀的情况。

2.点赞

1.引入redis缓存避免高并发下数据库写的压力,同时提高读的性能

2.使用redis的原子性也可以防止点赞数的数据错乱

3.点赞的同步功能对服务器压力大(redis),使用rabbitMQ每次点赞请求发送到MQ并及时返回,点赞请求及时结束,避免点赞请求线程占用时间长。

4.使用定时任务将redis中的数据同步到mysql中

redis中存储类型

  1. 使用set来存储被点赞的类型id,key为被点赞类型名,value为类型id

  2. hash存储某个类型点赞的记录,key为类型名+类型id,hashKey为点赞人,hashValue为点赞时间

4.XXL-JOB任务调度

首先部署调度中心,部署了调度中心之后,需要往调度中心注册执行器,添加调度任务。

(四)登录

1.单点登录和传统登录的区别

传统的登录方式

通过登录页面根据用户名查询用户信息,判断密码是否正确,正确则将用户信息写到session,访问的时候通过从session中获取用户信息,判断是否已登录,登录则允许访问。

由于session不能共享,服务越来越多,并且还服务还搭建集群,导致每访问另外一个服务都需要重新登录。

单点登录–Single Sign On(SSO

在多个应用服务(跨服务,跨服务器)中,用户只需要登录一次就可以访问所有相互之间信任的服务,是目前比较流行的企业业务整合的解决方案之一。

(五)网络

1.tcp/ip和udp的区别

rest(udp)、rpc(tcp/http)

连接和非连接(邮件和打电话)

2.三次握手和四次挥手

3.粘包和拆包

(六)jvm

1.jvm的主要组成部分及其作用

class loader 类加载器:加载类文件到内存。(只负责加载)

exection engine :执行引擎也叫解释器,负责解释命令,交由操作系统执行。

native interface:本地接口。本地接口的作用是融合不同的语言为java所用。

Runtimedata area 运行数据区:运行数据区是jvm的重点,我们所有所写的程序都被加载到这里,之后才开始运行。

2. JVM 运行时数据区?

程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;

Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;

本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;

Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;

方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

人事面试题

1.为什么从上家公司离职

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/485186
推荐阅读
相关标签
  

闽ICP备14008679号