整理了一些比较基础的面试题,一方面是为了自己以后时常复习,同时也是为了一些准备面试的开发小伙伴所准备的,如一些方面不完全不吝赐教,希望大家都能找到满意工作。
一、JAVA基础
1、JRE和JDK
- JRE(Java Runtime Enviroment) 是 Java 的运行环境,JRE 是运行 Java 程序所必须环境的集合,包含 JVM 标准实现及 Java 核心类库。它包括 Java 虚拟机、Java 平台核心类和支持文件
- JDK(Java Development Kit) 是 Java 开发工具包,它提供了 Java 的开发环境(提供了编译器 javac 等工具,用于将 java 文件编译为 class 文件)和运行环境(提 供了 JVM 和 Runtime 辅助包,JDK 是整个 Java 的核心
2、Java有哪些数据类型
byte,short,int,long,float,double,char,boolean
3、== 和 equals 的区别是什么?
==和equals都是我们平时开发中用来比较对象的,首先对于基本类型来说 == 比较的是值,对于引用类型来说 == 比较的是对象的地址, equals的话如果我们不对它进行重写 equals和 ==效果一致
4、为什么重写equals一定要重写hashcode?
假设来说,我们对equals进行重写,没有重写hashCode,就有可能导致 a.equals(b)这个表达式成立,但是 hashCode 却不同。在散列集合中使用hashCode来计算key的存储位置,两个相同的对象存在hash表中不同的位置,当我们根据对象获取数据时,就会出现两个相同对象在hash不同位置,这样出现错误。
hashcode规定:
- 两个对象相等,hashcode一定相等
- 两个对象不等,hashcode不一定不等
- hashcode相等,两个对象不一定相等
- hashcode不等,两个对象一定不等
5、final的作用?
- 被final修饰的类不能被继承;
- 被final修饰的方法不能被重写;
- 被final修饰的变量不能被更改;
6、String,StrngBuffer和StringBuilder的区别?
首先String是不可变的,对String进行大量的操作都会创建新的String对象,占用空间,所以推出了可变的StringBuilder和StrngBuffer,他俩的区别在于StrngBuffer是线程安全的,但是速度不如StringBuilder
7、JAVA中的IO流有哪些?
- 按流划分,分为输入流和输出流
- 按单位划分,分为字节流和字符流
inputStream,outputStream,reader,write;
8、BIO,NIO,AIO的区别?
- BIO:同步并阻塞,一个连接对应一个线程,即客户端发送一个连接,服务器就需要一个线程去处理,如果连接数超过了线程数就需要等待线程,就会发生阻塞。
- NIO:同步非阻塞,和BIO不同在于NIO一个线程可以处理多个连接,客户端发送的连接都会注册到复用器上,然后轮询连接,有请求就处理。
- AIO:异步非阻塞,异步IO是基于事件和回调机制实现的,应用操作完成后直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程去进行后续操作。
9、什么是反射?
动态获取类的信息以及动态调用对象的方法这就是反射
10、什么是序列化?
- 序列化他首先是用来处理对象流的机制。它将对象进行内容流化,传输入网络之间。
- 可以将序列化对象写入文件,并从文件中读取出来,对它进行反序列化,这样的话对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
- 整个过程 Java 虚拟机(JVM)都是独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象
11、面向对象三大特性
- 封装:封装就是将对象的属性进行私有化,并提供给外界访问属性的方法,封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
- 继承:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法。
- 多态:多态是同一个行为具有多个不同表现形式或形态的能力。
12、抽象和普通类的区别
- 抽象类可以有抽象方法,普通类没有
- 抽象类不能进行实例化,普通类可以
13、抽象类和接口的区别
14、重载和重写的区别?
- 重载:一个类中有多个同名方法,但是多个方法参数列表不同。
- 重写:发生在与子类和父类之间,子类对父类方法进行重写,参数类型必须和父类相同,返回值类型可以不同。
15、throw 和 throws 的区别?
- throw:作用于方法内,throw是抛出具体问题对象
- throws:作用在方法上,throws是用来声明异常
16、try-catch-finally 中,如果 catch 中 return 了, finally 还会执行吗?
答:会执行,在 return 前执行。
17、java常见的异常有哪些?
编译时异常 |
运行时异常 |
IOException:输入输出流异常 |
RuntimeException:运行时异常 |
FileNotFoundException:文件找不到的异常 |
NullPointerException:空指针异常 |
SQLException:数据库操作异常 |
ClassCastException:类型转换异常 |
ClassNotFoundException:类找不到的异常 |
ArrayIndexOutofBoundsException:数组越界异 |
18、java常见集合有哪些?
- List
- Map
- HashMap
- Set
19、ArrayList和LinkedList的区别?
- ArrayList:ArrayList是底层是动态数组,ArrayList查询快
- LinkedList:LinkedList底层是链表,LinkedList增删快
20、ArrayList和 Vector的区别?
- ArrayList:ArrayList线程是不安全的,性能快
- Vector:Vector使用了Synchronized来实现线程同步,是线程安全的,性能低
21、如何实现数组和List之间的转换?
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
String[] str1 = list.toArray(new String[list.size()]);
for (String s : str1) {
System.out.println(s);
}
String[] strs = {"AAA","BBB","CCC"};
List<String> strList = Arrays.asList(strs);
for (String s : strList) {
System.out.println(s);
}
22、如何实现两个列表合并成一个列表?
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
List<String> list2 = new ArrayList<String>();5
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.addAll(list2);
23、 HashMap 和 Hashtable 有什么区别?
- HashMap:HashMap是线程不安全的,HashMap可以存储为null的key和value
- HashTable:HashTable是线程安全的,HashTable不可以存储为null
24、 HashMap实现原理?
- 首先HashMap是由hash算法实现的,我们通过HashMap的put()方法将key和value存放在HashMap中,HashMap会根据HashCode()方法计算Key得出hash值,通过hash算法,将hash值转换成下标,该下标位置上没有元素,就将数据存储进去,如果下标下存有链表,此时就会用key值和链表上每个节点的key值进行equal比较,如果equal返回fasle,那么数据就会被添加到链表的末尾,如果返回true,那么数据就会被覆盖。
- HashMap在1.8之前采用 数组和链表实现的,1.8以后采用数组,链表,红黑树实现 当链表长度超过8数组长度超过64就会采用红黑树存储
25、 遍历HashMap的方法有哪些?
- 第一种方法:通过map.keySet()获取map中所有的key值,再通过map.values()获取map中所有的value值
- 第二种方法使用map.entrySet()后遍历
- 使用java8新特性Lambda表达式遍历
26、 线程安全集合有哪些?
- Vetor
- HashTable
- ConcurrentHashMap
- CopyOnWriteArrayList
- CopyOnWriteSet
27、 冒泡排序?
面试的时候遇到过手写一次,所以列出来了
int arr[] = {9,8,4,1,5,2,7};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-i-1;j++) {
if(arr[j]>arr[j+1]){
int a = arr[j+1];
arr[j+1] = arr[j];
arr[j]=a;
}
}
}
28、 HashMap和ConcurrentHashMap区别?
主要区别在于HashMap不安全ConcurrentHashMap线程安全
29、ConcurrentHashMap的数据结构?
- 在java1.7时ConcurrentHashMap是由segment和HashEntry数组结构构成,即ConcurrentHashMap将数据分割成一段一段的存储,让后给每一段分配一个锁,当线程占用某一段数据时,其他线程也可正常访问其他数据
- 在java1.8以后ConcurrentHashMap采用数组+链表+红黑树,采用CAS+synchronized保证线程安全。
30、浅拷贝和深拷贝的区别?
数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈中;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。
- 浅拷贝:对于基本数据类型,直接拷贝值。对于引用数据类型,只复制对象的引用地址,新对象和旧对象引用同一个地址,修个地址中的值,新旧对象都会改变。
- 深拷贝:对于基本数据类型,直接拷贝值。对于引用数据类型,它开辟一个新的内存空间,在新内存空间里复制一个一摸一样的对象,修个其中一个对象值,不影响其他对象。
浅拷贝要比深拷贝花销小,速度快。
31、String为什么不可变?
String的成员数组被final进行修饰并访问权限是私有的,String没有暴力和提供任何修改数组的方法,一些对字符串操作的方法都是返回的新String对象,String类上还被final修饰,也就表示了该类不可能被继承从而杜绝了子类覆盖父类方法的可能。
32、HashMap是如何解决hash冲突的?
- HashMap底层采用数组,默认为16,两个不同的hash值的key,最终落到同一个下表,这时候HashMap会引用链表来解决hash冲突,将key值插入链表末端,当链表长度大于8数组长度大于64,HashMap会将链表转为红黑树
- 再hash法,当产生hash冲突时,采用另外一个hash进行计算
- 建立公共溢出区,将存在hash冲突的key统一放在公共溢出区
二、多线程
1、进程和线程的区别?
- 进程:内存中运行的应用程序,一个进程都有自己独立的内存空间,一个进程可以有多个线程.
- 线程:进程中的执行单元,负责进程中的程序执行。
2、并发和并行的区别?
- 并发:系统中只有一个cpu,它不能同时进行一个以上的线程处理,它只能将cup的运行时间分成若干个时间段,再将时间段分配给各个线程去执行,这就是并发。
- 并行:系统中有多个cpu,每一个cpu执行一个线程,多个线程互相不影响,这就是并行。
3、线程的生命周期?
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
- 新建状态:使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。
- 就绪状态:当线程对象调用了start()方法之后,线程进入就绪状态。
- 运行状态:线程获取cpu资源,开始执行run()方法,线程进入运行状态。
- 阻塞状态:当线程执行了sleep(),wait()等方法,线程失去了资源占有权,线程进入阻塞状态。
- 死亡状态:当运行状态下的线程完成任务或者被终止条件发生时候,线程进入死亡状态。
阻塞状态细说的化他还分为三种:
- 等待阻塞:运行状态下线程执行了wait()方法,线程进入等待阻塞状态。
- 同步阻塞:线程获取synchronized同步锁失败的时候,线程进入同步阻塞状态。
- 其他阻塞:线程调用sleep()方法或者join()方法,线程进入到阻塞状态。当sleep()状态超时,join()等待线程终止或超时,线程会重新回到就绪状态
4、线程的创建方法?
java一共提供三种方式创建线程
- 实现Runnable接口
- 继承Thread类
- 实现 Callable 接口,并实现 call() 方法
5、notify()和notifyAll()之间的区别?
- notify():notify()方法执行后只会唤醒一个线程。
- notifyAll():notifyAll()方法执行后会唤醒所有线程。
6、多线程中sleep与wait的区别是什么?
- sleep方法不会释放锁,wait方法会释放锁。
- sleep不需要被唤醒,wait需要被唤醒。
- sleep是线程方法,wait是Object方法。
7、为什么我们再执行run()方法前会调用start()方法,为什么不直接调用run()?
- 直接执行run()方法的而不执行start()方法,这样的话run()方法就会被当做一个普通方法去执行,并不会在某个线程中执行。
8、线程调度算法是什么?
其实线程调度就是按照特定机制为多个线程分配CPU的使用权
9、线程调度算法两种模式?
- 分时调度:分时调度就是让线程轮流获取cpu使用权,并且平均分配给每个线程所占用cpu的时间片
- 抢占式调度:优先让线程池中优先级高的线程占用cpu,如果线程池中线程优先级相同,那么就随机选择一个线程,让其占用cpu。
10、什么是线程死锁?
线程死锁其实就是A线程手中持有资源B,C线程手中持有资源D,在这两个线程都不释放资源的情况下,两个线程互相请求获取对方手中资源,这时两个线程就会互相等待对方释放资源,这样就进入到了死锁状态。
11、 形成死锁的四个必要条件是什么
- 互斥条件:在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,就只能等待,直至占有资源的进程用毕释放。
- 占有且等待条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
- 不可抢占条件:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。(比如一个进程集合,A在等B,B在等C,C在等A)
12、如何防止发生死锁?
- 避免一个线程去获取多个锁资源。
- 可以使用定时锁,等待一个锁太就没获取到,就是放自己拥有的锁,避免死锁。
- 尽量避免一个锁同时占用多个资源,尽量保证一锁一资源。
13、ThreadLocal 是什么?
ThreadLocal首先它是一种线程隔离机制,它提供了多线程环境下对共享变量访问的安全性。在多线程访问共享变量中,一般解决办法是给变量加锁,但是加锁会带来性能降低,ThreadLocal就采用了一种空间换时间的思想,每个线程里面,都有一个容器来存储共享变量的副本,然后每个线程只对自己的变量副本进行操作,不影响其他线程,解决了线程安全也避免了加锁的开销
14、synchronized的三种应用方式
- 修饰实例方法:给当前对象实例加锁,进入同步代码前要先获取当前对象实例的锁。
- 修饰静态方法:给当前类对象加锁, 进入同步代码前要先获取当前类对象的锁。
- 修饰代码块:给指定加锁对象加锁,进入同步代码前要先获取指定对象的锁。
15、说一下 synchronized 底层实现原理?
synchronized底层是通过monitor(监视器锁)对象来完成的。
- 如果monitor的进入数为0,则该线程获取monitor的使用权,这时进入数由0变1。
- 如果该线程已经占有monitor,则至于要重新进入,则monitor的进入数加1。
- 如果已经有线程占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0则该线程再重新获取monitor使用权
16、synchronized 和 Lock 有什么区别?
Lock比synchronized 更加灵活,lock可以自主决定什么时候加锁,什么时候释放锁,只需要调用lock()和unLock()方法就绪,同时Lock还提供非阻塞竞争锁方法tryLock(),该方法返回true/false来告诉当前线程是否已经有其他线程占用锁,
Synchronized是关键字,它无法实现非阻塞竞争锁的方法,另外Synchronized释放锁是被动的,只有当代码执行完成或者抛出异常才会释放锁
17、ReentrantLock如何实现公平锁或非公平锁?
ReentrantLock 默认采用非公平锁的策略来实现锁,其次ReentrantLock 内部使用AQS来实现锁竞争,没有竞争到资源锁的线程,会加入到AQS的同步队列里面,该队列是个双向链表
- 公平锁实现:线程在竞争锁资源时判断AQS同步队列有没有等待线程
- 非公平锁实现:不管线程有没有等待线程,直接尝试抢占锁资源,抢不到再加入AQS同步队列
18、什么是volatile 关键字?
- 可见性而言,当一个共享变量被volatile修饰时,它可以保证共享内存中的值为最新值。
- 原子性而言: volatile和CAS结合可以保证原子性。
19、synchronized 和 volatile 的区别是什么?
- synchronized修饰类,方法,变量,volatile是变量修饰符。
- synchronized可以保证变量的可见性和原子性,而volatile只能保证可见性。
- synchronized可能会造成线程阻塞,而volatile不会造成线程阻塞。
20、乐观锁和悲观锁?
- 乐观锁:乐观锁每次去拿数据时都认为别人不会更改数据,所以对数据不加锁,但是它会使用版本号来记录别人有没有更改数据。
- 悲观锁:他和乐观锁相反,他每次去拿数据都认为别人会更改更改数据,所以它每次去拿都会给数据进行加锁,这样别人想拿到数据就会阻塞。
21、公平锁和非公平锁?
1.公平锁:多个线程按照申请锁的顺序去获取锁,
- 优点:所有线程都能获取到资源。
- 缺点:除了队列中第一线程,其他线程都会阻塞,cpu的唤醒阻塞线程开销会变大
2.非公平锁:多个线程去获取锁时,会直接尝试获取,不按申请锁顺序来。
- 有点:减少cpu唤醒线程开销
- 缺点:可能会导致优先级低的线程一直长时间获取不到锁,导致饿死
22、可重入锁和不可重入锁?
- 可重入锁:它会判断锁的拥有权,如果是自己,那么它依旧可以访问资源,并上锁次所加1,如果其他线程访问则阻塞。
- 不可重入锁:当线程第一次加锁时,由于没有释放锁,再次获取锁时造成了死循环,这种现象就是不可重入锁。
23、什么是自旋锁?
自旋锁其实就是线程在没有获取锁时,不被直接挂起,反而一直等待,不断判断锁能否被获取,直到获取锁才退出循环。
自旋锁的目的是为了减少线程被挂起的几率,减少资源消耗。
24、独占锁和共享锁?
- 独占锁:独占锁是指锁一次只能被一个线程所持有,那么其他线程都不能对该数据加任何锁。获取独占锁的线程既能读数据也能修改数据。
- 共享锁:共享锁是指锁可以被多个线程所持有,获得共享锁的线程只能对数据进行读操作,不能进行修改。
25、什么是死锁,活锁,饥饿锁?
死锁:死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的互相等待现象,这就是死锁。
活锁:活锁是指任务或者执行者没有被阻塞,因为某些原因不满足,一直重复尝试,这就是活锁。
饥饿:一个或多线程因为某种原因一直获取不到锁,导致一直无法执行,这就是饥饿。
导致饥饿的原因:
- 优先级高的线程一直获取cpu时间,导致优先级低的线程一直无法获取cpu。
- 线程在等待一个本身也处于永久等待的对象(比如该对象调用wait()方法)。
26、什么是线程池?
在我们使用线程时,线程的创建和销毁会占用cpu资源,线程池本身有参数来控制线程的创建数量,这样可以避免无休止的创建线程所带来的资源损耗。其次线程池对线程起到复用技术,工作线程处于运行状态下,他会从队列中获取等待执行的任务,一人队列为空,线程就会被阻塞,等任务进入队列时,线程被唤醒并执行任务。
27、为什么要用线程池?
- 降低资源消耗,线程池里的线程可以重复使用,减少了线程创建和销毁的过程。
- 提高响应速度,当有任务需求时,我们不需要创建一个新线程,直接使用线程池线程执行,减少了等待线程创建时间。
- 提高线程的易管理性,线程池可以进行线程的统一分配和监控。
28、线程池创建的方式?
- newCachedThreadPool:是一种可以缓存的线程池,它可以用来处理大量短期的突发流量。
- newFixedThreadPool:是一种固定线程数量的线程池。它的特点是核心线程和最大线程数量都是一个固定的值如果任务比较多工作线程处理不过来,就会加入到阻塞队列里面等待
- newSingleThreadExecutor:只有一个工作线程的线程池。并且线程数量无法动态更改,因此可以保证所有的任务都按照 FIFO 的方式顺序执行。
- newScheduledThreadPool:具有延迟执行功能的线程池可以用它来实现定时调度
- newWorkStealingPoolJava8 里面新加入的一个线程池它内部会构建一个 ForkJoinPool,利用工作窃取的算法并行处理请求。这些线程都是通过工具类 Executors 来构建的,线程池的最终实现类是ThreadPoolExecutor。
29、线程池都有哪些状态?
- RUNNNING:接收新任务并处理排队任务。
- SHUTDOWN:不接受新任务,但处理排队任务。
- STOP:不接受新任务,不处理排队任务,并中断正在进行的任务。
- TIDYING:所有任务终止,workCount(工作数量)为0,线程转换到 TIDYING 状态将运行 terminated() 终止方法。
- TERMINATED: terminated() 终止方法执行完成。
30、创建线程池的参数?
- corePoolSize:核心线程数
- maximunPoolSize:最大线程数量
- keepAliveTime:线程保持时间
- unit:时间单位
- workQueue:阻塞队列
- threadFactory:线程工厂
- handler:线程池拒绝策略
31、线程池有哪些拒绝策略?
- AbortPolicy:终止策略。默认的拒绝策略 直接抛出RejectedExecutionException异常。调用者可以捕获该异常并根据需求写处理代码。
- DiscardPolicy:抛弃策略。直接放弃被拒绝的策略。
- DiscardOldestPolicy:抛弃最老策略。抛弃阻塞队列里面最老的任务。
- CallerRunsPolicy:调用者运行策略,在调用者线程中执行该任务。
32、线程池中submit()和execute()方法有什么区别?
- 接收参数:submit()可以执行Runnable和Callable类型任务,execute()只能执行Runnable类型任务。
- 返回值:submit()方法可持有计算结果Futur对象,execute()方法没有。
三、MySQL面试题
1、什么是MySQL?
MySQL就是我们最常用的关系型数据库之一,我们可以将数据存在MySQL中并使用sql语句高效的查出我们想要的数据。
2、数据库的三大范式?
- 第一范式:列不可在拆分
- 第二范式:非主键列必须完全依赖于主键
- 第三范式:非主键列只依赖于主键,不依赖于其他非主键
3、MyISAM索引和InnoDB索引的区别?
MyISAM |
InnoDB |
对比项 |
不支持 |
支持 |
事务 |
表锁 |
表锁,行锁(默认) |
锁类型 |
可以没有 |
必须有 |
主键 |
不支持 |
支持 |
外键 |
默认InnoDB
4、我们为什么要使用索引?
索引其实是一种数据结构,它可以大大减少数据检索时的磁盘IO次数,从而提升查询效率
5、索引都有哪些类型?
- 主键索引:数据列不允许重复,不允许为null,一个表只能有一个主键
- 唯一索引:数据列不能有重复,可以为null,一个表可以有u东哥唯一索引
- 普通索引:普通索引是最基本的索引,它没有任何限制可以为null,仅用来加速查询
- 组合索引:在多个字段上创建索引,只有查询条件中引用了创建索引时的一个字段,索引才会触发。
- 全文索引:全文索引主要用来查询文本的关键字,而不是直接与索引中的值进行比较
6、索引的创建方式?
ALTER TABLE`table_name`ADDPRIMARYKEY (column);创建主键索引
ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引
ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引
7、什么情况下索引会失效?
- 使用组合索引没有遵循最左原则,会导致索引失效
- 使用!=(不等于),<>(大于小于),IS NOT NULL,会导致索引失效
- 使用like条件以%开头,会导致索引失效
- 类型转换,会导致索引失效
8、常见的索引类型?
常见索引类型为:hash 、B树、B+树
- hash:底层就是hash表,查询时,根据key调用hash函数获取hashCode值,根据hashCode值找到对于的数据地址,根据地址拿去对应数据
- B树:B树是一种多路搜索树,n路搜索树代表每个节点最多有n个节点,每个节点存储key+指向下一层节点的指针+记录key数据的地址。查找时根据根节点向下查找,直到找到对于的key值
- B+树:B+树是B树的变种主要区别在于B+树的非叶子节点只存储key+指向下一层节点的指针。B+树是一个有序链表,因此遍历只需要一次线性遍历叶子节点就好
9、什么是聚簇索引?
- 聚簇索引:将数据存放和索引放在一起,找到索引也就找到了数据,叶子节点data域保存完整数据记录的就是聚簇索引 InnoDB就是采用聚簇索引
- 非聚簇索引:叶子节点data域只保存主键值或数据地址的就是非聚簇索引,MyISAM就是非聚簇索引
10、什么是回表?
通过辅助索引查询到主键值,再拿主键值去主键索引中查找这个过程就是回表
11、什么是数据库事务?
事务其实就是保证我们数据库中的操作要么全部成功,要么全部失败。、
12、事务的四大特性?
- 原子性:事务的操作要么全部执行,要么全部不执行
- 一致性:一个事务在执行前后执行后,数据库必须处于一致状态
- 隔离性:多个事务同时操作,互相不影响。
- 持久性:事务执行后,对数据库的操作是永久的
13、MySQL的事务隔离级别?
隔离级别 |
脏读 |
不可重复读 |
幻读 |
读未提交(Read Uncommitted) |
有 |
有 |
有 |
读已提交(Read Committed) |
无 |
有 |
有 |
可重复读(Repeatable Read) |
无 |
无 |
有 |
串行化(Serializable) |
无 |
无 |
无 |
14、什么是脏读,不可重复读,幻读?
- 脏读:事务A已经更新了数据,事务B读取了该数据,这时A事务发生了回滚操作,B事务读取的就是脏数据
- 不可重复读:事务A读取了数据,事务B对其进行了修改,当事务A再次读取时,一个事务内两次读取数据值不同,这就是不可重复读
- 幻读:在一个事务内两次读取数据数量不同,可能因为在读取中发生数据插入,这就是幻读
15、按照锁粒度来分有哪些锁?
- 行锁:行锁是MySQL中锁粒度最小的锁,它针对当前操作行进行加锁。行锁开销大,加锁慢,发生锁冲突低
- 表锁:表锁是MySQL中锁粒度最大的锁,它针对于当前操作表进行加锁,表锁开销小,枷锁快,发生锁冲突高
- 全局锁:加锁之后整个数据库都处于只读状态。所有数据操作都会被挂起。
16、InnoDB 的行锁是怎么实现的?
InnoDB行锁是通过索引实现的,只有通过索引条件检索数据才会使用到行锁,否则就是用表锁
17、什么是视图?
视图本质上就是一张虚拟的表,但不包含表中应有的列和数据,只包含使用时动态检索数据的查询
视图特点:
- 视图的建立是由实际表产生的虚拟表
- 视图的建立和删除都不影响实际表
- 简化SQL查询,提高开发效率
18、视图使用场景?
- 重用SQL语句
- 使用整张表的部分而不是全表
- 保护数据。可以给用户赋予表指定部分数据而不是全表访问权限
- 简化复杂SQL语句。
19、什么是存储过程?
存储过程是一组为了完成特点功能的SQL语句集,它存储在数据库中,一次编译后永久有效,用户根据指定存储过程的名字并给出参数来执行它。
优点:
- 较少工作量:存储过程一次编译永久有效,可以重复使用
- 安全性高:执行存储过程需要一定权限的用户
缺点:
- 调试麻烦
20、4种SQL语言分别是什么?
- DDL(数据库定义语言) CREATE
- DQL(数据库查询语言) SELECT
- DML(数据库操纵语言) INSERT,UPDATE,DELETE
- DCL(数据库控制语言) GRANT,COMMIT,ROLLBACK
21、MySQL中内连接,外连接的区别?
- 内连接:组合两个表的纪律,返回两张表中交集的数据
- 外连接:外连接分为左外连接和右外连接 左外连接即左表的记录会全部现实出来,右边只显示符合条件记录,右边不足地方填充为null 右外连接和左外相反
22、MySQL中in和exists区别?
- 子查询中结果集记录较小使用in,反之大使用exists
23、MySQL中Union和UnionAll区别?
- Union:对两个结果集进行并集操作,不包含重复数据,同时进行默认排序
- UnionAll:对两个结果集进行并集操作,包含重复数据,不进行排序
24、MySQL中Drop、Delete 和 Truncate 的区别?
- Drop:从数据库中删除表的所有数据包括索引和权限
- Delete:删除表的全部或者部分数据,表结构还在
- Truncate:删除表的全部数据,表结构还在
25、怎样优化你的SQL查询?
SQL语句慢可以通过EXPLAIN和慢查询日志来排除
- 查询参数不要使用*用什么参数查什么
- 避免在where条件中使用!= <> 操作符
- 能用UnionAll代替Union就是用UnionAll
- 给字段进行加索引
四、Spring面试题
1、什么是Spring?
Spring是一个轻量级的开发框架,Spring的根本是简化应用开发的复杂性
1、Spring的优点有哪些?
优点:
- 方便解耦,简化开发:Spring提供DI机制,我们可以将对象的创建交给Spring管理
- 支持AOP编程:Spring提供面向切面编程,可以实现对程序的权限拦截,日志记录功能
2、什么是Spring IOC?
IOC就是控制反转,我们将对象的控制权交由Spring进行管理,也就是说我们不需要自己创建对象,也就是new对象,当我们需要某个对象时,Spring容器会自动帮我们创建。IOC中有个很强大的功能叫做DI也就是我们常说的依赖注入,Spring中可以通过@Autowired或者@Resource注解将Spring容器中的对象注入到指 定的位置
3、什么是Spring AOP?
AOP也就是我们常说的面向切面编程,我们可以将业务中的重复代码,比如记录日志啊这样的代码提取出来,切入到我们想要增强的方法上,极大的减少我们开发的冗余代码。
4、开发中常用的IOC和AOP注解?
- IOC
- @Component
- @Repository
- @Sevcie
- @Controller
- DI
- @@Resource
- @Autowired
- AOP
- @Aspect:切面
- @Ponitucut:切点
- @Before:前置通知
- @Around 环绕通知
- @AfterReturning 返回后通知
- @AfterThrowing 异常抛出通知
5、@Resource和@Autowired区别?
- @Resource:默认按照名称装配,名称匹配不成功会按照类型在进行装配
- @Autowired:默认按照类型装配,类型匹配不成功可以配合@Qualifier在指定名称匹配
6、@Resource装配顺序?
@Resource装配顺序
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
7、Sping AOP的底层实现是什么?
AOP的实现方式是动态代理,动态代理分为两种。
- JDK动态代理:JDK代理是针对于实现接口类进行代理,利用反射机制生成一个实现代理接口的匿名类,再调用方法前调用InvokeHandler来处理
- CGLIB动态代理:CGLIB代理是针对于类实现代理,对指定类生成一个子类,并覆盖其中方法。它利用ASM开源包,将代理对象的class文件加载进来,通过修改其字节码生成子类来处理
8、Spring AOP中常见名词?
- 连接点(Join Point):指程序运行过程中所执行的方法
- 切面(Aspect):被抽取出来的公共模块,可以用来横切多个对象。一个切面可以由多个切点和通知组成
- 切点(Pointcut):切点就是定义要对哪些连接点进行拦截
- 通知(Adivice):就是需要增加到业务方法中的公共代码
- 目标对象(Target):指被增强的对象
- 织入(Weaving):为目标对象创建动态代理的过程就是织入。
9、Spring 通知(Adivice)有哪些类型?
- 前置通知:在连接点前执行
- 后置通知:在连接点退出时执行
- 环绕通知:包围一个连接点执行,可以在方法执行前或方法执行后进行自定义行为
- 返回后通知:在连接点正常执行后通知
- 抛出异常后通知:在方法抛出异常后执行通知
10、Spring 的事务管理机制?
- 编程式事务:通过编程管理事务
- 声明式事务:可以将业务代码和事务管理分离,只需要使用注解或者xml来管理事务
11、Spring 的事务的传播行为?
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在事务,就加入到这个是事务当中。默认的事务传播级别
- PROPAGATION_SUPPORTS:如果当前有事务就加入该事务,如果当前没有事务,就以非事务方式执行
- PROPAGATION_MANDATORY:如果当前有事务就加入该事务,如果当前没有事务,就抛出异常
- PROPAGATION_REQUIRES_NEW:无论当前有没有事务,都创建一个新事物执行
- PROPAGATION_NOT_SUPPORTED:以非事务执行,如果当前有事务,就将事务挂起
- PROPAGATION_NEVER:以非事务执行,如果当前存有事务,那么就抛出异常
- PROPAGATION_NESTED:如果当前有事务,则嵌套事务内执行。如果当前没有事务,则执行默认事务
12、Spring 的事务的隔离级别?
DEFAULT: Spring 中默认的事务隔离级别 ,以连接的数据库的事务隔离级别为准。
相比于 MySQL 的事务隔离级别,Spring 中多了一种 DEFAULT 的事务隔离级别。
13、Spring Bean的生命周期?
- 实例化bean:
- 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,容器就会调用createBean进行实例化。
- 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所- 有的bean。
- 赋值初始化:
- 实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设
置属性的接口完成属性设置与依赖注入
- Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源。
- 如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法
- 如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法
- 如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法
- 如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
- 销毁:
- 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法
- 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
14、Spring Bean的作用域?
spring作用域主要分为5种
- singleton:bean在Spring IOC容器中只有一个实例
- prototype:每次从容器中调用bean,都返回一个新的实例
- request:每次Http请求都会创建一个新的bean
- session:同Http Session公用一个bean,不同的session使用不同的bean
- application:限定一个Bean的作用域为ServletContext的生命周期
15、Spring Bean是线程安全的吗?
Spring框架中的单例bean本身并不安全,但是也要根据bean的作用域来说
- singleton:对于singleton作用域的bean,所有线程都共享一个实例bean,因此线程不安全
- prototype:对于prototype作用的bean,每次都创建一个新的实例,线程之间不共享bean所以线程安全
所以我们只需要将bean作用域由singleton改成prototype作用域bean线程也就安全了
16、Spring如何处理线程并发问题?
在Spring中,我们可以将bean作用域声明为singleton,因为Spring在对一些bean非线程安全的状态下采用ThreadLocal进行处理,ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal
17、Spring基于xml注入bean的几种方式?
- 通过Set方法注入
- 构造器注入 通过index设置参数的位置 type设置参数类型
- 静态工厂
- 实例工厂
18、Spring Bean的自动装配?
Spring中我们可以通过使用autowire来配置自动装载模式,对象无需自己查找或常见其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象
在Spring框架xml配置中共有5种自动装配:
- no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean
- byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配
- byType:通过参数的数据类型进行自动装配
- constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配
- autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
一般使用注解来进行自动装配@Autowired、@Resource
19、Spring中BeanFactory和ApplicationContext的区别
- BeanFactroy:只有在使用到bean时才会对该bean进行实例化,运行速度慢,占用空间小
- ApplicationContext:它在容器启动中,一次性创建所有bean实例化,运行速度快,占用空间大
20、@RestController 和 @Controller 的区别
@RestController注解,相当于@Controller+@ResponseBody两个注解的结合,直接返回实体对象,但使用@RestController这个注解,就不能返回jsp,html页面。@Controller类中的方法可以直接通过返回String跳转到jsp、ftl、html等模版页面,配合@ResponseBody也可以返回实体对象
- @ResponseBody:将方法的返回结果写入HTTP response body 中
- @RequestMapping:返回值解析为跳转路径
21、Spring 依赖注入的方式?
变量注入:使用直接在变量上使用注解,
- 优点:注入方式简单
- 缺点:注入对象不能用final修饰,可能会导致循环依赖
构造器注入:
- 优点:可以防止NullPointerException,可以用final修饰,避免了循环依赖如果有循环依赖则直接项目启动报错
- 缺点:当注入对象多的时候,代码臃肿
Setter方法注入:
- 优点:注入的依赖可以为null,允许在类构造完成后重新注入
- 缺点:注入对象不能用final修饰
22、Spring如何解决循环依赖?
首先循坏依赖有三种形态
- 互相依赖:A依赖B,B依赖A
- 循环依赖:A依赖B,B依赖C,C依赖A
- 自我依赖:A依赖A
Spring中采用了三级缓存来解决循环依赖,当调用getBean方法时,Spring首先会向一级缓存查找Bean,如果一级没有就去二级,都没有就代表该目标Bean没有进行实例化,于是Spring会将实例化的Bean存放在二级缓存中,同时标记是否存在循环依赖,如果不存在循环依赖就将Bean存放在二级缓存,否则就存在一级缓存
三级缓存作用在于,当Bean需要代理工厂创建,那么就会将实例保存在三级缓存,并同步到一级缓存
五、SpringMVC面试题
1、什么是Spring MVC
SpringMVC通过分离Model,view,Controller,将web程序进行解耦,减少代码开发,方便合作开发。
2、SpringMVC工作原理?
- 用户发请求到DispatcherServlet
- DispatcherServlet调用HandlerMapping(处理器映射器)
- HandlerMapping(处理器映射器)找到具体的处理器返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapter(处理器适配器)
- HandlerAdapter经过适配调用具体的处理器Controller
- Controller执行完成返回ModelAndView
- DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
- ViewResolver解析后返回具体View
- DispatcherServlet对View进行渲染视图
- DispatcherServlet响应用户
3、SpringMVC核心组件?
- 前端控制器 DispatcherServlet:接受请求,响应结果
- 处理器映射器HandlerMapping:根据请求地址url查找处理器
- 处理器适配器HandlerAdapter:负责执行处理器
- 处理器 Controller
- 视图解析器 ViewResolver:进行试图解析
4、mybatis 中 #{}和 ${}的区别是什么?
Mybatis中提供的#和$都是实现动态SQL的一种方式。
- #{}:#{}相当于占位符,当有特殊参数时,会自动转义,同时#{}可以防止SQL注入
- : {}: :{}相当于直接把参数拼接到了原始的SQL里面,Mybatis不会对他进行任何特殊处理,所以${}无法防止SQL注入
5、mybatis 的缓存机制?
Mybatis中设置了二级缓存这样的机制,是用来提升检索的效率,也就是说避免每一次数据的检索都去查询数据库。
一级缓存:一级缓存是SqlSession级别的缓存,也叫本地缓存,每一个用户在查询时,都是会用SqlSession来执行,为了避免每次都去查询数据库,Mybatis将缓存存储在SqlSession的本地缓存里,后续的SQL如果命中缓存的情况下,就可以直接去本地缓存去读取数据。如果想实现跨SqlSession级别的缓存一级缓存是没法实现的,因此Mybatis中引入了二级缓存,当多个用户在查询数据的时候,只要有任何一个人拿到SqlSession拿到了数据就会放如二级缓存,那么其他SqlSession就能直接从二级缓存中拿取数据。缓存机制执行的流程是,如果开启了二级缓存那么先去二级缓存中查询,没有命中再去一级缓存,都没有命中就去数据库查询。
6、SpringMVC如何重定向或转发?
- 重定向:在返回值前加redirect
- 转发:在返回值前加forward
7-、mybatis 的动态标签?
六、SpringBoot面试题
1、什么是SpringBoot?
我们在传统的SSM框架中,需要编写大量的XML配置文件,SpringBoot将大量的配置文件集成到SpringBoot内部,简化我们开发
优点:
- 内嵌Tomcat,项目独立运行
- 简化XML配置文件,简化开发
2、springboot自动装配原理?
SpringBoot自动装配核心在于@SpringBootApplication注解他是一个复合注解里面包含:
- @SpringBootConfiguration:该注解上有个@Configuration注解,表示SpringBoot启动类是一个配置类,最终被注入spring容器中
- @EnableAutoConfiguration:表示开启自动装配它也是一个复合注解包含2个注解:
- @AutoConfigurationPackage 该注解上有一个@Import注解作用是将启动类所在包及子包组件扫描到spring容器中。
- @Import({AutoConfigurationImportSelector.class}):查找位于META-INF/spring.factories文件中的所有自动配置类,并加载这些类。
- @ComponentScan:自动扫描并加载符合条件的组件或者bean
3、springboot监视器作用?
SpringBoot actuator是spring中重要功能之一,监视器可以帮助你访问生产环境中正在运行的应用程序的当前状态,监视器模块公开了一组可直接座位Http url访问Rest端点来检查状态
4、springboot开发过程你如何使用事务?
直接在service上使用@Transactional直接
5、springboot如何实现全局异常处理?
@ControllerAdvice类注解配合@ExceptionHandler方法注解使用,对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面
6、springboot如何实现热部署?
使用DevTools
7、springboot如何实现定时任务?
开发中一般使用@Scheduled 注解来实现
8、springboot如何解决跨域问题?
全局配置需要添加自定义类实现 WebMvcConfigurer 接口,然后实现接口中的 addCorsMappings 方法
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(1800)
.allowedOrigins("*");
}
}
七、SpringCloud面试题
1、谈谈你对微服务的理解?
将大型的单体应用拆分成多个应用程序,多个应用程序运行在各自的进程中,这多个应用程序就是微服务
优点:
- 每个应用都是独立项目,可以独立部署
- 直接写后端代码并暴露接口就绪不需要关注前端开发
- 技术更新灵活,微服务可以根据业务特点灵活选择技术栈
缺点:
- 开发复杂,服务之间调用复杂性提高
- 部署测试麻烦
2、SpringBoot和SpringCloud区别?
- SpringBoot:快速开发个体服务,可以单独开发
- SpringCloud:更关注全局服务管理,离不开SpringBoot开发
3、SpringCloud常用组件?
在微服务框架中,涉及多个微服务之间的发现就牵扯到SpringCloud Eureka,它将服务的生产者注册到Eureka Service中,当调用者来获取服务时,根据生产者服务别名获取实际的服务通信地址进行远程调用。Eureka还提供了心跳机制,当服务注册者在默认时间(30s)向Eureka Service发送心跳反应,如果说Eureka Service在几个周期(90s)内没有收到心跳,则会将该服务从Eureka Service服务注册表中移除。多个服务注册到Eureka中,就会出现调用问题,这时候就用到Ribbon和Feign,Feign集成了Ribbon,我们需要设定负载均衡策略,将用户请求合理的分配给多个服务器,并调用。在微服务服务调用过程中,某些服务会调用失败或者调用进入长时间等待占用系统资源,这时候就需要Hystrix,将调用失败服务和长时间等待服务进行服务熔断,还可以在大量服务进来时,服务器可能承受困难,它可以采取服务限流在一定时间内只进入某些数量的服务,依次处理减少服务器压力。在多个服务中,每个服务都需要配置自己服务必要的配置文件,我们可以采取Config对这些配置文件集中管理,并且我们可以动态的调整配置文件,不需要服务重启就可以重新加载配置文件,更好的减少服务重启消耗的时间。最后是GateWay或者Zuul,微服务中多个服务我们需要将服务暴露给前端,如果每个服务都一个地址,非常不好管理,我可以采用GateWay或者Zuul对服务地址统一管理。一般就用这么多服务。
- Eureka:服务注册于发现
- Zuul/GateWay:服务网关
- Ribbon:客户端负载均衡
- Feign:声明性的Web服务客户端
- Hystrix:断路器
- Config:分布式统一配置管理
4、什么是Eureka?
Eureka就是我们常说的服务注册中心,我们将服务提供者注册到Eureka中,由服务消费者到Eureka中进行消费。
5、什么是服务注册于发现?
当服务提供者在启动时,会将自己服务器信息以别名方式注册到注册中心,当服务消费者以该别名方式去注册中心去获取实际的服务通信地址,然后实现远程调用
6、Eureka两大组件?
- Eureka Service(提供服务注册服务):每个微服务节点都会注册到Eureka Service中,Eureka Service的服务注册表中会存储所有可用服务信息
- Eureka Client(通过注册中心访问):它是一个Java客户端,用于简化Eureka Server的交互。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期(默认周期90秒) 内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除
7、什么是Eureka的心跳机制?
注册进来的节点会Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期(默认周期90秒) 内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除
8、什么是Eureka的自我保护机制?
在Eureka服务运行期间,他会统计心跳成功比例在15分钟内是否低于85%,如果低于,那么Eureka Service就会认为当前实例的客户端出现连接故障,Eureka Service就会将这些实例保护起来,不让其过期。这样做就是为了防止EurekaClient可以正常运行, 但是与EurekaServer网络不通情况下, EurekaServer 不会立刻 将 EurekaClient 服务剔除。
9、Eureka和ZooKeeper有什么区别?
最大区别在于Eureka集群中每个节点都是平等的,只要有一台Eureka就可以保证服务可用,ZooKeeper节点挂了会采取选举,在选举期间服务不可用
10、什么是负载均衡?
简单说就是将用户的请求分配到多个服务器上,从而达到服务器的高可用
11、什么是Ribbon?
SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的负载均衡和服务调用
12、Ribbon默认自带的负载规则你知道那些?
- RoundRobinRule 轮询
- RandomRule 随机
- RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重新选择
- WeightedResponseTimeRule 响应速度越快,越容易被选择
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
13、什么是Feign
在原来开发中我们使用Ribbon和RestTemplate配合使用,由于对服务依赖的调用可能不止一处,一个接口会被多处调用,通常会针对每个微服务进行封装。而Feign再次基础上进行一步封装,它帮我们定义和实现依赖服务接口的实现,我们只需要创建接口就行了。并且Feign还继承了Ribbon
14、什么Hystrix?
在分布式系统中,我们会依赖各种服务,服务之间会发生调用失败,就会导致雪崩,Hystrix能防止雪崩它具有服务降级,服务熔断等功能。
15、什么是服务雪崩?
假如A服务调用B服务和C服务,B服务和C服务又调用其他服务,这时候由于某个服务调用响应时长过长或者不可用,A服务的调用资源占比就会越来越高,从而引起系统崩溃也就是服务雪崩
16、什么是服务降级,服务熔断,服务降级?
- 服务降级:当客户端请求服务器时,防止客户端一直等待,直接返回提示给客户端
- 服务熔断:当请求失败数量在一定时间内达到设定值,直接拒接访问并调用服务降级
- 服务降级:当大量请求进来时,为了避免拥挤,采取一次进固定值有序进行。
17、什么是网关?
网关就是在微服务框架中,所有的网络请求必须通过网关进行转发到具体的服务。网关可以统一管理微服务请求,安全控制黑名单白名单等。
18、网关与过滤器有什么区别?
19、什么是GateWay?
GateWay微为服务架构提供—种简单有效的统一的API路由管理方式。Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
20、为什么公司要是用GateWay而不是用Zuul?
- Spring的官方主推GateWay,而Zuul已经停止更新了,未来没有功能上的提升。
- GateWay支持异步而Zuul仅支持同步
- GateWay内部实现了限流,负载均衡,而Zuul没有实现
21、GateWay工作流程?
客户端向GateWay发出请求,然后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到GatewayWeb Handler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
22、什么是Cofig?
在微服务系统中,每个服务都需要必要的配置信息才能运行,config就提供了微服务下各种环境下配置的集中管理。
能做什么:
- 集中管理配置文件
- 不要再每个服务上编写配置文件,服务会向配置中心统一拉去自己的配置信息
- 运行期间动态的调整配置,无需服务重启
23、Nacos?
Nacos包含了Eureka+Config+Bus
八、Redis面试题
1、什么是Redis?
Redis是一种基于键值对的NoSQL数据库(非关系型数据库);是一个key-value存储系统
Redis有两个特点:高能性 可靠性
高能性:Redis将所有数据都存储在内存中,所有读写性特别高
可靠性:Redis将内存中的数据利用RDB和AOF的形式保存到硬盘中,这样就可以避免发生断点或机器故障时内存数据丢失的问题
接下来介绍redis的数据类型,顺便介绍使用场景,结合工作中到底哪里使用了redis
2、Redis存储数据类型有哪些?
3、Redis的持久化?
Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。
RDB:默认的Redis持久化方式,按照一定时间将内存中的数据以快照的形式保存到磁盘中,Redis会创建一个新线程进行持久化,将数据写到一个临时文件,等持久化结束替换原来的文件, 数据文件为dump.rdb。
- 优点:性能快,使用单独线程进行持久化,主线程不进行保证了Redis的高性能
- 缺点:因为它会创建一个临时文件,所以需要2倍空间,占用空间大
AOF:AOF是采取日志的形式记录每个操作(不包含读操作),追加到文件当中去,重启时会再次执行AOF文件来恢复数据
- 优点:空间节省
- 缺点:AOF记录越多,文件越大,恢复就越慢
两个同时开启会优先采取AOF恢复数据
4、什么是缓存穿透、缓存击穿、缓存雪崩
- 缓存穿透:在请求访问时,缓存和数据库都没有某个值,这样导致每次对这个值的查询请求都会穿透到数据库,就是缓存穿透。解决方法:
- 缓存空对象:Redis和MySQL中都没有该数据,我们就在Redis中存储一个空对象,这样后续对该数据查询就能被Redis直接返回,
- 布隆过滤器:采用布隆过滤器去判断改值是否存在,不存在直接返回。
- 缓存击穿:指热点Key在某个时间点过期的时候,这时对这个Key进行大量的请求,从而导致所有请求落到数据库中。解决方法:
- 热点数据永不过期
- 缓存雪崩:缓存中有大批量的缓存过期,这时发生大量的请求,全部请求直接访问数据库,导致数据库压力过大甚至宕机。解决方法:
- 将缓存的过期时间分开
- 热点数据永不过期
5、布隆过滤器?
布隆过滤器是一种数据结构,它用于检索一个元素是否存在于集合中。
6、Redis的过期策略?
- 定时过期:给Key创建一个定时器,到了过期时间就会自动对Key进行立刻清除
- 惰性过期:只有要访问这个Key时,才回去判断该Key是否过期,过期则清除
- 定期过期:每隔一定时间,扫描一定量的Key,过期的则清除
7、Redis的淘汰策略?
- volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
- allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
- volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key。
- allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘
- volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据
- allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
- noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。
8、Redis主从模式?
Redis主从模式是实现Redis集群集群的一种方式,集群中包含一个主节点和多个从节点,主节点用来数据的写,从节点用来数据的读取,并且主节点数据变更会同步给从节点。
9、Redis主从复制?
Redis主从复制包括全量复制和增量复制,全量复制发生在初始化阶段,从节点向主节点发送同步请求,主节点会生成一份当前数据的快照给从节点,从节点对数据进行加载并全部复制。增量复制是发生在主节点数据有变化时,会将变化数据同步给所有从节点。
10、Redis哨兵模式?
Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务。 某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。
11、Redis哨兵模式下哨兵的工作流程?
- 哨兵会向它监控的节点每秒钟发送一次PING命令
- 如果一个实例超过了一定时间,没有有效回复哨兵,那么哨兵就会将该实例标注主观下线
- 如果一个Master被标注主管下线,其他监控它的哨兵也检测它不可用,当不可用达到一定值,那么它就会被标注为客观下线,若Master重新向哨兵的 PING 命令返回有效回复, Master 的主观下线状态移除
- 在一般情况下 每个哨兵回向它监控所有节点每10s发送一次INFO命令
- 没有回复,Master被标为客观下线,若没有足够数量同意Master下线,则客观下线状态被移除
12、Redis集群下每个集群存储的数据一致吗?
Redis实现分布式存储,对数据进行分片操作,所以每个Redis节点存储不同的内容
13、如何保证Redis的双写一致性?
采用延时双删,我们首先将Redis缓存进行删除,再去更新数据库,在更新数据库的途中可能会发生数据库读取数据存储到Redis缓存中,所以我们设定一个时间间隔进行Redis缓存第二次删除
14、Reids的事务机制?
Redis的事务是没有隔离级别的,Redis单条命令是原子性执行,但是事务不保证原子性,并且没有回滚,事务中命令任意命令执行失败,其他命令仍会被执行
事务相关命令:
- MULTI:标识一个事务的开启,即开启事务;
- EXEC:执行事务中的所有命令,即提交;
- DISCARD:放弃事务;和回滚不一样,Redis事务不支持回滚。
- WATCH:监视Key改变,用于实现乐观锁。如果监视的Key的值改变,事务最终会执行失败。
- UNWATCH:放弃监视。
15、Redis实现分布式锁
Redis提供了一个实现排他锁的的命令,当key不存在就返回1,存在就返回0,我们还需要对锁设置一个失效时间,避免死锁,这样的可能会发生一种情况,就是锁过期了但是业务还没执行完,所以我们可以采用Redisson,它内置了一个看门狗机制,检测到里面语句为执行完成会自动对锁过期时间续期。以上就是我对分布式锁理解。
九、Elasticsearch
1、什么是倒排索引?
- 正排索引:由key查询value
- 正排索引:倒排索引的话就是说,根据文件内容去关联主键id