Java面试必备八股文

基础篇

面向对象

面向对象和面向过程是两中不同的问题的处理角度。

面向过程给更注重的是步骤和顺序,面向对象更注重的是参与者

面向过程比较高效,面向对象更易复用,维护,扩展

面向对象有三大特性:

封装:就是隐藏对象的内部实现细节对外暴漏一个接口,外部调用时无需知道内部是如何实现的

继承:基于基类的方法,并作出自己的改变或扩展

多态:多态的前提就是要有继承,方法重写,父类引用指向子类引用,基于对象所属的类不同,外部对同一个方法的调用,实现的是不同的逻辑,但无法调用子类独有的方法(不是对父类进行重写的)

多态的作用

多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。

java程序是如何执行的?

先把Java代码编译为字节码,也就是将.java文件编译为.class文件

class文件放入Java虚拟机,这个虚拟机指的是Oracle官方自带的Hotspot JVM

Java虚拟机使用类加载器classLoad装载class文件

加载完成后会,会进行字节码校验,字节码校验通过后JVM解释器会把字节码翻译为机器码交由操作系统执行

静态变量和实例变量的区别

静态变量前static

实例变量属于某个对象的属性,必须创建实例对象,其中实例变量才会被分配空间,才能够使用这个实例变量。静态变量不属于对象,属于类,通常也叫类变量,只要程序加载了类的字节码,不用进行实例化,静态变量就会被分配空间,就能直接使用。

== 和 equals有什么区别?

==基本类型对比比的是值 == 引用类型对比的是地址

而equals方法是Object类中的方法,用于比较两个对象的内容是否相等,即判断两个对象的属性值是否相等。因此,两者的比较方式和比较的内容不同,使用时需要根据具体情况选择合适的方法。

重载和重写的区别

重载是在一个类中方法名称相同 参数列表不同返回值不同权限修饰符不同

重写:是发生在子类和父类之间的,子类继承父类实现父类的方法 方法名相同参数列表相同返回值范围小于等于父类抛出异常的范围要小于等于父类,访问修饰符要大于等于父类;子类为private不能进行重写。

finaly在Java中有什么作用

finaly修饰类类不能被继承

finaly修饰方法 方法不能被重写

finaly修饰变量 变量变为常量 不能被修改必须赋值

异常处理机制

(1)使用try、catch、finaly捕获异常,finaly中的代码一定会执行,捕获异常后程序会继续执行

(2)使用throws声明该方法可能会抛出的异常类型,出现异常后,程序终止

String属不属于基本类型

不属于,基本数据类型包括:int、double、char、short、byte、boolean、long、float

Java中操作字符串的类,以及他们之间有什么区别?

String StringBuffer StringBuilder

String不是可变长字符串 StringBufferStringBUilder是可边长字符串

每次操作都会生成新的String对象,然后指针指向新生成的String对象,而StringBuffer、StringBuilder可以在原有的对象上进行操作,所以在经常改变字符串内容的情况不要使用String

StringBuffer不是线程安全的StringBUilder是线程安全的

StringBuffer效率比StringBuilder高

String str="i"和String str=new String("i")一样吗?

不一样String str="i"会分配的字符串常量池中,String str=new String("i")会分配到堆内存中

String常用的方法有哪些

indexOf() :返回指定字符的索引

charAt() :根据索引返回指定字符

raplace() :字符串替换

trim() : 去除空格

split() : 根据指定字符分割字符串

getBytes() :返回字符串的byte类型数组

length() :返回字符串的长度

subString() :截取字符串

equals() :把字符串进行比较

toLowerCase() :将字符串转换为小写

toUpperCase() :将字符串转换为大写

抽象类必须要有抽象方法吗?

不是 抽象类可以有非抽象的方法

抽象类和普通类有什么区别

抽象类不能实例化 普通类可以实例化

普通类不能有抽象方法 抽象类可以有抽象方法

抽象类能不能使用finaly

不能使用finally要是用了子类就不能继承就没有意义,编译是也会报错。

接口和抽象列的区别

相似点:

1)接口和抽象类都不能被实例化

2)实现接口或继承抽象类的普通子类都必须实现这些抽象方法

不同点:

1)抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,

2)抽象类可以有构造方法,而接口没有

3)抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值

Java中的io流分为几类

输入流输出流 字节流字符流

BIO,NIO,AIO有什么区别

Files有什么常用的方法

什么是Java的反射

就是动态的访问类的基本属性和方法

对于任意的一个类,都可以知道他的属性和方法,对于任意一个对象都能调用他的属性和方法,这种动态获取到信息以及动态的调用对象方法的功能被称为Java的反射机制

自定义注解

集合容器

Collection和Cllections的区别

Collection是集合接口,提供了对集合进行基本操作的通用接口方法在Java类库中有很多具体实现

Collections是一个集合的工具类,提供了一系列的静态方法供集合使用

List,Set,Map的区别

List 和Set是conllection的子接口,Map不是和collection平级

List 是有序的 Set是无须的 map 也是无序的

List是不可重复的 Set是可重复的 Map的key是不可以重复的 value是可以重复的

HashMap和HashTable有什么区别

(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低 hashMap不是线程安全的

(2)HashTable的Key不允许为null HashMap,key不能重复,可以为null

(3)HashTable只对key进行一次hash,HashMap进行了两次Hash

(4)HashTable底层使用的数组加链表 hashMapJdk1.8以后是基于数组+链表+红黑树来实现的

说一下HashMap的实现原理?

数据结构:数组+链表实现(扩容是扩容到原来的两倍)

Jdk8开始链表高度到8,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树

原理:当我们向HashMap中put元素时,首先根据key的hashcode重新计算出hash值,再根据hash值得到元素在数组中的位置(下标),如果该数组在其位置存放了其他元素,那么这个位置上的元素将以链表的形式存放,如果链表的高度达到8并且数组长度超过64就会将链表替换成红黑树,如果该位置没有存放其他元素,就直接将该元素放到数组的该位置上

通过进行两次哈希,HashMap 能够尽可能地减少哈希冲突,提高键值对的存取效率。它可以在第一次哈希的基础上,再次进行调整,以找到一个合适的位置存储数据,使得键值对分散在整个哈希表中,尽量避免冲突。

.想要线程安全的HashMap怎么办?

(1)使用ConcurrentHashMap

(2)使用HashTable

(3)Collections.synchronizedHashMap()方法

ConcurrentHashMap原如何保证的线程安全?

JDK1.7:使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁

JDK1.8:采用CAS+Synchronized保证线程安全,每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全

什么是链表?

链表是物理存储单位上,非连续,非顺序的存储单位它是由一个个节点通过指针联系起来的,其中每个节点都包含数据和指针

说一下HashSet的实现原理?

HashSet是由HashMap实现的,Hash底层使用的HashMap来保存数据,所有实现比较简单

ArraysList和LinkedList的区别

ArrayList底层是数组结构LinkedList底层是链表接口

Array的查询效率比LinkedList的查询效率高

但是LinkedList的增加和删除的效率比ArrayList高,ArrayList是数组,在对其增删操作时,会对数据的下标索引造成影响,需要进行数据移动

如何实现数组和List之间的切换?

数组转换为List:使用数组的asList方法

List转换为数据:使用List的toArray()方法

ArrayList的原理

底层是数组实现的,

他的默认长度为十,检查元素的个数是否达到上线,要是达到上限就会进行扩容,扩容的数组是原来数组的1.5倍

ArrayList和Vecot的区别

ArrayList不是线程安全的Vecot是线程安全的

ArrayList的性能比Vecot的性能好

Vecot的扩容是每次都扩容已一倍Arratlist的扩容是扩容给50%

Arrray和ArrayList的区别

Array是能存储进本数据类型和对象ArrayList只能存储对象

Array是指定大小后不可变的,ArrayList是可变的

ArrayhemiArrayList那么多的功能

哪些集合类是线程安全的?

Vecot HashTable Statck enumeration(比较器)

迭代器Iterator是什么?

迭代器是一种设计模式,他是一个对象,它可以遍历并选择序列中的对象,通常称为轻量级对象,因为创建它的代价小

Iterator和IteratorList有什么区别?

Iterator可以来遍历Set和List集合,IteratorList只能用来遍历List集合

Iterator只能进行前向遍历,IteratorList即可以前向也可以后向遍历

IteratorList继承Iterator,并包含其他的功能

多线程

.进程和线程的区别,进程间如何通信

进程:系统运行的基本单位,进程在运行过程中都是相互独立,但是线程之间运行可以相互影响。

线程:独立运行的最小单位,一个进程包含多个线程且它们共享同一进程内的系统资源

进程间通过管道、 共享内存、信号量机制、消息队列通信

. 什么是线程上下文切换

当一个线程被剥夺cpu使用权时,切换到另外一个线程执行

并行和并发的区别

并行是两个或多个事件在同一时间进行运行,并发是指两个或多个事件在同一时间间隔进行运行

并行是在不同实体类的多个事件,并发是在同一个实体类的多个事件

创建线程的几种方式

继承Thread类

实现Runnable接口

实现Callable接口

在线程池中创建线程

说一下runnable和callable有什么区别

Runnable和Callable 的返回值不同

Runnable的run方法没有返回值

Callable发call方法有返回值,是个泛型

保证并发安全的三大特性?

原子性:线程的一次或多次操作在执行期间不被其他线程影响

可见性:当一个线程在工作内存修改了变量,其他线程能立刻知道

有序性:JVM对指令的优化会让指令执行顺序改变,有序性是禁止指令重排

线程有那些状态

创建线程:创建当前线程还没有执行start方法

就绪状态:调用的线程的start方法,线程进入就绪状态

运行状态:将就绪状态的线程设置为当前线程此状态的线程就为运行状态,调用run方法

堵塞状态:线程正在运行被停止

死亡状态:当前线程执行完run方法并且调用stop方法线程变为死亡状态

Sleep和wait有什么区别

Sleep是Thread中的方法 wait是Object的方法

调用Sleep方法不会释放类锁 调用wait方法会释放类锁
wait()要在同步方法或者同步代码块中执行,sleep()没有限制

wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒

Notify和NotifyAll有什么区别

Notify可能会产生死锁NotifyAll不会产生死锁

NotifyAll会将线程中的所有的睡眠状态的线程唤醒,Notify只会唤醒一个线程

Notify是Notify的一个优化

线程run和start有什么区别

Start方法是启动线程的方法 run方法是运行线程任务逻辑的方法 run方法可以调用多次start只能调用一个

创建线程四种方法的区别

Runnable和Thrable主进程不会获取运行线程的结果

Collable主进程可以获取线程的运行结果,x但是不利于控制服务器中的线程资源,会将资源消耗殆尽

线程池创建线程可以获取线程的运行结果,性能稳定,并能捕获异常,但是在复杂的业务中一个异步调用可能会调用另一个异步调用运行的结果

使用线程池的好处?

较低能量损耗

线程池重复使用已经创建的线程降低线程的创建和销毁损耗的资源

提高线程的响应速度

线程池中的线程数没有超过线程的最大线程数,有些线程进入等待状态,当有任务来临无需创建新的线程就能运行

提高线程的管理性

线程池会根据当前线程的状态来优化线程,减少创建和销毁的带来的损耗无限的创建线程不仅会耗资源还会较低系统的效率,使用线程池进行统一管理

线程池中submit()和execute()方法有什么区别?

接受的参数不同

Submint没有返回值exrcute()有返回值

Submit方便异常的处理

在java程序中怎么保证多线程的安全

体现在三个方面:

原子性:提供互斥访问,同一时刻只能有一个线程对数据操作(atomic,synchronized)

可见性:一个线程对主内存的修改可以被其他线程看到(volatile,synchronized)

有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则)

什么是死锁

死锁指多个线程在执行过程中,因争夺资源造成的一种相互等待的僵局

线程死锁是指在执行的过程中进行资源的抢夺形成的一种等待的僵局

怎么防止死锁

死锁产生的四个必要条件:

互斥条件:同一资源同时只能由一个线程读取

不可抢占条件:不能强行剥夺其他线程占有的资源

请求和保持条件:请求其他资源的同时对自己手中的资源保持不放

循环等待条件:在相互等待资源的过程中,形成一个闭环

想要预防死锁,只需要破坏其中一个条件即可,比如使用定时锁、尽量让线程用相同的加锁顺序,还可以用银行家算法可以预防死锁

ThreadLocal原理

原理是为每个线程创建变量副本,不同线程之间不可见,保证线程安全。每个线程内部都维护了一个Map,key为threadLocal实例,value为要保存的副本。

但是使用ThreadLocal会存在内存泄露问题,因为key为弱引用,而value为强引用,每次gc时key都会回收,而value不会被回收。所以为了解决内存泄漏问题,可以在每次使用完后删除value或者使用static修饰ThreadLocal,可以随时获取value

说一下synchronized底层实现原理?

Synchronized可以保证方法和代码块在运行时在同一时刻只有一个方法或者代码块执行

.Synchronized锁原理和优化

Synchronize是通过对象头的markwordk来表明监视器的,监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换要从用户态切换为核心态,成本很高,此时这种锁叫重量级锁,在JDK1.6以后引入了偏向锁、轻量级锁、重量级锁

偏向锁:当一段代码没有别的线程访问,此时线程去访问会直接获取偏向锁

轻量级锁:当锁是偏向锁时,有另外一个线程来访问,会升级为轻量级锁。线程会通过CAS方式获取锁,不会阻塞,提高性能,

重量级锁:轻量级锁自旋一段时间后线程还没有获取到锁,会升级为重量级锁,重量级锁时,来竞争锁的所有线程都会阻塞,性能降低

注意,锁只能升级不能降级

什么是AQS锁?

AQS是一个抽象类,可以用来构造锁和同步类,如ReentrantLock,Semaphore,CountDownLatch,CyclicBarrier。

AQS的原理是,AQS内部有三个核心组件,一个是state代表加锁状态初始值为0,一个是获取到锁的线程,还有一个阻塞队列。当有线程想获取锁时,会以CAS的形式将state变为1,CAS成功后便将加锁线程设为自己。当其他线程来竞争锁时会判断state是不是0,不是0再判断加锁线程是不是自己,不是的话就把自己放入阻塞队列。这个阻塞队列是用双向链表实现的

可重入锁的原理就是每次加锁时判断一下加锁线程是不是自己,是的话state+1,释放锁的时候就将state-1。当state减到0的时候就去唤醒阻塞队列的第一个线程。

.为什么AQS使用的双向链表?

因为有一些线程可能发生中断 ,而发生中断时候就需要在同步阻塞队列中删除掉,这个时候用双向链表方便删除掉中间的节点

.有哪些常见的AQS锁

AQS分为独占锁和共享锁

ReentrantLock(独占锁):可重入,可中断,可以是公平锁也可以是非公平锁,非公平锁就是会通过两次CAS去抢占锁,公平锁会按队列顺序排队

Semaphore(信号量):设定一个信号量,当调用acquire()时判断是否还有信号,有就获取一个信号量,没有就阻塞等待其他线程释放信号量,当调用release()时释放一个信号量,唤醒阻塞线程。

应用场景:允许多个线程访问某个临界资源时,如上下车,买卖票

CountDownLatch(倒计数器):给计数器设置一个初始值,当调用CountDown()时计数器减一,当调用await() 时判断计数器是否归0,不为0就阻塞,直到计数器为0。

应用场景:启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行

CyclicBarrier(循环栅栏):给计数器设置一个目标值,当调用await() 时会计数+1并判断计数器是否达到目标值,未达到就阻塞,直到计数器达到目标值

应用场景:多线程计算数据,最后合并计算结果的应用场景

MySql

什么是数据库 为什么要使用数据库

数据库就是数据管理的产物。

数据保存在内存

优点:存取速度快

缺点:数据不能永久保存

数据保存在文件

优点:数据永久保存

缺点:读取速度慢,查询数据不方便

数据保存在数据库

数据永久保存

查询方便效率高

管理数据方便

什么是SQL?

结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言

作用:用于存储数据、查询、更新和管理关系数据库系统

MyIsAm和InnoDB的区别(数据库的存储引擎)

InnoDB有三大特性,分别是事务、外键、行级锁,这些都是MyIsAm不支持的,

另外InnoDB是聚簇索引,MyIAm是非聚簇索引,

InnoDB不支持全文索引,MyIAm支持

InnoDB支持自增和MVCC模式的读写,MyIAm不支持

MyIsAM的访问速度一般比InnoDB快,差异在于innodb的mvcc、行锁会比较消耗性能,还可能有回表的过程(先去辅助索引中查询数据,找到数据对应的key之后,再通过key回表到聚簇索引树查找数据)

数据库的三大范式?

第一范式:每个列都不可再拆分

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非组件

说一下ACID是什么(数据库的特性)?

Atomicity(原子性):一个事务的中的所有操作,要不全部完成,要不全部不完成,不会出现再中间某个环节结束

Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏

Isolation(隔离性):防止多个事务再并发执行时由于交叉执行而导致的数据不一致

Durability(持久性):事务结束后,对数据的修改就是永久的,即便系统故障页不会丢失

ACID靠什么保证的呢?

 A原子性(atomicity) 由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql

 C一致性(consistency) 一般由代码层面来保证

 I隔离性(isolation) 由MVCC来保证

 D持久性(durability) 由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复

.MVCC是什么

MVCC是多版本并发控制,为每次事务生成一个新版本数据,每个事务都由自己的版本,从而不加锁就决绝读写冲突,这种读叫做快照读。只在读已提交和可重复读中生效。

实现原理由四个东西保证,他们是

undolog日志:记录了数据历史版本

readView:事务进行快照读时动态生成产生的视图,记录了当前系统中活跃的事务id,控制哪个历史版本对当前事务可见

隐藏字段DB_TRC_ID: 最近修改记录的事务ID

隐藏字段DB_Roll_PTR: 回滚指针,配合undolog指向数据的上一个版本

mysql中的常用语句

- DDL(Data Definition Language):数字定义语句,主要进行定义/改变表的结构、数据类型、表之间的链接等操作。常用语句关键字有create、drop、alter等

- DML(Data Manipulation Language):数据操纵语句,主要对数据进行添加、修改、删除操作,关键字包括inster、delete、update等

- DQL(Data Query Language):数据查询语言,主要对数据库进行查询操作,常用的关键字包括select、from、where等

- DCL(Data Control Language):数据控制语句,主要用来设置/更改数据库用户权限,常用的关键字有grant、remove等

事务的隔离级别

在高并发情况下,并发事务会产生脏读、不可重复读、幻读问题,这时需要用隔离级别来控制

先了解下几个概念:脏读、不可重复读、幻读。

脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。

不可重复读是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,另一个事务修改了数据并提交了。

幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

读未提交: 允许一个事务读取另一个事务已提交的数据,脏读

读已提交:一个事务只能看见已经提交事务所做的改变。可避免脏读的发生

可重复读: MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,解决了不可重复读的问题。

可串行化:通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。

如何设计数据库?

(1)抽取实体,如用户信息,商品信息,评论

(2)分析其中属性,如用户信息:姓名、性别...

(3)分析表与表之间的关联关系

然后可以参考三大范式进行设计,设计主键时,主键要尽量小并且定义为自增和不可修改

mysql的视图?

视图是一种虚拟的表,具有和物理表相同的功能,可以对视图进行增、改、查操作,视图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表,它使得我们获取数据更容易,相比多表查询

Mysql内连接、左连接、右连接的区别

内连接是把匹配的关联数据显示出来,左连接是把左边表的数据显示出来,右边的表显示出符合条件的数据,右连接则相反

索引是一种特殊的文件,它们包含着对数据表里所有引用的指针

索引是一种数据结构,数据库索引是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中的数据。

通俗来说:索引就相当于目录,为了方便查找书中的内容,通过对内容建立索引形成目录,索引是个文件是要占据物理空间的。

索引有哪些优缺点?

优点:

大大加快数据的检索时间

使用索引可以在查询的过程中使用优化隐藏器,提高系统性能

缺点:

时间方面:创建索引和维护索引要耗费时间,具体的,当对表中的数据进行增删改时,索引也要动态维护,会降低增删改的执行效率

空间方面:索引需要占物理空间

索引的数据结构

索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引,默认的索引类型为B+树索引。

Hash索引和B+树索引的区别?

哈希索引不支持排序,因为哈希表是无序的。

哈希索引不支持范围查找。

哈希索引不支持模糊查询及多列索引的最左前缀匹配。

因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。

为什么B+树比B树更适合实现数据库索引?

B+数的数据储存是在叶子节点上的,叶子节点均为 索引方便扫库,B树的分支节点也同样存储数据我们要是查找数据还要进行再一次的遍历,就比较麻烦,因为在数据库中基于范围的查找都是比较频繁的所以使用B+树索引,

B+树的节点只存储索引key值,具体信息的地址存在于叶子节点的地址中。这样就可以存放更多的节点。

B+树的查询效率更加稳定,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

MySQL有哪些索引

主键索引:一张表只能有一个主键索引,主键索引列不能有空值和重复值

唯一索引:唯一索引不能有相同值,但允许为空

普通索引:允许出现重复值

组合索引:对多个字段建立一个联合索引,减少索引开销,遵循最左匹配原则

全文索引:通过建立倒排索引提升检索效率,广泛用于搜索引擎

0

哪些情况索引会失效

(1)where条件中有or,除非所有查询条件都有索引,否则失效

(2)like查询用%开头,索引失效

(3)索引列参与计算,索引失效

(4)违背最左匹配原则,索引失效

(5)索引字段发生类型转换,索引失效

(6)mysql觉得全表扫描更快时(数据少),索引失效

where和having的区别?

where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数,having可以

MySQL 锁的类型有哪些呢?

说两个维度:

共享锁(简称S锁)和排他锁(简称X锁)

读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。

写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。

表锁和行锁

表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。

行锁又可以分为乐观锁和悲观锁

悲观锁可以通过for update实现

乐观锁则通过版本号实现

说一下乐观锁和悲观锁?

数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。

悲观锁:。假设会发生并发冲突,会对操作的数据进行加锁,直到事务提交,才会释放锁,其他的事务才能对数据进行修改

实现方式:使用数据库中的锁机制

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否数据是否被修改过。给表增加version字段,在修改提交之前检查version与原来取到的version值是否相等,若相等,表示数据没有被修改,可以更新,否则,数据为脏数据,不能更新。

实现方式:乐观锁一般使用版本号机制或CAS算法实现。

什么是MySQL主从同步?

主从同步使得数据可以从一个数据库服务器复制到其他服务器上,在复制数据时,一个服务器充当主服务器(master),其余的服务器充当从服务器(slave)。

因为复制是异步进行的,所以从服务器不需要一直连接着主服务器,从服务器甚至可以通过拨号断断续续地连接主服务器。通过配置文件,可以指定复制所有的数据库,某个数据库,甚至是某个数据库上的某个表。

为什么要做主从同步?

读写分离,使数据库能支撑更大的并发。

在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能。

数据备份,保证数据的安全。

讲一下MySQL架构?

MySQL主要分为 Server 层和存储引擎层:

Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。

存储引擎: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。

Server 层基本组件

连接器: 当客户端连接 MySQL 时,server层会对其进行身份认证和权限校验。

查询缓存: 执行查询语句的时候,会先查询缓存,先校验这个 sql 是否执行过,如果有缓存这个 sql,就会直接返回给客户端,如果没有命中,就会执行后续的操作。

分析器: 没有命中缓存的话,SQL 语句就会经过分析器,主要分为两步,词法分析和语法分析,先看 SQL 语句要做什么,再检查 SQL 语句语法是否正确。

优化器: 优化器对查询进行优化,包括重写查询、决定表的读写顺序以及选择合适的索引等,生成执行计划。

执行器: 首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会根据执行计划去调用引擎的接口,返回结果。

如何做mysql性能优化?

建立合适的索引

开启慢查询日志,记录执行速度慢的sql,对慢sql语句进行优化

优化sql语句执行效率的方案:

为搜索字段(where中的查询条件)、排序字段、select查询列,创建合适的索引

尽量建立组合索引并注意组合索引的创建顺序,按照顺序组织查询条件、尽量将筛选力度大的查询条件放到左边

尽量使用覆盖索引

select语句中尽量不要使用*

order by和group by语句尽量使用到索引

where条件中尽量不要使用1=1、not in语句(推荐使用not exists)

不用mysql的内置函数,内置函数不会建立查询缓存

尽量不用count(*) 、尽量使用使用count(主键)

MyBatis

什么是Mybatis?

MyBatis是一个半ORM(对象关系映射)框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。他是为简化数据库操作而设计的。MyBatis通过配置文件或者注解即可将数据库数据与pojo实体类联系起来

Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

由于MyBatis专注于SQL本身,灵活度高,所以比较适合对性能的要求很高,或者需求变化较多的项目,如互联网项目。

什么是 半自动化(半ORM)

Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具

Mybaits的优缺点:

(1)优点:

消除jdbc中的重复代码

可以在XML或注解中直接 编写SQL语句,比较灵活,方便对SQL的优化和调整

SQL在XML中与代码解耦,按照对应关系方便管理

XML中提供了动态SQL的标签,方便根据条件拼接SQL

提供了XML、注解与Java对象的映射机制

与spring集成比较方便。

(2)缺点:

字段较多、关联表多时,编写SQL工作量较大

SQL语句依赖了数据库特性,会导致程序的移植性差,切换数据库困难

#{}和${}的区别是什么?

${}是字符串替换,#{}是预处理;使用#{}可以有效的防止SQL注入,提高系统安全性。

Mybatis在处理${}时,就是把${}直接替换成变量的值。而Mybatis在处理#0{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

实体类中的属性名和表中的字段名不一致时,怎么办?

使用查询时sql语句定义别名,让字段名的别名和实体类的属性名对应

通过<ResultMap>来映射字段名和实体类属性名一一对应

通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法重载吗?

Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象 MappedProxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。

Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

备注:在旧版本的Mybatis中,namespace是可选的,不过新版本的namespace已经是必填项了。

Mybatis是如何进行分页的?分页插件的原理是什么?

Mybatis使用RowBounds(肉棒死)对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect(带莱克特)方言,添加对应的物理分页语句和物理分页参数。

简述Mybatis的插件运行原理,以及如何编写一个插件。

答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor(赛)这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是Invoca(ken) tionHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

编写插件:实现Mybatis的Interceptor(赛)接口并复写intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association(额扫死en )关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

延迟加载通过在需要访问关联对象时才去加载它们,以提高性能和减少不必要的数据库查询。在访问关联对象之前,会检查是否已经加载,如果未加载,则会通过代理对象发送查询语句进行加载。这样可以避免不必要的关联对象查询,提升系统的效率和性能

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

Mybatis的一级、二级缓存:

一级缓存:一级缓存是sqlSession级别的,在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession'之间的缓存数据区域(HashMap)是互相不影响的,默认是开启的。

二级缓存:是Mapper级别的,也就是说可以多个sqlSession去操作同一个Mapper的sql语句,多个sqlSessin可以共用二级缓存,默认是关闭的需要在Mapper配置文件中开启配置。

需要注意的是,尽管缓存可以提高查询性能,但在某些情况下,缓存也可能导致数据一致性的问题。因此,在使用缓存时,需要根据具体的业务场景和需求来合理配置和使用缓存。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

映射形式:

第一种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。《ResultMap》

第二种是使用sql列的别名功能,将列的别名书写为对象属性名。

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

Mybatis动态sql有什么用?执行原理?有哪些动态sql?

MyBatis动态SQL是一种可以根据不同条件生成不同SQL语句的机制,它能够根据业务需求和运行时条件来动态地拼接和生成SQL语句。动态SQL提供了灵活性和可重用性,可以减少代码冗余并简化SQL语句的编写。

Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

Xml映射文件中,除了常见的select|insert|updae|delete标签外,还有哪些标签?

《ReuestMap》 《Sql》 《selectKey》

<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签 trim | where | set | foreach | if | choose | when | otherwise | bind 等,其中 <sql> 为sql片段标签,通过<include>标签引入sql片段,<selectKey>为不支持自增的主键生成策略标签。

使用MyBatis的mapper接口调用时有哪些要求?

Mapper接口方法名和mapper.xml中定义的每个sql的id相同;

Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;

Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;

Mapper.xml文件中的namespace即是mapper接口的类路径

mybatis的工作原理?

1. 读取mybatis配置文件:mybatis-config.xml 为mybatis的全局配置文件,配置了mybatis的运行环境等信息。

2. 加载映射文件。映射文件即sql文件,该文件中配置了操作数据库的SQL语句

3. 构造会话工厂:通过mybatis的环境等配置信息构建会话工厂SqlSessionFactory

4. 创建会话对象:由会话工厂创建SqlSession 对象,该对象中包含了执行sql语句的所有方法

5. Executor执行器:mybatis底层定义了一个Executor接口来操作数据库,他将根据SqlSession 传递的参数动态的生成执行的sql语句,同时负责查询缓存的维护。

6. MappedStatement 对象:在Executor接口的执行方法中有一个MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的sql语句的id、参数信息等。

7. 输入参数映射

8. 输出结果映射

MybatisPlus

Spring

什么是Spring?

Spring是一个框架也是一个容器也是也种生态

Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,提高开发人员的开发效率以及系统的可维护性。

解释一下什么是ioc?

IOC(Inversion Of Controll,控制反转)是一种设计思想,指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。

以前需要我们自己new对象,对象始终会和其他接口或类耦合起来,使用Spring IOC之后就就不需要我们自己去new对象,spring会给我们创建好对象,这就叫控制反转

DI—Dependency Injection(依赖注入):

依赖注入实现了控制反转的思想。

Spring通过依赖注入的方式来完成Bean管理的。

Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。

IOC的原理:

Spring 的 IoC 的实现原理就是工厂模式加反射机制

解释一下什么是aop?

AOP(Aspect-Oriented Programming,面向切面编程)

将与核心业务无关的代码独立的抽取出来,形成一个独立的切面,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。

减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性

Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果是代理对象并且没有实现接口的类,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。。

AOP的作用:

利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用性和开发效率

1. AOP的七大术语

    • 连接点(Joinpoint):描述的是位置 :可以指植入切面的位置
    • 切点(Pointcut):本质上是方法 :植入切面的方法
    • 通知(Advice):又叫做增强 (就是具体增强的代码 通知说的就是代码)
      • 通知包括
        • 前置通知
        • 后置通知
        • 环绕通知
        • 异常通知
        • 最终通知
    • 切面(Aspect):就是切点加通知就是切面
      • 切点+通知=切面
    • 织入(Weaving):把通知应用带目标对象的过程
    • 代理对象(Poxy):一个目标对象被植入通知后产生的新对象
    • 目标对象(Target):被织入的通知

Spring通知(Advice)有哪些类型?

常见的通知:

前置通知:方法执行前调用 对应注解@Before

后置通知:方法执行后会回调 对应注解@After

返回通知:方法返回后回调 对应注解@AfterReturning

异常通知:方法出现异常时调用 对应注解@AfterThrowing

环绕通知:动态代理、手动推荐方法运行 对应注解@Around

AOP 有哪些应用场景?举几个例子:

  • 记录日志(调用方法后记录日志)
  • 监控性能(统计方法运行时间)
  • 权限控制(调用方法前校验是否有权限)
  • 事务管理(调用方法前开启事务,调用方法后提交关闭事务 )
  • 缓存优化(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )

Spring是怎么aop自定义日志

先创建一个切面,将切面交给Spring管理

在切面中添加切点和通知(通知并通过切入点表达式来表示要对哪些方法进行日志打印)

这样既可以实现日志的打印

spring的优点?

(1)spring属于低侵入式设计,代码的污染极低;

(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

(3)Spring提供了AOP技术,可以方便地实现对程序进行权限拦截和运行监控等功能

(4)spring对于主流的应用框架提供了集成支持。

spring用了哪些设计模式

BeanFactory用了工厂模式,

AOP用了动态代理模式,

RestTemplate用来模板方法模式,

SpringMVC中handlerAdaper用来适配器模式,

Spring里的监听器用了观察者模式

循环依赖是什么,怎么解决的?


循环依赖就是在创建 A 实例的时候里面包含着 B 实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。

spring通过三级缓存来解决循环依赖:

一级缓存:缓存经过完整的生命周期的Bean

二级缓存 :缓存未经过完整的生命周期的Bean

三级缓存:缓存的是ObjectFactory,其中存储了一个生成代理类的拉姆达表达式

我们在创建 A 的过程中,先将 A 放入三级缓存 ,这时要创建B,B要创建A就直接去三级缓存中查找,并且判断需不需要进行 AOP 处理,如果需要就执行拉姆达表达式得到代理对象,不需要就取出原始对象。然后将取出的对象放入二级缓存中,因为这个时候 A 还未经 过完整的生命周期所以不能放入一级缓存。这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B创建完成,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了

Spring是如何设计程序的,BeanFactory和ApplicationContext有什么区别?

BeanFactory是spring的原始接口,针对原始结构的实现类功能比较单一,BeanFactory接口实现的容器,特点是在每次获取对象时才会创建对象。

ApplicationContext继承了BeanFactory接口,拥有BeanFactory的所有功能,并且扩展了很多高级特性,每次容器启动时就会创建所有对象。

结论:早期电脑的性能低,内存小,所以spring的容量不足,不能将所有的对象全部创建好放入容器,,所以使用的是BeanFactory,需要某个对象时,再进行创建,随着电脑硬件的发展,内存越来越大,所以spring框架就引入了ApplicationContext,将所有的对象都创建好放入容器,需要使用哪个对象时,去容器中取。

BeanFactory和FactoryBean的区别

BeanFactory

Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。

BeanFactory是工厂。

FactoryBean

FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。

在Spring中,Bean可以分为两类:

● 第一类:普通Bean

● 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)

spring中的bean是线程安全的吗?

不是,spring框架中的单例bean不是线程安全的

spring中的bean默认是单例模式,spring框架并没有对单例bean进行多线程封装

Spring Bean的生命周期?

  1. 实例化(Instantiation):在这个阶段,Spring会通过构造函数或工厂方法创建Bean的实例。
  2. 属性赋值(Population of properties):在实例化后,Spring会通过依赖注入的方式为Bean的属性设置值,包括基本类型、引用类型和集合类型等。
  3. 初始化(Initialization):在属性赋值完成后,Spring会调用Bean的初始化方法,可以自定义初始化方法。通常可以使用@PostConstruct注解或实现InitializingBean接口来定义初始化逻辑。
  4. 使用(In Use):在初始化完成后,Bean可以被容器和其他对象使用。
  5. 销毁(Destruction):在容器关闭或Bean被销毁时,Spring会调用Bean的销毁方法,可以自定义销毁方法。通常可以使用@PreDestroy注解或实现DisposableBean接口来定义销毁逻辑。

只有spring正常关闭bean销毁方法才能被调用

注:初始化方法和销毁方法配置完场后要在xml中对其方法进行调用才行,在bean标签中添加属性,调用初始化方法使用init-method,调用销毁方法使用destroy-method,并且调用销毁方法之前要进行手动销毁才能调用(close()方法)

spring支持几种bean的作用域?

单例:singleton(森钩藤):spring ioc 容器中只存在一个 bean 实例,bean 以单例模式存在,是系统默认值;

原型:prototype(噗肉投胎不):每次请求都会创建一个新的bean实例

请求request:每次 http 请求都会创建一个 bean

会话session:对于同一个 http session 共享一个 bean 实例

全局会话global session(阁楼bou):每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

spring自动装配bean有哪些方式?

隐式的bean发现机制和自动装配

在Java代码或者XML进行显示的配置

Spring 事务原理?

spring事务有编程式和声明式,我们一般@Transactional进行声明式事务,在某个方法上增加 注解,这个方法中的sql会统一成功或失败。

原理是:

当一个方法加上@Transactional注解,spring会根据这个类生成一个代理对象并将这个代理对象作为bean,当使用这个bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、用来保证数据库操作的一致性

spring事务失效场景

(1)事务方法所在的类没有加载到容器中

(2)事务方法不是public类型

(3)同一类中,一个没有添加事务的方法调用另外以一个添加事务的方法,事务不生效

(4)spring事务默认只回滚运行时异常,可以用rollbackfor属性设置

(5)业务自己捕获了异常,事务会认为程序正常秩序

spring事务的隔离级别

default:默认级别,使用数据库自定义的隔离级别

其它四种隔离级别与mysql一样

spring事务的传播行为

多个事务方法相互调用的情况下

● REQUIRED(瑞快yer的):支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】

● SUPPORTS(四炮而肆):支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】

● MANDATORY(满的特锐):必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】

● REQUIRES_NEW(瑞快yer死new):开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】

● NOT_SUPPORTED(Not四炮忒特):以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】

● NEVER(乃wer):不使用事务,如果当前事务存在,则抛出异常

● NESTED(奈斯忒特):如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】

@Autowired的作用是什么?

@Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,通过@Autowired 的使用来消除 set/get 方法

@Autowired和@Resource有什么区别?

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它的required=false)

@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入

SpringMVC

什么是Spring MVC ?简单介绍下你对springMVC的理解?

Spring MVC是基于Spring框架的一种轻量级、灵活的Web框架,用于开发Web应用程序。将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

Springmvc的优点:

(1)可以支持各种视图技术,而不仅仅局限于JSP;

(2)方便与Spring框架集成(如IoC容器、AOP等);

(3)清晰的角色分配:前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。

(4) 支持各种请求资源的映射策略

Spring MVC的工作原理是什么?

  • 客户端发送请求到前端控制器(DispatcherServlet)。
  • 前端控制器根据请求的URL找+到对应的处理器映射器(HandlerMapping)。
  • 处理器映射器根据请求的URL映射到具体的处理器(Controller)。
  • 处理器执行业务逻辑,返回ModelAndView对象。
  • 前端控制器根据返回的ModelAndView对象找到对应的视图解析器(ViewResolver)。
  • 视图解析器将视图解析为具体的视图(如JSP)。
  • 前端控制器将视图返回给客户端进行展示

Spring MVC中的核心组件是什么?

    • 前端控制器(DispatcherServlet):负责请求的分发和处理流程的控制。
    • 处理器映射器(HandlerMapping):根据请求的URL映射到具体的处理器。
    • 处理器(Controller):执行业务逻辑并返回ModelAndView对象。
    • 视图解析器(ViewResolver):将逻辑视图解析为具体的视图。
    • 视图(View):负责将处理结果渲染为HTML等格式并返回给客户端。

Spring MVC中的控制器是什么?如何定义一个控制器?

    • 控制器(Controller)是处理请求的组件,负责处理用户请求、调用业务逻辑、返回处理结果。
    • 可以通过注解(如@Controller)或实现特定接口(如Controller接口)来定义一个控制器。
    • 在控制器中可以定义多个处理方法(也称为处理器方法),每个处理方法对应一个URL请求。

SpringMVC常用的注解有哪些?

@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。

SpringMvc里面拦截器是怎么写的:

有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可:

<!-- 配置SpringMvc的拦截器 -->

<mvc:interceptors>

<!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 -->

<bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean>

<!-- 只针对部分请求拦截 -->

<mvc:interceptor>

<mvc:mapping path="/modelMap.do" />

<bean class="com.zwp.action.MyHandlerInterceptorAdapter" />

</mvc:interceptor>

</mvc:interceptors>

SpringBoot

什么是springboot?

  • Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用
    Spring 的难度,简省了繁重的配置,提供了各种启动器,使开发者能快速上手。

为什么要使用springboot?

  • 快速开发,快速整合,配置简化、内嵌服务容器

因为Spring, SpringMVC 需要使用的大量的配置文件 (xml文件)还需要配置各种对象,把使用的对象放入到spring容器中才能使用对象需要了解其他框架配置规则,SpringBoot减少了配置文件,内置了tomcat,能够独立运行

springboot有什么优点?

1. 容易上手,提升开发效率

2. 简化配置

自动配置

快速启动

5. 避免大量的maven导入和各种版本冲突

springboot自动配置原理


Spring Boot的自动配置原理是基于条件化配置和约定大于配置的原则。它会根据应用程序的环境、依赖项和属性配置来自动决定是否启用某个功能,并自动配置相应的组件。通过扫描和加载自动配置类,并根据条件判断是否应用该配置。同时,使用属性文件来配置应用程序的行为。使用启动器依赖项可以简化特定功能的引入和配置。这样可以减少开发人员的配置工作,提高开发效率和开发体验。

运行springboot有哪几种方式?

  1. 打包用命令或者放到容器中运行
  2. 用 Maven/ Gradle 插件运行
  3. 直接执行 main 方法运行

SpringBoot 中的starter?

Spring Boot的启动器(Starters)是一组预配置的依赖项,用于简化应用程序的构建和配置。这些启动器提供了一组相关的依赖项,以支持特定的功能或技术栈,例如Web应用程序、数据访问、安全性等。通过使用启动器,可以轻松地引入和配置所需的依赖项,以快速构建具有所需功能的应用程序。

Runner启动器:

可以实现接口ApplicationRunner 或者CommandLineRunner这两个接口实现方式一样,都只提供了一个run方法

springboot常用注解

@RestController :修饰类,该控制器会返回Json数据

@RequestMapping("/path") :修饰类,该控制器的请求路径

@Autowired : 修饰属性,按照类型进行依赖注入

@PathVariable : 修饰参数,将路径值映射到参数上

@ResponseBody :修饰方法,该方法会返回Json数据

@RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中

@Controller@Service@Compont: 将类注册到ioc容器

@Transaction:开启事务

Spring Boot中的监视器是什么?

Spring boot actuator(啊可图Ater)是spring启动框架中的重要功能之一。用于监控和管理应用程序的运行时状态。它提供了一组端点(endpoints),可以通过HTTP请求访问这些端点来获取应用程序的各种信息,例如健康状况、运行状态、配置信息、日志等。监视器可以帮助开发人员和运维人员更好地了解应用程序的运行情况,以及进行故障排查和性能调优等操作。同时,监视器还支持自定义的端点,可以根据需求添加额外的端点来暴露更多的应用程序信息。查状态。

如何使用springboot实现异常处理?

使用@ControllerAdvice注解实现一个ControllerAdvice类,可以统一处理控制器类抛出的所有异常。在该类中,使用@ExceptionHandler注解处理具体的异常类型,并返回相应的错误信息。这样可以简化异常处理的代码,使得处理异常变得更加方便和统一。

@Component和@Bean的区别?

@Component表明一个类会作为组件类,并告知spring要为这个类创建Bean,@Component通常是通过类路径扫描自动侦测以及自动装配到spring容器中

@Bean注解告诉spring这个方法会返回一个对象,这个对象要注册为spring应用上下文的bean,通常方法体包含了最终产生bean的实例的逻辑,并且方法名就是实例名

**总结:**@Component和@Bean都是用来注册Bean并装配到spring容器中,但Bean比Component的自定义性更强,可以实现一些Component实现不了的自定义加载类

Springboot如何解决跨域问题?

  1. 使用全局配置:在Spring Boot的配置类中添加@Bean注解的WebMvcConfigurer类型的方法,并重写其中的addCorsMappings方法,配置跨域规则。
  2. 使用注解:在Controller类或方法上添加@CrossOrigin注解,指定允许跨域的来源、方法、头部信息等。

springboot的核心配置文件?

- bootstrap(.yml 或 .properties):由AppicationContext加载,比application优先加载,配置在应用程序上下文的引导阶段生效。一般来说在spring cloud config和Nacos中会用到它,且bootstrap中的属性不能被覆盖。

- application(.yml 或 .properties):由ApplicationContext加载,用于springboot的自动化配置

SpringCloud

什么是Spring Cloud?

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

  • SpringCloud的优点
  1. 耦合度比较低。不会影响其他模块的开发。
  2. 减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。
  3. 配置比较简单,基本用注解就能实现,不用使用过多的配置文件。
  4. 微服务跨平台的,可以用任何一种语言开发。
  5. 每个微服务可以有自己的独立的数据库也有用公共的数据库。
  6. 直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。
  • SpringCloud的缺点
  1. 部署比较麻烦,给运维工程师带来一定的麻烦。
  2. 针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
  3. 系统集成测试比较麻烦
  4. 性能的监控比较麻烦。

springcloud中的组件有那些?

说出主要的组件:

  • Spring Cloud Eureka,服务注册中心,特性有失效剔除、服务保护
  • Spring Cloud Zuul,API服务网关,功能有路由分发和过滤
  • Spring Cloud Config,分布式配置中心,支持本地仓库、SVN、Git、Jar包内配置等模式
  • Spring Cloud Ribbon,客户端负载均衡,特性有区域亲和,重试机制
  • Spring Cloud Hystrix,客户端容错保护,特性有服务降级、服务熔断、请求缓存、请求合并、依赖隔离
  • Spring Cloud Feign,声明式服务调用本质上就是Ribbon+Hystrix
  • Spring Cloud Stream,消息驱动,有Sink、Source、Processor三种通道,特性有订阅发布、消费组、消息分区
  • Spring Cloud Bus,消息总线,配合Config仓库修改的一种Stream实现,
  • Spring Cloud Sleuth,分布式服务追踪,需要搞清楚TraceID和SpanID以及抽样,如何与ELK整合

具体说说SpringCloud主要项目?

Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架"Spring Boot化"的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。

  • Spring Cloud Config Config能够管理所有微服务的配置文件

集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。

  • Spring Cloud Netflix

Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。

  1. Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;
  2. Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;
  3. Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;
  4. Feign:基于Ribbon和Hystrix的声明式服务调用组件;
  5. Zuul:API网关组件,对请求提供路由及过滤功能。
  • Spring Cloud Bus
  1. 用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置信息。
  2. 简单来说就是修改了配置文件,发送一次请求,所有客户端便会重新读取配置文件(需要利用中间插件MQ)。
  • Spring Cloud Consul

Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

  • Spring Cloud Security

Spring Cloud Security提供了一组原语,用于构建安全的应用程序和服务,而且操作简便。可以在外部(或集中)进行大量配置的声明性模型有助于实现大型协作的远程组件系统,通常具有中央身份管理服务。它也非常易于在Cloud Foundry等服务平台中使用。在Spring Boot和Spring Security OAuth2的基础上,可以快速创建实现常见模式的系统,如单点登录,令牌中继和令牌交换。

  • Spring Cloud Sleuth

在微服务中,通常根据业务模块分服务,项目中前端发起一个请求,后端可能跨几个服务调用才能完成这个请求(如下图)。如果系统越来越庞大,服务之间的调用与被调用关系就会变得很复杂,假如一个请求中需要跨几个服务调用,其中一个服务由于网络延迟等原因挂掉了,那么这时候我们需要分析具体哪一个服务出问题了就会显得很困难。Spring Cloud Sleuth服务链路跟踪功能就可以帮助我们快速的发现错误根源以及监控分析每条请求链路上的性能等等。

  • Spring Cloud Stream

轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。

  • Spring Cloud Task

Spring Cloud Task的目标是为Spring Boot应用程序提供创建短运行期微服务的功能。在Spring Cloud Task中,我们可以灵活地动态运行任何任务,按需分配资源并在任务完成后检索结果。Tasks是Spring Cloud Data Flow中的一个基础项目,允许用户将几乎任何Spring Boot应用程序作为一个短期任务执行。

  • Spring Cloud Zookeeper
  1. SpringCloud支持三种注册方式Eureka, Consul(go语言编写),zookeeper
  2. Spring Cloud Zookeeper是基于Apache Zookeeper的服务治理组件。
  • Spring Cloud Gateway

Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。

  • Spring Cloud OpenFeign

Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。要使用Feign,我们可以将调用的服务方法定义成抽象方法保存在本地添加一点点注解就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

服务注册和发现是什么意思?Spring Cloud 如何实现?

当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka 服务注册和发现可以在这种情况下提供帮助。由于所有服务都在 Eureka 服务器上注册并通过调用 Eureka 服务器完成查找,因此无需处理服务地点的任何更改和处理。

什么是Eureka?

Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过Eureka Service来监控各个微服务是否运行正常。

Eureka怎么实现高可用?

集群吧,注册多台Eureka,然后把SpringCloud服务互相注册,客户端从Eureka获取信息时,按照Eureka的顺序来访问。

什么是Eureka的自我保护模式?

默认情况下,如果Eureka Service在一定时间内没有接收到某个微服务的心跳,Eureka Service会进入自我保护模式,在该模式下Eureka Service会保护服务注册表中的信息,不再删除注册表中的数据,当网络故障恢复后,Eureka Servic 节点会自动退出自我保护模式DiscoveryClient的作用?

可以从注册中心中根据服务别名获取注册的服务器信息。

什么是网关?

网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。

网关的作用是什么?

统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等

什么是Spring Cloud Zuul(服务网关)?

Zuul是对SpringCloud提供的成熟对的路由方案,他会根据请求的路径不同,网关会定位到指定的微服务,并代理请求到不同的微服务接口,他对外隐蔽了微服务的真正接口地址。

  • 三个重要概念:动态路由表,路由定位,反向代理:
    • 动态路由表:Zuul支持Eureka路由,手动配置路由,这俩种都支持自动更新
    • 路由定位:根据请求路径,Zuul有自己的一套定位服务规则以及路由表达式匹配
    • 反向代理:客户端请求到路由网关,网关受理之后,在对目标发送请求,拿到响应之后在 给客户端
  • 它可以和Eureka,Ribbon,Hystrix等组件配合使用,
  • Zuul的应用场景:
    • 对外暴露,权限校验,服务聚合,日志审计等

网关与过滤器有什么区别?

网关是对所有服务的请求进行分析过滤,过滤器是对单个服务而言。

常用网关框架有那些?

Nginx、Zuul、Gateway

既然Nginx可以实现网关?为什么还需要使用Zuul框架?

Zuul是SpringCloud集成的网关,使用Java语言编写,可以对SpringCloud架构提供更灵活的服务。

  • filterType():过滤器拦截位置

Ribbon是什么?

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法

Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单的说,就是在配置文件中列出后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。(有点类似Nginx)

Nginx与Ribbon的区别?

Nginx是反向代理同时可以实现负载均衡,nginx拦截客户端请求采用负载均衡策略根据upstream配置进行转发,相当于请求通过nginx服务器进行转发。Ribbon是客户端负载均衡,从注册中心读取目标服务器信息,然后客户端采用轮询策略对服务直接访问,全程在客户端操作。

Ribbon底层实现原理?

Ribbon使用discoveryClient从注册中心读取目标服务信息,对同一接口请求进行计数,使用%取余算法获取目标服务集群索引,返回获取到的目标服务信息。

@LoadBalanced注解的作用?

开启客户端负载均衡。

什么是断路器

当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)

断路器有三种状态

  • 打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务
  • 半开状态:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭
  • 关闭状态:当服务一直处于正常状态 能正常调用

什么是 Hystrix?

在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,就会导致雪崩,Hystrix就是这样的一个工具,防雪崩利器,它具有服务降级,服务熔断,服务隔离,监控等一些防止雪崩的技术。

Hystrix有四种防雪崩方式:

  • 服务降级:接口调用失败就调用本地的方法返回一个空
  • 服务熔断:接口调用失败就会进入调用接口提前定义好的一个熔断的方法,返回错误信息
  • 服务隔离:隔离服务之间相互影响
  • 服务监控:在服务发生调用时,会将每秒请求数、成功请求数等运行指标记录下来。

什么是Feign?

Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易

他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

SpringCloud有几种调用接口方式?

· Feign

· RestTemplate

Ribbon和Feign调用服务的区别?

调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐

而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

什么是Spring Cloud Gateway?

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。

使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由RouteLocatorBuilder可以让你添加各种predicates和filters,predicates断言的意

Redis

redis为什么这么快?

1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。

2. 数据结构简单,对数据操作也简单

3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在

说说你对redis的了解?

首先redis是基于c语言编写的,而且是内存中的数据库,读写速度很快。在项目中也会经常的使用redis,用来做缓存、分布式锁、也可以用来设计消息队列、同时还支持事务、持久化、多种集群方案。

说说Redis的基本数据结构类型

大多数小伙伴都知道,Redis有以下这五种基本类型:

· String(字符串)

· Hash(哈希)

· List(列表)

· Set(集合)

· zset(有序集合)

redis为什么是单线程的性能依然很好?

- 避免了线程切换的资源损耗

- 单线程避免了资源共享与竞争,不用考虑锁的问题

- 基于内存的,内存的读写速度非常快

- 使用非阻塞的IO多路复用机制

- 数据存储进行了压缩优化

- 使用了高性能的数据结构,如hash、跳表等

redis本身就是基于内存的,如果我的数据全部在内存中,单线程操作就是效率最高的。因为多线程的本质就是cpu模拟出来多个线程的情况,这种模拟出来的情况就有个代价,就是上下文切换,对于一个内存系统来说,没有上下文切换的效率是最高的。一次上下文切换大概在1500ns(纳秒)左右,假设在内存中读取1MB的连续数据耗时大约250us(微秒),假设由多个线程读取了1000次,那么就有1000次的上下文切换,就有1500ns*1000=1500us,单线程执行250us,多线程上下文切换就用了1500us,

为什么redis所有的数据都放在内存中?

使之具有高速读写和数据持久化的特征

如果程序直接与磁盘交互,磁盘IO速度会严重影响redis的性能

内存硬件成本低

什么是热Key问题,如何解决热key问题

什么是热Key?在Redis中,我们把访问频率高的key,称为热点key。

如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。

而热点Key是怎么产生的呢?主要原因有两个:

· 用户消费的数据远大于生产的数据,如秒杀、热点新闻等读多写少的场景。

· 请求分片集中,超过单Redi服务器的性能,比如固定名称key,Hash落入同一台服务器,瞬间访问量极大,超过机器瓶颈,产生热点Key问题。

如何解决热key问题?

· Redis集群扩容:增加分片副本,均衡读流量;

· 将热key分散到不同的服务器中;

· 使用二级缓存,即JVM本地缓存,减少Redis的读请求。

什么是缓存穿透、缓存击穿、缓存雪崩?怎么解决?

**缓存穿透:**

请求数据库中根本不存在的数据,数据库中不存在,缓存中就更加不存在,导致每次请求直接怼到数据库,数据库压力过大;

**解决:**

- 缓存空值,并设置过期时间小一点,不然会对数据的准确性造成影响

- 对用户请求进行合法校验

- 布隆过滤器

**缓存击穿:**

请求了很多缓存中不存在但数据库存在的数据,一般是缓存过期导致的,也导致请求直接怼到数据库;

**解决:**

- 对于比较热点的数据,我们可以在缓存中设置永不过期,也可以在访问数据的时候,在缓存中更新这些数据的过期时间,如果是批量入库的缓存项,我们可以为这些缓存项分配合理的过期时间,避免同意时刻失效

- 使用分布式锁,保证每个key同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,其他线程没有获得分布式锁的权限,需要进行等待。不过在高并发的情况下,这种方案对分布式锁的访问压力比较大

**缓存雪崩:**

缓存大面积的失效

**解决:**

- 使用集群来确保服务器实例在任何时间都处于服务状态,(搭建redis集群)

- 让失效的时间点不分布在同一时间点

Redis 的持久化机制有哪些?优缺点说说

Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。

Redis提供了RDBAOF两种持久化机制

RDB,就是把内存数据以快照的形式保存到磁盘上。

RDB 的优点:

适合大规模的数据恢复场景,如备份,全量复制等

RDB缺点

没办法做到实时持久化/秒级持久化。

新老版本存在RDB格式兼容问题

AOFappend only file 持久化,采用日志的形式来记录每个写操作,追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题。默认是不开启的。

AOF的优点

· 数据的一致性和完整性更高

AOF的缺点

· AOF记录的内容越多,文件越大,数据恢复变慢。

Redis集群方案

(1)主从模式:个master节点,多个slave节点,master节点宕机slave自动变成主节点

(2)哨兵模式:在主从集群基础上添加哨兵节点或哨兵集群,用于监控master节点健康状态,通过投票机制选择slave成为主节点

(3)分片集群:主从模式和哨兵模式解决了并发读的问题,但没有解决并发写的问题,因此有了分片集群。分片集群有多个master节点并且不同master保存不同的数据,master之间通过ping相互监测健康状态。客户端请求任意一个节点都会转发到正确节点,因为每个master都被映射到0-16384个插槽上,集群的key是根据key的hash值与插槽绑定

Redis集群主从同步原理

主从同步第一次是全量同步:slave第一次请求master节点会根据replid判断是否是第一次同步,是的话master会生成RDB发送给slave。

后续为增量同步:在发送RDB期间,会产生一个缓存区间记录发送RDB期间产生的新的命令,slave节点在加载完后,会持续读取缓存区间中的数据

redis常见的性能问题有哪些?该如何解决?

锁未被释放

B锁被A锁释放了

数据库事务超时

锁过期了,业务还没执行完

redis的主从复制问题

使用过Redisson嘛?说说它的原理

分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释

聊聊Redis 事务机制

Redis通过MULTI、EXEC、WATCH等一组命令集合,来实现事务机制。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

简言之,Redis事务就是顺序性、一次性、排他性的执行一个队列中的一系列命令。

Redis执行事务的流程如下:

开始事务(MULTI)

命令入队

执行事务(EXEC)、撤销事务(DISCARD

Redis分布式锁的实现原理

原理是使用setnx+setex命令来实现,但是会有一系列问题:

(1)任务时常超过缓存时间,锁自动释放。可以使用Redision看门狗解决

(2)加锁和释放锁的不是同一线程。可以在Value中存入uuid,删除时进行验证。但是要注意验证锁和删除锁也不是一个原子性操作,可以用lua脚本使之成为原子性操作

(3)不可重入。可以使用Redision解决(实现机制类似AQS,计数)

(4)redis集群下主节点宕机导致锁丢失。使用红锁解决

redis怎么实现分布式锁?

使用SETNX命令:利用Redis的SETNX命令(SET if Not eXists),可以将一个键设置为锁,只有在键不存在时才能成功设置。多个线程或进程尝试使用SETNX命令来争夺锁,只有一个线程能成功设置键,即获取到了锁。

设置锁的过期时间:在使用SETNX命令成功设置锁后,可以通过设置键的过期时间来防止锁的长时间占用。可以使用Redis的EXPIRE命令为锁设置一个合适的过期时间,确保在一定时间后锁会自动释放。

释放锁:当线程完成了对共享资源的操作后,需要手动释放锁。可以使用Redis的DEL命令将锁的键删除,以释放锁供其他线程使用。

redis分布式锁有什么缺陷?

分布式锁不能解决超时的问题,分布式锁有一个是超时时间,程序的执行如果超出了锁的超时时间就会出问题

redis如何做内存优化?

缩减键值对象:满足业务要求下key越短越好;value值进行适当压缩

共享对象池:

尽可能使用散列表

编码优化控制编码类型

控制key的数量

有哪些应用场景?

Redis 在互联网产品中使用的场景实在是太多太多,这里分别对 Redis 几种数据类型做了整理:

1)String:缓存、限流、分布式锁、计数器、分布式 Session 等。

2)Hash:用户信息、用户主页访问量、组合查询等。

3)List:简单队列、关注列表时间轴。

4)Set:赞、踩、标签等。

5)ZSet:排行榜、好友关系链表。

Nginx

什么是Nginx?

Nginx是一个 轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用nginx网站用户有很多,例如:新浪、网易、 腾讯等。

为什么要用Nginx?

跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,

而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。

使用Nginx的话还能:

节省宽带:支持GZIP压缩,可以添加浏览器本地缓存

稳定性高:宕机的概率非常小

接收用户请求是异步的

为什么Nginx性能这么高?

因为他的事件处理机制:异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决

Nginx怎么处理请求的?

nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址

server { # 第一个Server区块开始,表示一个独立的虚拟主机站点

listen 80; # 提供服务的端口,默认80

server_name localhost; # 提供服务的域名主机名

location / { # 第一个location区块开始

root html; # 站点的根目录,相当于Nginx的安装目录

index index.html index.htm; # 默认的首页文件,多个用空格分开

} # 第一个location区块结果

什么是正向代理和反向代理?

正向代理就是一个人发送一个请求直接就到达了目标的服务器

反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规 则分发给了后端的业务处理服务器进行处理了

使用“反向代理服务器的优点是什么?

反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。

Nginx的优缺点?

优点:

占内存小,可实现高并发连接,处理响应快

可实现http服务器、虚拟主机、方向代理、负载均衡

Nginx配置简单

可以不暴露正式的服务器IP地址

缺点:

动态处理差:nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,

Nginx应用场景?

http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。

虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。

反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。

nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。

Nginx配置文件nginx.conf有哪些属性模块?

worker_processes 1; # worker进程的数量

events { # 事件区块开始

worker_connections 1024; # 每个worker进程支持的最大连接数

} # 事件区块结束

http { # HTTP区块开始

include mime.types; # Nginx支持的媒体类型库文件

default_type application/octet-stream; # 默认的媒体类型

sendfile on; # 开启高效传输模式

keepalive_timeout 65; # 连接超时

server { # 第一个Server区块开始,表示一个独立的虚拟主机站点

listen 80; # 提供服务的端口,默认80

server_name localhost; # 提供服务的域名主机名

location / { # 第一个location区块开始

root html; # 站点的根目录,相当于Nginx的安装目录

index index.html index.htm; # 默认的首页文件,多个用空格分开

} # 第一个location区块结果

error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户

location = /50x.html { # location区块开始,访问50x.html

root html; # 指定对应的站点目录为html

}

}

......

Nginx静态资源?

静态资源访问,就是存放在nginx的html页面,我们可以自己编写

如何用Nginx解决前端跨域问题?

使用Nginx转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。

Nginx虚拟主机怎么配置?

1、基于域名的虚拟主机,通过域名来区分虚拟主机——应用:外部网站

2、基于端口的虚拟主机,通过端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台

3、基于ip的虚拟主机。

基于虚拟主机配置域名

需要建立/data/www /data/bbs目录,windows本地hosts添加虚拟机ip地址对应的域名解析;对应域名网站目录下新增index.html文件;

#当客户端访问www.lijie.com,监听端口号为80,直接跳转到data/www目录下文件

server {

listen 80;

server_name www.lijie.com;

location / {

root data/www;

index index.html index.htm;

}

}

#当客户端访问www.lijie.com,监听端口号为80,直接跳转到data/bbs目录下文件

server {

listen 80;

server_name bbs.lijie.com;

location / {

root data/bbs;

index index.html index.htm;

}

}

基于端口的虚拟主机

使用端口来区分,浏览器使用域名或ip地址:端口号 访问

#当客户端访问www.lijie.com,监听端口号为8080,直接跳转到data/www目录下文件

server {

listen 8080;

server_name 8080.lijie.com;

location / {

root data/www;

index index.html index.htm;

}

}

#当客户端访问www.lijie.com,监听端口号为80直接跳转到真实ip服务器地址 127.0.0.1:8080

server {

listen 80;

server_name www.lijie.com;

location / {

proxy_pass http://127.0.0.1:8080;

index index.html index.htm;

}

}

location的作用是什么?

location指令的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作。

location的语法能说出来吗?

注意:~ 代表自己输入的英文字母

匹配符 匹配规则 优先级

= 精确匹配 1

^~ 以某个字符串开头 2

~ 区分大小写的正则匹配 3

~* 不区分大小写的正则匹配 4

!~ 区分大小写不匹配的正则 5

!~* 不区分大小写不匹配的正则 6

/ 通用匹配,任何请求都会匹配到 7

Location正则案例

示例:

#优先级1,精确匹配,根路径

location =/ {

return 400;

}

#优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写

location ^~ /av {

root /data/av/;

}

#优先级3,区分大小写的正则匹配,匹配/media*****路径

location ~ /media {

alias /data/static/;

}

#优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里

location ~* .*\.(jpg|gif|png|js|css)$ {

root /data/av/;

}

#优先7,通用匹配

location / {

return 403;

}

限流怎么做的?

Nginx限流就是限制用户请求速度,防止服务器受不了

限流有3种

正常限制访问频率(正常流量)

突发限制访问频率(突发流量)

限制并发连接数

Nginx的限流都是基于漏桶流算法,底下会说道什么是桶铜流

实现三种限流算法

1、正常限制访问频率(正常流量):

限制一个用户发送的请求,我Nginx多久接收一个请求。

Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,限制的原理实质是基于漏桶算法原理来实现的。在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制单个IP的请求处理频率。

#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;

#绑定限流维度

server{

location/seckill.html{

limit_req zone=zone;

proxy_pass http://lj_seckill;

}

}

1r/s代表1秒一个请求,1r/m一分钟接收一个请求, 如果Nginx这时还有别人的请求没有处理完,Nginx就会拒绝处理该用户请求。

2、突发限制访问频率(突发流量):

限制一个用户发送的请求,我Nginx多久接收一个。

上面的配置一定程度可以限制访问频率,但是也存在着一个问题:如果突发流量超出请求被拒绝处理,无法处理活动时候的突发流量,这时候应该如何进一步处理呢?Nginx提供burst参数结合nodelay参数可以解决流量突发的问题,可以设置能处理的超过设置的请求数外能额外处理的请求数。我们可以将之前的例子添加burst参数以及nodelay参数:

#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;

#绑定限流维度

server{

location/seckill.html{

limit_req zone=zone burst=5 nodelay;

proxy_pass http://lj_seckill;

}

}

1

为什么就多了一个 burst=5 nodelay; 呢,多了这个可以代表Nginx对于一个用户的请求会立即处理前五个,多余的就慢慢来落,没有其他用户的请求我就处理你的,有其他的请求的话我Nginx就漏掉不接受你的请求

3、 限制并发连接数

Nginx中的ngx_http_limit_conn_module模块提供了限制并发连接数的功能,可以使用limit_conn_zone指令以及limit_conn执行进行配置。接下来我们可以通过一个简单的例子来看下:

http {

limit_conn_zone $binary_remote_addr zone=myip:10m;

limit_conn_zone $server_name zone=myServerName:10m;

}

server {

location / {

limit_conn myip 10;

limit_conn myServerName 100;

rewrite / http://www.lijie.net permanent;

}

}

上面配置了单个IP同时并发连接数最多只能10个连接,并且设置了整个虚拟服务器同时最大并发数最多只能100个链接。当然,只有当请求的header被服务器处理后,虚拟服务器的连接数才会计数。刚才有提到过Nginx是基于漏桶算法原理实现的,实际上限流一般都是基于漏桶算法和令牌桶算法实现的。接下来我们来看看两个算法的介绍:

漏桶流算法和令牌桶算法知道?

漏桶算法

漏桶算法是网络世界中流量整形或速率限制时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。也就是我们刚才所讲的情况。漏桶算法提供的机制实际上就是刚才的案例:突发流量会进入到一个漏桶,漏桶会按照我们定义的速率依次处理请求,如果水流过大也就是突发流量过大就会直接溢出,则多余的请求会被拒绝。所以漏桶算法能控制数据的传输速率。

令牌桶算法

令牌桶算法是网络流量整形和速率限制中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。Google开源项目Guava中的RateLimiter使用的就是令牌桶控制算法。令牌桶算法的机制如下:存在一个大小固定的令牌桶,会以恒定的速率源源不断产生令牌。如果令牌消耗速率小于生产令牌的速度,令牌就会一直产生直至装满整个令牌桶。

为什么要做动静分离?

Nginx是当下最热的Web容器,网站优化的重要点在于静态化网站,网站静态化的关键点则是是动静分离,动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们则根据静态资源的特点将其做缓存操作。

让静态的资源只走静态资源服务器,动态的走动态的服务器

Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术。

对于静态资源比如图片,js,css等文件,我们则在反向代理服务器nginx中进行缓存。这样浏览器在请求一个静态资源时,代理服务器nginx就可以直接处理,无需将请求转发给后端服务器tomcat。

若用户请求的动态文件,比如servlet,jsp则转发给Tomcat服务器处理,从而实现动静分离。这也是反向代理服务器的一个重要的作用。

Nginx怎么做的动静分离?

只需要指定路径对应的目录。location/可以使用正则表达式匹配。并指定对应的硬盘中的目录。如下:(操作都是在Linux上)

location /image/ {

root /usr/local/static/;

autoindex on;

}

创建目录

mkdir /usr/local/static/image

1

进入目录

cd /usr/local/static/image

1

放一张照片上去#

1.jpg

1

重启 nginx

sudo nginx -s reload

1

打开浏览器 输入 server_name/image/1.jpg 就可以访问该静态图片了

Nginx负载均衡的算法怎么实现的?策略有哪些?

为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。

Nginx负载均衡实现的策略有以下五种:

1 轮询(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。

upstream backserver {

server 192.168.0.12;

server 192.168.0.13;

}

2 权重 weight

weight的值越大分配

到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。


upstream backserver {

server 192.168.0.12 weight=2;

server 192.168.0.13 weight=8;

}

权重越高,在被访问的概率越大,如上例,分别是20%,80%。

3 ip_hash( IP绑定)

每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题

upstream backserver {

ip_hash;

server 192.168.0.12:88;

server 192.168.0.13:80;

}

4 fair(第三方插件)

必须安装upstream_fair模块。

对比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。

upstream backserver {

server server1;

server server2;

fair;

}

哪个服务器的响应速度快,就将请求分配到那个服务器上。

5、url_hash(第三方插件)

必须安装Nginx的hash软件包

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。

upstream backserver {

server squid1:3128;

server squid2:3128;

hash $request_uri;

hash_method crc32;

}

Nginx配置高可用性怎么配置?

当上游服务器(真实访问服务器),一旦出现故障或者是没有及时相应的话,应该直接轮训到下一台服务器,保证服务器的高可用

Nginx配置代码:


server {

listen 80;

server_name www.lijie.com;

location / {

### 指定上游服务器负载均衡服务器

proxy_pass http://backServer;

###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间

proxy_connect_timeout 1s;

###nginx发送给上游服务器(真实访问的服务器)超时时间

proxy_send_timeout 1s;

### nginx接受上游服务器(真实访问的服务器)超时时间

proxy_read_timeout 1s;

index index.html index.htm;

}

}

Nginx怎么判断别IP不可访问?

# 如果访问的ip地址为192.168.9.115,则返回403

if ($remote_addr = 192.168.9.115) {

return 403;

}

怎么限制浏览器访问?

不允许谷歌浏览器访问 如果是谷歌浏览器返回500

if ($http_user_agent ~ Chrome) {

return 500;

}

Rewrite全局变量是什么?

变量 含义

$args 这个变量等于请求行中的参数,同$query_string

$content length 请求头中的Content-length字段。

$content_type 请求头中的Content-Type字段。

$document_root 当前请求在root指令中指定的值。

$host 请求主机头字段,否则为服务器名称。

$http_user_agent 客户端agent信息

$http_cookie 客户端cookie信息

$limit_rate 这个变量可以限制连接速率。

$request_method 客户端请求的动作,通常为GET或POST。

$remote_addr 客户端的IP地址。

$remote_port客户端的端口。

$remote_user已经经过Auth Basic Module验证的用户名。

$request_filename当前请求的文件路径,由root或alias指令与URI请求生成。

$schemeHTTP方法(如http,https)。

$server_protocol 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。

$server_addr 服务器地址,在完成一次系统调用后可以确定这个值。

$server_name服务器名称。

$server_port 请求到达服务器的端口号。

$request_uri 包含请求参数的原始URI,不包含主机名,如”/foo/bar.php?arg=baz”。

$uri不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。

$document_uri 与$uri相同。

Linux

linux常用命令

ifconfig:查看网络接口详情

ping:查看与某主机是否能联通

ps -ef|grep 进程名称:查看进程号

lost -i 端口 :查看端口占用情况

top:查看系统负载情况,包括系统时间、系统所有进程状态、cpu情况

free:查看内存占用情况

kill:正常杀死进程,发出的信号可能会被阻塞

kill -9:强制杀死进程,发送的是exit命令,不会被阻塞

  • 5
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值