2019年JAVA最新面试题部分

一:基础篇

1.1 集合篇

1.ArrayList和LinkedList的区别是什么(面试)?

答案:①是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

② 底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;

③ 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。

④是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

⑤内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。

2.什么是List接口

答:①是元素有序并且可以重复的集合,被称为序列,

②List可以精准的控制每个元素的插入位置,或者删除某个位置元素

③List接口的常用子类有ArrayList,LinkedList,Vector(已经过时)

3.什么是HashMap(重点)

答案:①HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null  value和null key

②本质为无序散列,Hash算法来决定存储位置.

③HashMap的数据结构和底层实现(面试重点)

 JDk1.8之前的HashMap由数组+链表组成的,即”链表散列”,数组是HashMap的主题,链表则是主要是为了解决哈希冲突而存在的(“拉链法”),如果定位到的数组位置不含链表(entry的next指向null),那么对于查找,添加等操作很快,仅需要一次就能寻找到地址;如果定位到数组包含链表,对于添加操作,其时间复杂度依然为O(1),因为最新的Entry会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过key对象的equals方法逐一对比.  

④HashMap不是线程安全的,当迭代其中有其他的线程改变了HashMap中的值将会抛出ConcurrentModifcationException.

关于HashMap的扩容机制

【Threshold(开始扩容的临界点)=Size(HashMap的容量)*Load_Factor(负载因子);】

4.什么时候开始扩容?

答:当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数size)*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置。

为什么是0.75,大量实验得出的结果

如果取0.5,超过一般就扩容,造成资源的浪费.

如果取1,到临界值才扩容,会增加哈希碰撞的几率.

5.扩容的方法是?

调用resize方法

6.Hashmap为什么大小是2的幂次?

答案:为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。

7.HashMap线程安全问题,对应的线程安全Map是什么?

答:①对应的的是ConCurrentHashMap支持高并发的访问和更新,它是线程安全的.

②ConCurrentHashMap在JDK1.8中和HashMap的底层相同都是散列表+红黑树

③通过部分锁定+CAs算法来进行实现线程安全的.

④ConCurrentHashMap的key和Value都不能为null.

8.什么是CAs算法?

答:Compare and swap是一种有名的无锁算法,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试.

总结:先比较是否相等,如果相当则替换.

 

1.2多线程

1.什么是进程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

2.什么是线程

线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。

3.并发和并行:

①并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上有多个进程同时执行的效果

②并行指在同一时刻,有多条指令在多个处理器上同时执行。

4.多线程有什么优势?

答:①进程之间不能共享内存,但线程之间共享内存非常容易

②系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高

③Java语言内置了多线程功能支持,从而简化了Java的多线程编程

5.ThreadRunnable的区别

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

6.多线程的生命周期

答:在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态。

流程:

1)、新建状态(New):新创建了一个线程对象。

2)、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3)、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4)、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5)、死亡状态(Dead):线程会以一下三种方式结束:

①run()或call()方法执行完成,线程正常结束

②线程抛出一个未捕获的Exception或Error

③直接调用该线程的stop()方法来结束线程——该方法容易导致死锁

注:当主线程结束时,其他线程不受影响,不会随之结束。

线程睡眠Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

   线程等待Object类中的wait()方法(无限等待),导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。

   线程让步Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。

   线程加入join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

   线程唤醒Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

7.什么是线程安全和线程不安全?

答案:由于系统的线程调度具有一定的随机性,当使用多个线程来访问同一个数据时,很容易“偶然“出现线程安全问题。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

8.线程安全问题

(1)什么叫线程安全

答: 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问,直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

(2)何为线程不安全

答:线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

9.线程同步锁有什么特点

答:同步代码块:synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

synchronized(obj){需要同步的代码(多条语句操作共享数据的代码)}

obj:同步锁:

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

1. 锁对象可以是任意类型。

2. 多个线程对象要使用同一把锁。          

10.同步有什么特点:

         答:同步的前提是:多个线程并且多个线程使用的是同一个锁对象

11.同步的有什么好处

答:同步的出现解决了多线程的安全问题

12.同步有什么弊端

答:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

13.同步方法:

         答:同步方法就是使用synchronized关键字来修饰某个方法,则该方法称为同步方法。同步方法的同步监视器是this,也就是调用该方法的对象。

14.同步锁是谁?

答:对于非static方法,同步锁就是this

对于static方法,我们使用当前方法所在类的字节码对象(类名.class)

15.线程安全的类具有什么特征
答:①该类的对象可以被多个线程安全的访问

②每个线程调用该对象的任意方法之后都将得到正确结果

③每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。

16. Java sleep 方法和 wait 方法的区别?(答案)

         答:虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁

17.run()方法和start()方法区别:

答:run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。启动线程使用start()方法,不是run()方法。调用start()方法来启动线程,系统会把该线程run()方法当成线程执行体来处理。如果直接调用run()方法,则run()方法会被当成普通方法立即执行,而不是线程执行体。而且在run()方法返回之前其他线程无法并发执行。

18.多线程有几种实现方法?同步有几种实现方法?

答:①多线程有两种实现方法,分别是继承Thread类与实现Runnable接口

②同步的实现方面有两种,分别是synchronized,wait与notify

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉

InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某

一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

19.启动一个线程是用run()还是start()?

答:启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

20.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

答:分几种情况

1).其他方法前是否加了synchronized关键字,如果没加,则能。

2).如果这个方法内部调用了wait,则可以进入其他synchronized方法。

3).如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。

4).如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。

21.线程的基本概念、线程的基本状态以及状态之间的关系

答:①一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程。(如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。)

②线程基本状态:就绪,运行,synchronize阻塞,wait和sleep挂起,结束。wait必须在synchronized内部调用。调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。

22.简述synchronized和java.util.concurrent.locks.Lock的异同

答:主要相同点:Lock能完成synchronized所实现的所有功能

主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。

 

1.3 IO

1.java中有几种类型的流?

答:字符流和字节流。字节流继承inputStream和OutputStream,字符流继承自InputSteamReader和OutputStreamWriter。

2.谈谈Java IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞

答:输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。

IO流主要分为字符流和字节流。

①字符流中有抽象类InputStream和OutputStream,它们的子类FileInputStream,FileOutputStream,BufferedOutputStream等。

②字符流BufferedReader和Writer等。都实现了Closeable, Flushable,

③Appendable这些接口。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。

④java中的阻塞式方法是指在程序调用改方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句。比如read()和readLine()方法。

3.字符流和字节流有什么区别?

答:字符流以字符或者字符数组的形式读写数据,只能读写二进制文件;字节流能读写各种类型的数据。

字节流与字符流的不同是他们的处理方式,字节流是最基本的,采用ASCII编码。

但是实际上很多数据是文本,所以提出字符流的概念,采用unicode编码

两者之间通过inputStreamReader与outputStreamWriter来关联,实际上是通过byte[]与String来关联

字节流输出: 程序-->字节流-->文件

字符流输出:程序-->字符流-->缓冲-->文件

程序中所有的数据都是以流的方式进行传输与保存的

在关闭字符流后会强制性的将缓冲区的数据输出,若没有关闭缓冲区的内容是无法输出的,

如果想不关闭并且还想输出缓冲区的内容,用writer类的flush()方法来完成,

4.讲讲NIO

答:看了一些文章,传统的IO流是阻塞式的,会一直监听一个ServerSocket,在调用read等方法时,他会一直等到数据到来或者缓冲区已满时才返回。调用accept也是一直阻塞到有客户端连接才会返回。每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。并且多线程处理多个连接。每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部未准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。

对于NIO,它是非阻塞式,核心类:

1.Buffer为所有的原始类型提供 (Buffer)缓存支持。

2.Charset字符集编码解码解决方案

3.Channel一个新的原始 I/O抽象,用于读写Buffer类型,通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。

5.BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法

答案:属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法

6.什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征

答案:节点流 直接与数据源相连,用于输入或者输出处理流:在节点流的基础上对之进行加工,进行一些功能的扩展处理流的构造器必须要 传入节点流的子类

7.如果我要对字节流进行大量的从硬盘读取,要用那个流,为什么

答案:BufferedInputStream 使用缓冲流能够减少对硬盘的损伤

8.如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么

答案:Printwriter 可以打印各种数据类型

9.怎么样把我们控制台的输出改成输出到一个文件里面,这个技术叫什么

答案:SetOut(printWriter,printStream)重定向

10.怎么样把输出字节流转换成输出字符流,说出它的步骤

答案:使用 转换处理流OutputStreamWriter 可以将字节流转为字符流New OutputStreamWriter(new FileOutputStream(File file));

11.什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作

答案:对象序列化:将对象以二进制的形式保存到硬盘上;

反序列化:将二进制文件转化为对象读取.

将需要序化的类实现Serializable接口

12.解释一下java.io.Serializable接口(面试常考)

答:类通过实现 Java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。

①PrintStream、BufferedWriter、PrintWriter的比较? 
PrintStream类的输出功能非常强大,通常如果需要输出文本内容,都应该将输出流包装成PrintStream后进行输出。

②与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志,另外,为了自动刷新,可以创建一个 PrintStream

③BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符串的高效写入。通过write()方法可以将获取到的字符输出,然后通过newLine()进行换行操作。BufferedWriter中的字符流必须通过调用flush方法才能将其刷出去。并且BufferedWriter只能对字符流进行操作。如果要对字节流操作,则使用BufferedInputStream。

  ④PrintWriter的println方法自动添加换行,不会抛异常,若关心异常,需要调用checkError方法看是否有异常发生,PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush);

13.什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征

答案:节点流 直接与数据源相连,用于输入或者输出

处理流:在节点流的基础上对之进行加工,进行一些功能的扩展处理流的构造器必须要 传入节点流的子类

14.InputStream里的read()返回的是什么,read(byte[] data)是什么意思,返回的是什么值

答案:返回的是所读取的字节的int型(范围0-255)read(byte [ ] data),将读取的字节储存在这个数组,返回的就是传入数组参数个数Read字节读取字节、字符读取字符

15.OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

答:write将指定字节传入数据源

Byte b[ ]是byte数组

b[off]是传入的第一个字符

b[off+len-1]是传入的最后的一个字符 

len是实际长度

 

二、数据库篇

1.什么是sql语句优化?

答:就是对性能低下的sql语句转化成目的相同但是性能优异的语句。

sql优化的几种方法(重点记)

1).对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。       

2).应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:       

select id from t where num is null      

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:     

select id from t where num=0   

    3).应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。       

    4).应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:   

select id from t where num=10 or num=20       

可以这样查询:   

select id from t where num=10      

union all 

select id from t where num=20      

    5). in 和 not in 也要慎用,否则会导致全表扫描,如:     

select id from t where num in(1,2,3)      

对于连续的数值,能用 between 就不要用 in 了:      

select id from t where num between 1 and 3

    6).下面的查询也将导致全表扫描:  

select id from t where name like '%abc%'     

    7).应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:   

select id from t where num/2=100    

应改为:      

select id from t where num=100*2   

    8).应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:       

select id from t where substring(name,1,3)='abc'--name以abc开头的id    

应改为:      

select id from t where name like 'abc%'    

    9).不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。       

    10).在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。   

    11).不要写一些没有意义的查询,如需要生成一个空表结构:     

select col1,col2 into #t from t where 1=0  

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:       

create table #t(...)      

   

12).很多时候用 exists 代替 in 是一个好的选择:

select num from a where num in(select num from b)   

用下面的语句替换:   

select num from a where exists(select 1 from b where num=a.num)   

    13).并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。    

    14).索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。       

一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。       

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

    16).尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。   

    17).任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。   

    18).避免频繁创建和删除临时表,以减少系统表资源的消耗。

19).在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert

20).如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。    

21).尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。       

    22).使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

23).与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通

常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。

2.为什么要对其进行优化?

答 :因为sql语句是对数据库进行操作的惟一途径,对数据库系统的性能起着决定性的作用,所以优化它,能够提高访问速度。

3.如何找出需要优化的sql语句呢?这里就直接介绍sql语句优化步骤

答:① 开启慢查询日志 :当一条sql执行超时时,mysql可以将慢的sql记录到日志中。

② 使用执行计划(explain)分析慢的原因:执行计划能将 sql语句发送给mysql评估执行该条sql需要经历哪些步骤消耗多少资源

③ show profile: 主要是用来查询SQL在mysql服务器里面的执行细节和生命周期情况

④ 数据库服务器参数性能调优

4.数据库的表结构优化

​ 答:1).在创建表的属性时,要选择最合适的字段

​ Mysql是一种关系型数据库,可以很好地支 持大数据量的存储,但是一般来说,数据库中的表越小,在它上面执行的查询也就越快。因此,在创建表的时候,为了获得更好的性能。

2).尽量把字段设置为NOT NULL

​ 在可能的情况下,尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。

3).创建索引

​ ①索引是对数据库表中一个或多个列的值进行排序的结构.使用索引查询可以避免全表扫描。

​ ②创建索引的语句: create index 创建名 on 表名字(字段名)。

​ ③如果想查看当前表是否需要优化可以查看数据库中查询效率:

5.数据库的存储引擎

​ MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。

查看数据库的存储引擎:SHOW VARIABLES LIKE 'storage_engine'
常见的三种存储引擎

​ 1.InnoDB存储引擎

​ 2. MyISAM存储引擎

​ 3.MEMORY存储引擎

6.常用函数:

1.数学函数
 数学函数主要用于处理数字,包括整型、浮点数等。

2.字符串函数
  字符串函数是MySQL中最常用的一类函数,字符串函数主要用于处理表中的字符串。

3.日期时间函数
  MySQL的日期和时间函数主要用于处理日期时间。

4.系统信息函数
  系统信息函数用来查询MySQL数据库的系统信息。

5.加密函数
  加密函数是MySQL用来对数据进行加密的函数。

7.数据库三范式是什么?

答:第一范式(1NF):字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式)

第二范式(2NF):第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。

(要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。)

第三范式的要求:满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。

所以第三范式具有如下特征:

1).每一列只有一个值

2).每一行都能区分。

3).每一个表都不包含其他表已经包含的非主关键字信息。

8、说出一些数据库优化方面的经验?

答:用PreparedStatement 一般来说比Statement性能高:一个sql 发给服务器去执行,涉及步骤:

语法检查、语义分析, 编译,缓存有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。(比喻:就好比免检产品,就是为了提高效率,充分相信产品的制造商)表中允许适当冗余;sql语句全部大写,特别是列名和表名都大写。

 

三、框架部分

3.1Dubbo

1.Dubbo是什么?

答:DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC(远程过程调用协议)远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架

说白了就是个远程服务调用的分布式框架

其核心部分包含:
1).远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
2). 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
 3). 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

2.为什么要用Dubbo(优点和好处),解决什么问题?

答:Dubbo提供了三个关键功能:基于接口的远程调用,容错与负载均衡,服务自动注册与发现。

①透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。      
  ②软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
  ③服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

3.Dubbo如何暴露与发现服务?(一定要懂)

2019年JAVA最新面试题部分

 

答:

节点角色说明:

Provider: 暴露服务的服务提供方。

Consumer: 调用远程服务的服务消费方。

Registry: 服务注册与发现的注册中心。

Monitor: 统计服务的调用次调和调用时间的监控中心。

Container: 服务运行容器。

调用关系说明:

0).服务容器负责启动,加载,运行服务提供者。

1).服务提供者在启动时,向注册中心注册自己提供的服务。

2).服务消费者在启动时,向注册中心订阅自己所需的服务。

3).注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推

送变更数据给消费者。

4).服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,

如果调用失败,再选另一台调用。

5).服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计

数据到监控中心。

4.Dubbo 和 Spring Cloud 有什么区别?

答:Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。这两种方式各有优劣。对于类似于电商等同步调用场景多并且能支撑搭建Dubbo 这套比较复杂环境的成本的产品而言,Dubbo 确实是一个可以考虑的选择。虽然在一定程度上来说Spring Cloud牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适。

5.Dubbo 支持的通信协议有哪些?

答:1).dubbo协议(默认),使用NIO异步通信,单一长连接,hessian序列化协议,使用在传输数据量较小(每次请求在100kb以内),但是并发量很高的场景。

2)rmi(Remote Method Invocation 远程方法调用)java二进制序列化,使用在多个短连接,使用在消费者和提供者数量差不多的情况,适用于文件的传输,一般比较少用。

3).hessian协议,hessian序列化协议,多个短连接,使用在提供者比消费者数量还多的请你赶快,适用于文件传输,一般比较少用。

6.怎么实现远程通信

答:远程通信:简单来说,就是一个系统去调用另一个系统中的数据.

常见的有三种方式:

(1)Webservice的方式:

     1)优点:跨语言跨平台

     2)缺点:它是基于soap协议的,使用http+xml的方式进行数据传输,http是应用层协议,传输效率不是很高,而且xml的解析也比 较费时,所以项目内部进行通信的时候,不建议使用Websservice的方式
 

(2)restful形式的服务:

     1)优点:restful本身就是http,使用的是http+json的方式进行数据传输,因为json数据本身是非常简洁的,所以它比webservice的 传输效率更高;手机app端一般都使用该方法,其他很多项目也是用这种方式

     2)缺点:如果服务太多的话,会出现服务之间调用关系混乱,此时就需要治理服务

(3)Dubbo

     使用的是RPC协议进行远程调用,RPC协议是一个二进制协议,直接使用的socket进行通信,传输效率高,并且可以统计出系统 之间的调用关系和调用次数系统分布式SAO系统的内部通信推荐使用dubbo

7.Dubbo为什么使用异步单一长连接?

答:Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

8.Dubbo有哪几种集群容错方案,默认是哪种?

答:①Failover Cluster:失败重试

当服务消费方调用服务提供者失败后自动切换到其他服务提供者服务器进行重试。这通常用于读操作或者具有幂等的写操作,需要注意的是重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

②Failfast Cluster:快速失败

当服务消费方调用服务提供者失败后,立即报错,也就是只调用一次。通常这种模式用于非幂等性的写操作。

③Failsafe Cluster:失败安全

当服务消费者调用服务出现异常时,直接忽略异常。这种模式通常用于写入审计日志等操作。

④Failback Cluster:失败自动恢复

当服务消费端用服务出现异常后,在后台记录失败的请求,并按照一定的策略后期再进行重试。这种模式通常用于消息通知操作。

⑤Forking Cluster:并行调用

当消费方调用一个接口方法后,Dubbo Client会并行调用多个服务提供者的服务,只要一个成功即返回。这种模式通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

⑥Broadcast Cluster:广播调用

当消费者调用一个接口方法后,Dubbo Client会逐个调用所有服务提供者,任意一台调用异常则这次调用就标志失败。这种模式通常用于通知所有提供者更新缓存或日志等本地资源信息。

如上,Dubbo本身提供了丰富的集群容错模式,但是如果您有定制化需求,可以根据Dubbo提供的扩展接口Cluster进行定制。

9.Dubbo的异常机制是什么?

答:Dubbo有一个名为ExceptionFilter的异常过滤器,包含如下机制:

1).如果是checked异常,直接抛出 !

2).在方法签名上有声明,直接抛出 throws MyCustomException

3). 异常类和接口类在同一jar包里,直接抛出

4). 是JDK自带的异常,直接抛出(className.startsWith("java.") || className.startsWith("javax."))

5). 是Dubbo本身的异常,直接抛出(RpcException)

6). 其他异常通过RuntimeException

10.挂机_Dubbo挂机了怎么处理

答:首先zookeeper挂了还能调用服务,在启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用。但是在注册中心全部挂掉后增加新的提供者,则不能被消费者发现.(前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的。)

11.安全_dubbo+zookeeper如何解决不同系统间调用的安全性问题

答:Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。

(1)可以全局设置开启令牌验证:

<!--随机token令牌,使用UUID生成-->

<dubbo:provider interface="com.foo.BarService" token="true" />

<!--固定token令牌,相当于密码-->

<dubbo:provider interface="com.foo.BarService" token="123456" />

(2)也可在服务级别设置:

<!--随机token令牌,使用UUID生成-->

<dubbo:service interface="com.foo.BarService" token="true" />

<!--固定token令牌,相当于密码-->

<dubbo:service interface="com.foo.BarService" token="123456" />

(3)还可在协议级别设置:

<!--随机token令牌,使用UUID生成-->

<dubbo:protocol name="dubbo" token="true" />

<!--固定token令牌,相当于密码-->

<dubbo:protocol name="dubbo" token="123456" />

12.Zookeeper 介绍(谈谈你对zookeeper的了解)

答:官方推荐使用 zookeeper 注册中心。注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。

Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为Dubbox 服务的注册中心,工业强度较高,可用于生产环境。

13.作用_zookeeper注册中心的作用?端口是多少

答:(1)作用:Zookeeper注册中心的作用主要就是注册和发现服务的作用。类似于房产中介的作用,在系统中并不参与服务的调用及数据的传输。

(2)端口及作用

①2181:对cline端提供服务

②3888:选举leader使用

③2888:集群内机器通讯使用(Leader监听此端口)

14.zookeeper在项目中怎么用的

答:1.作为dubbo的注册中心,暴露服务,然后消费方订阅服务用的

2.作为solr集群的调配中心,达到负载均衡的效果

15.缺陷_zookeeper存在什么缺陷

答:①本身不是为高可用性设计,master撑不住高流量容易导致系统crash;②zookeeper的选举过程速度很慢;③难以避免数据的不一致;④zookeeper的性能是有限的

 

3.2springmvc

1.讲下 springmvc框架的工作流程

答一:1)、用户向服务器发送请求,请求被 SpringMVC的前端控制器 DispatcherServlet截获。

2)、DispatcherServlet对请求的   URL(统一资源定位符)进行解析,得到 URI(请求资源标识符),然后根据该 URI,调用 HandlerMapping获得该  Handler配置的所有相关的对象,包括  Handler对象以及 Handler对象对应的拦截器,这些对象都会被封装到一个  HandlerExecutionChain对象当中返回。

3)、DispatcherServlet根据获得的   Handler,选择一个合适的 HandlerAdapter。HandlerAdapter的设计符合面向对象中的单一职责原则,代码结构清晰,便于维护,最为重要的是,代码的可复制性高。HandlerAdapter会被用于处理多种  Handler,调用 Handler实际处理请求的方法。

4)、提取请求中的模型数据,开始执行 Handler(Controller)。在填充 Handler的入参过程中,根据配置,spring将帮助做一些额外的工作

消息转换:将请求的消息,如 json、xml等数据转换成一个对象,将对象转换为指定的响应信息。

数据转换:对请求消息进行数据转换,如 String转换成 Integer、Double等。

数据格式化:对请求的消息进行数据格式化,如将字符串转换为格式化数字或格式化日期等。

数据验证:验证数据的有效性如长度、格式等,验证结果存储到 BindingResult或  Error中。

5)、Handler执行完成后,向  DispatcherServlet返回一个 ModelAndView对象ModelAndView对象中应该包含视图名或视图模型。

6)、根据返回的ModelAndView对象,选择一个合适的 ViewResolver(视图解析器) 返回给

DispatcherServlet。

7)、ViewResolver结合  Model和 View来渲染视图。

8)、将视图渲染结果返回给客户端。

以上 8个步骤,DispatcherServlet、HandlerMapping、HandlerAdapter和    ViewResolver等对象协同工

作,完成 SpringMVC请求—>响应的整个工作流程,这些对象完成的工作对于开发者来说都是不可

见的,开发者并不需要关心这些对象是如何工作的,开发者,只需要在 Handler(Controller)当中完成对请求的业务处理。

答二:
(1)用户发送请求至前端控制器DispatcherServlet;

(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;

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

DispatcherServlet;

(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;

(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);

(6)Handler执行完成返回ModelAndView;

(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;

(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;

(9)ViewResolver解析后返回具体View;

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

(11)DispatcherServlet响应用户。

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

答:Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

3、Springmvc有什么优点:

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

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

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

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

4、Spring MVC的主要组件?

答:(1)前端控制器 DispatcherServlet(不需要程序员开发)

作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。

(2)处理器映射器HandlerMapping(不需要程序员开发)

作用:根据请求的URL来查找Handler

(3)处理器适配器HandlerAdapter

注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。

(4)处理器Handler(需要程序员开发)

(5)视图解析器 ViewResolver(不需要程序员开发)

作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)

(6)视图View(需要程序员开发jsp)

View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)

5、springMVC和struts2的区别有哪些?

答:(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。

(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。

6.SpringMVC怎么样设定重定向和转发的?

答:①转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"

②重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"

7、SpringMvc怎么和AJAX相互调用的?

答:通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :

1)加入Jackson.jar

2)在配置文件中配置json的映射

3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。

8、如何解决POST请求中文乱码问题,GET的又如何处理呢?

答:(1)解决post请求乱码问题:

在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;

(2)get请求中文参数出现乱码解决方法有两个:

①修改tomcat配置文件添加编码与工程编码一致

 ②另外一种方法对参数进行重新编码:

9、Spring MVC的异常处理

答:可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。

10、SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?

答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。

11、 SpringMVC常用的注解有哪些?

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

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

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

12、SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?

答:一般用@Conntroller注解,表示是表现层,不能用别的注解代替。

13、如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?

答:可以在@RequestMapping注解里面加上method=RequestMethod.GET。

14、怎样在方法里面得到Request,或者Session

答:直接在方法的形参中声明request,SpringMvc就自动把request对象传入。

15、如果想在拦截的方法里面得到从前台传入的参数,怎么得到?

答:直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。

16、如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?

答:直接在方法中声明这个对象,SpringMvc就自动会把属性赋值到这个对象里面。

17、SpringMvc中函数的返回值是什么?

答:返回值可以有很多类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一起的,但一般用String比较好。

18、SpringMvc用什么对象从后台向前台传递数据的?

答:通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表达式拿到。

19、怎么样把ModelMap里面的数据放入Session里面?

答:可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。

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

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

21、注解原理:

答:注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。

 

3.3 Mybatis
1、什么是Mybatis

答:(1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

(2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

(3)通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。

2、Mybaits有什么优点:

答:(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;

(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。

(4)能够与Spring很好的集成;

(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

3、MyBatis框架的缺点:

答:(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

4、MyBatis框架适用场合:

答:①MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。

②对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。

5、MyBatis与Hibernate有哪些不同?

答:(1)Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。

(2)Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。 

(3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。 

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

答:#{}是预编译处理,${}是字符串替换。

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

Mybatis在处理${}时,就是把${}替换成变量的值。使用#{}可以有效的防止SQL注入,提高系统安全性。

7、当实体类中的属性名和表中的字段名不一样 ,怎么办

答:第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。第2种: 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。

8.请你描述一下 myBatis 的缓存

答:MyBatis 的缓存分为一级缓存和二级缓存, 一级缓存放在 session 里面,默认就有,二级缓存放在它的命名空间里,默认是打开的,二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文 件中配置Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。

一级缓存是指SqlSession级别的缓存,当在同一个SqlSession中进行相同的SQL语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存1024条SQL。二级缓存是指可以跨SqlSession的缓存。Mybatis中进行SQL查询是通过org.apache.ibatis.executor.Executor接口进行的,总体来讲,它一共有两类实现,一类是BaseExecutor,一类是CachingExecutor。前者是非启用二级缓存时使用的,而后者是采用的装饰器模式,在启用了二级缓存时使用,当二级缓存没有命中时,底层还是通过BaseExecutor来实现的。

一级缓存

一级缓存是默认启用的,在BaseExecutor的query()方法中实现,底层默认使用的是PerpetualCache实现,PerpetualCache采用HashMap存储数据。一级缓存会在进行增、删、改操作时进行清除。

二级缓存

二级缓存是默认启用的,如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。我们要想使用二级缓存,是需要在对应的Mapper.xml文件中定义其中的查询语句需要使用哪个cache来缓存数据的。这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。但是需要注意的是对于同一个Mapper来讲,它只能使用一个Cache,当同时使用了和时使用定义的优先级更高。Mapper使用的Cache是与我们的Mapper对应的namespace绑定的,一个namespace最多只会有一个Cache与其绑定

9.myBatis 实现一对一和一对多分别有几种方式,又都是怎么操作的呢

答:(1)一对一有联合查询和嵌套查询 联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置collection节点配置一对 多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是 通过配置collection,但另外一个表的查询通过 select 节点配置

(2)一对多有联合查询和嵌套查询 联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置collection节点配置一对 多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是 通过配置collection,但另外一个表的查询通过 select 节点配置;

10.myBatis 里面的动态 Sql 是怎么设定的?用什么语法?

答:MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,通过 OGNL 语法来实现,但是如果要写的 完整,必须配合where,trim 节点,where 节点是判断包含节点有内容就插入 where,

否则不插 入,trim 节点是用来判断如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or 取 掉;

11.myBatis(IBatis)的好处是什么

答:ibatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的维护带 来了很大便利。ibatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象,大大简 化了 Java 数据库编程的重复工作。因为 Ibatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活控制 sql 语句,因此能够实现比 hibernate 等全自动 orm 框架更高的查询效率,能够完成复杂查询

12.接口绑定有几种实现方式,分别是怎么实现的?

答:a.一种是通过注解绑定,就是在接口的方法上面加上@[email protected] 等注解里面包含 Sql 语句来绑定,b,另外一种就是通过 xml 里面写 SQL 来绑定,在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名;

13.什么情况下用注解绑定,什么情况下用 xml 绑定

答:当 Sql 语句比较简单时候,用注解绑定, 当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多;

14.Mapper动态代理开发

答:只写接口,实现类由mybatis生成

四个原则:Mapper接口开发需要遵循以下规范

①Mapper.xml文件中的namespace与mapper接口的类路径相同。

②Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

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

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

15.有关mybatis接口方法中传入参数问题

答:mybatis参数传递主要分为以下的五种情况

1)单个参数

可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理。

2)多个参数

任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值。

3)命名参数(推荐使用)

为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字

4)POJO   

当这些参数属于我们业务POJO时,我们直接传递POJO

5)Map

我们也可以封装多个参数为map,直接传递

总结:

(1)当参数小于5时候通常使用@Param形式

(2)参数大于5时候使用JavaBean形式

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

答:第一种是使用<resultMap>标签,逐一定义数据库列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列的别名书写为对象属性名。有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

17.如何获取自动生成的(主)键值?

答:insert 方法总是返回一个int值 ,这个值代表的是插入的行数。如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。

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

答:Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。

Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

19.Mbatis中使用到的设计模式

答:Mybatis至少使用到了以下的设计模式:

1).Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、

XMLStatementBuilder、CacheBuilder;

2).工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;

3).单例模式,例如ErrorContext和LogFactory;

4).代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;

5).组合模式,例如SqlNode和各个子类ChooseSqlNode等;

6).模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;

7).适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;

8).装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;

9).迭代器模式,例如迭代器模式PropertyTokenizer;

20.JDBC编程有哪些不足之处,MBatis是如何解决这些问题的?

答:(1)数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。

(2)Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

(3)向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

解决: Mybatis自动将java对象映射至sql语句。

(4)对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象

21.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

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

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

②它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

22.使用MyBatismapper接口调用时有哪些要求?

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

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

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

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

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

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

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

 

3.4 Spring

1. 什么是spring?

答:Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。

2. 使用Spring框架的好处是什么?

答:①轻量:Spring 是轻量的,基本的版本大约2MB。

②控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。

③面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。

④容器:Spring 包含并管理应用中对象的生命周期和配置。

⑤MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。

⑥事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。

⑦异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

3.解释AOP模块

答:AOP模块用于发给我们的Spring应用做面向切面的开发, 很多支持由AOP联盟提供,这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring。

4.在Spring AOP 中,关注点和横切关注的区别是什么?

答:①关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。
    ②横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。

5.什么是代理?

答:代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。

6.谈谈你对spring IOC和DI的理解

答:IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spring框架管理,简单说,就是创建UserService对象控制权被反转到了Spring框架

DI:Dependency Injection 依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件

7.BeanFactory 接口和 ApplicationContext 接口有什区别

答:①ApplicationContext 接口继承BeanFactory接口,Spring核心工厂是BeanFactory ,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文件时初始化Bean。

  ②ApplicationContext是对BeanFactory扩展,它可以进行国际化处理、事件传递和bean自动装配以及各种不同应用层的Context实现 开发中基本都在使用ApplicationContext, web项目使用WebApplicationContext ,很少用到BeanFactory

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

IHelloService helloService = (IHelloService) beanFactory.getBean("helloService");

helloService.sayHello();

8.Bean注入属性有哪几种方式?

答:①spring支持构造器注入和setter方法注入

    ②构造器注入,通过 <constructor-arg> 元素完成注入

    ③setter方法注入, 通过<property> 元素完成注入【开发中常用方式】

9.什么是AOPAOP的作用是什么?

答:面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足,除了类(classes)以外,AOP提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理

Spring的一个关键的组件就是AOP框架,可以*选择是否使用AOP 提供声明式企业服务,特别是为了替代EJB声明式服务。最重要的服务是声明性事务管理,这个服务建立在Spring的抽象事物管理之上。允许用户实现自定义切面,用AOP来完善OOP的使用,可以把Spring AOP看作是对Spring的一种增强

10.Spring的核心类有哪些,各有什么作用

答:①BeanFactory:产生一个新的实例,可以实现单例模式

②BeanWrapper:提供统一的get及set方法

③ApplicationContext:提供框架的实现,包括BeanFactory的所有功能

11.介绍一个spring的事物管理

 答: 事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。

开发中为了避免这种情况一般都会进行事务管理。Spring中也有自己的事务管理机制,一般是使用TransactionMananger进行管 理,可以通过Spring的注入来完成此功能。spring提供了几个关于事务处理的类:

TransactionDefinition //事务属性定义

TranscationStatus //代表了当前的事务,可以提交,回滚。

PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类 AbstractPlatformTransactionManager,我们使用的事务管理类例如 DataSourceTransactionManager等都是这个类的子类。

12.怎样用注解的方式配置spring

答:①Spring在2.5版本以后开始支持用注解的方式来配置依赖注入。可以用注解 的方式来替代XML方式的bean描述,可以将bean描述转移到组件类的内部,只需要在相关类上、方法上或者字段声明上使用注解即可。注解注入将会被容器在XML注入之前被处理,所以后者会覆盖掉前者对于同一个属性的处理结果。

②注解装配在Spring中是默认关闭的。所以需要在Spring文件中配置一下才能使用基于注解的装配模式。如果你想要在你的应用程序中使用关于注解的方法的话,请参考如下的配置。

③在 <context:annotation-config/>标签配置完成以后,就可以用注解的方式在Spring中向属性、方法和构造方法中自动装配变量。

下面是几种比较重要的注解类型:

1). @Required:该注解应用于设值方法。

2)[email protected]:该注解应用于有值设值方法、非设值方法、构造方法和变量。

3)[email protected]:该注解和@Autowired注解搭配使用,用于消除特定bean自动装配的歧义。

4).JSR-250 Annotations:Spring支持基于JSR-250 注解的以**解,@Resource、@PostConstruct 和 @PreDestroy。

13.简述一下spring框架的优点?

答:①方便解耦,简化开发  (高内聚低耦合)

Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理;Spring工厂是用于生成bean

②AOP编程的支持

Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

声明式事务的支持,

③只需要通过配置就可以完成对事务的管理,而无需手动编程

方便程序的测试

④Spring对Junit4支持,可以通过注解方便的测试Spring程序

方便集成各种优秀框架

⑤Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持

14.简述一下IOC容器对BEAN的生命周期?

答:①通过构造器或工厂方法创建 Bean 实例

②为Bean 的属性设置值和对其他 Bean 的引用

③将Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessBeforeInitialization 方法

④调用 Bean 的初始化方法(init-method)

⑤将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessAfterInitialization 方法

⑥Bean 可以使用了

⑦当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

15.在Spring中AOP的使用场景有哪些?

答:主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
  主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业   务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务 逻辑的方法中,进而改  变这些行为的时候不影响业务逻辑的代码。

16.Spring支持的事务管理类型

答:Spring支持两种类型的事务管理:

①编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

②声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

17.Spring分布式事务如何处理的

答:第一种方案:可靠消息最终一致性,需要业务系统结合 MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制 MQ的消息状态;

第二种方案:TCC补偿性,分为三个阶段   TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理。

1).TRYING阶段主要是对业务系统进行检测及资源预留

2)CONFIRMING阶段是做业务提交,通过 TRYING阶段执行成功后,再执行该阶段。默认如果   TRYING阶段执行成功,CONFIRMING就一定能成功。

3)CANCELING阶段是回对业务做回滚,在 TRYING阶段中,如果存在分支事务  TRYING失败,则需要调用 CANCELING将已预留的资源进行释放

 

四、微服篇

4.1SpringBoot

1.什么是SpringBoot
  答:Spring Boot是Spring社区发布的一个开源项目,旨在帮助开发者快速并且更简单的构建项目。大多数SpringBoot项目只需要很少的配置文件。
2、SpringBoot核心功能
  答: ①独立运行Spring项目:Spring boot 可以以jar包形式独立运行,运行一个Spring Boot项目只需要通过java -jar xx.jar来运行。
   ②内嵌servlet容器:Spring Boot可以选择内嵌Tomcat、jetty或者Undertow,这样我们无须以war包形式部署项目。
   ③提供starter简化Maven配置:  spring提供了一系列的start pom来简化Maven的依赖加载,例如,当你使用了spring-boot-starter-web,会自动加入如图5-1所示的依赖包。
   ④自动装配Spring

SpringBoot会根据在类路径中的jar包,类、为jar包里面的类自动配置Bean,这样会极大地减少我们要使用的配置。当然,SpringBoot只考虑大多数的开发场景,并不是所有的场景,若在实际开发中我们需要配置Bean,而SpringBoot灭有提供支持,则可以自定义自动配置。
  ⑤准生产的应用监控:SpringBoot提供基于http ssh telnet对运行时的项目进行监控。
  ⑥无代码生产和xml配置  
     SpringBoot不是借助与代码生成来实现的,而是通过条件注解来实现的,这是Spring4.x提供的新特性。
3、SpringBoot优缺点
   答:优点:
  1).快速构建项目。
  2).对主流开发框架的无配置集成。
  3)项目可独立运行,无须外部依赖Servlet容器。
  4).提供运行时的应用监控。
  5).极大的提高了开发、部署效率。
  6).与云计算的天然集成。
 缺点:如果你不认同spring框架,也许这就是缺点。
4、SpringBoot特性
   答:①创建独立的Spring项目
    ②内置Tomcat和Jetty容器
    ③提供一个starter POMs来简化Maven配置
    ④提供了一系列大型项目中常见的非功能性特性,如安全、指标,健康检测、外部配置等
    ⑤完全没有代码生成和xml配置文件
5、SpringBoot CLI
   答:SpringBoot CLI 是SpringBoot提供的控制台命令工具。
6、SpringBoot maven 构建项目
   答:spring-boot-starter-parent:是一个特殊Start,它用来提供相关的Maven依赖项,使用它之后,常用的包依赖可以省去version标签。
9SpringBoot几个常用的注解
   答:1)@RestController和@Controller指定一个类,作为控制器的注解 
       2)@RequestMapping方法级别的映射注解,这一个用过Spring MVC的小伙伴相信都很熟悉 
       3)@EnableAutoConfiguration和@SpringBootApplication是类级别的注解,根据maven依赖的jar来自动猜测完成正确的spring的对应配置,只要引入了spring-boot-starter-web的依赖,默认会自动配置Spring MVC和tomcat容器
       4)@Configuration类级别的注解,一般这个注解,我们用来标识main方法所在的类,完成元数据bean的初始化。
       5)@ComponentScan类级别的注解,自动扫描加载所有的Spring组件包括Bean注入,一般用在main方法所在的类上 
       6)@ImportResource类级别注解,当我们必须使用一个xml的配置时,使用@ImportResource和@Configuration来标识这个文件资源的类。 
      7)@Autowired注解,一般结合@ComponentScan注解,来自动注入一个Service或Dao级别的Bean
           8)@Component类级别注解,用来标识一个组件,比如我自定了一个filter,则需要此注解标识之后,Spring Boot才会正确识别。

10.Springboot用过没,跟我说说,他的特点?

答: Springboot是从无数企业实战开发中总结出来的一个更加精炼的框架,使得开发更加简单,能使用寥寥数行代码,完成一系列任务。

1)Springboot解决那些问题

a)编码更简单

   ①Spring框架由于超重量级的  XML,annotation配置,使得系统变得很笨重,难以维护

②Springboot采用约点大于配置的方法,直接引入依赖,即可实现代码的开发

b)配置更简单

①Xml文件使用 javaConfig代替,XML中   bean的创建,使用@bean代替后可以直接注入。

②配置文件变少很多,就是  application.yml

c)部署更简单

    ① 一键启动,一键解压; ②不需要应用服务器:Tomcat(X)weblogic(x);

    ③降低对运行环境基本要求:部署环境只需要有JDK即可,默认内置tomcat服务器

d)监控更简单

Spring-boot-start-actuator: 可以查看属性配置;线程工作状态;环境变量;

JVM性能监控

4.2.springCloud(自己回去查阅一下资料哈)

 

五、项目的部分

5.1.项目高并发处理方案

1.线程和进程的区别:

答:(1)进程是一个 “执行中的程序”,是系统进行资源分配和调度的一个独立单位;

(2)线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其它资源(所以通信和同步等操作线程比进程更加容易);

(3).线程上下文的切换比进程上下文切换要快很多。

①进程切换时,涉及到当前进程的 CPU 环境的保存和新被调度运行进程的 CPU 环境的设置。

②线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。

或者所有,任何形式的转载都请联系作者获得授权并注明出处

2.什么是阻塞(Blocking)和非阻塞(Non-Blocking)?

答:阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区资源,那么其他所有需要这个而资源的线程就必须在这个临界区中进行等待。等待会导致线程挂起,这种情况就是阻塞。此时,如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区上的线程都不能工作。

非阻塞的意思与之相反,它强调没有一个线程可以妨碍其他线程执行。所有的线程都会尝试不断前向执行。

3.Java 中线程有几种状态?

答:六种(查看 Java 源码也可以看到是 6 种),并且某个时刻 Java 线程只能处于其中的一个状态。

1).新建(NEW)状态:表示新创建了一个线程对象,而此时线程并没有开始执行。

2).可运行(RUNNABLE)状态:线程对象创建后,其它线程(比如 main 线程)调用了该对象的 start() 方法,才表示线程开始执行。当线程执行时,处于 RUNNBALE 状态,表示线程所需的一切资源都已经准备好了。该状态的线程位于可运行线程池中,等待被线程调度选中,获取 cpu 的使用权。

3).阻塞(BLOCKED)状态:如果线程在执行过程终于到了 synchronized 同步块,就会进入 BLOCKED 阻塞状态,这时线程就会暂停执行,直到获得请求的锁。

4).等待(WAITING)状态:当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。在调用Object.wait方法或Thread.join方法,或者是等待java.util.concurrent库中的Lock或Condition时,就会出现这种情况;

5).计时等待(TIMED_WAITING)状态:Object.wait、Thread.join、Lock.tryLock和Condition.await 等方法有超时参数,还有 Thread.sleep 方法、LockSupport.parkNanos 方法和 LockSupport.parkUntil 方法,这些方法会导致线程进入计时等待状态,如果超时或者出现通知,都会切换会可运行状态;

6).终止(TERMINATED)状态:当线程执行完毕,则进入该状态,表示结束。

注意:从 NEW 状态出发后,线程不能再回到 NEW 状态,同理,处于 TERMINATED 状态的线程也不能再回到RUNNABLE 状态

4.sleep( ) 和 wait( n)、wait( ) 的区别:

答:①sleep 方法:是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话);

②wait 方法:是 Object 的方法,必须与 synchronized 关键字一起使用,线程进入阻塞状态,当 notify 或者notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,释放互斥锁;

5.synchronized 关键字:

答:底层实现:

1). 进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;

2). 当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。

含义:(monitor 机制)

Synchronized 是在加锁,加对象锁。对象锁是一种重量锁(monitor),synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。

6.什么是进程

答:进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间)。

比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左边的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。

8.什么是线程

答:进程是表示自愿分配的基本单位。而线程则是进程中执行运算的最小单位,即执行处理机调度的基本单位。通俗来讲:一个程序有一个进程,而一个进程可以有多个线程。

9.多线程的几种实现方式

答:(1) 继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法将启动一个新线程,并执行run()方法。这种方式实现多线程比较简单,通过自己的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。

(2) 实现Runnable接口创建线程

如果自己的类已经继承了两一个类,就无法再继承Thread,因此可以实现一个Runnable接口

(3) 实现Callable接口通过FutureTask包装器来创建Thread线程

(4) 使用ExecutorService、Callable、Future实现有返回结果的线程

ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。

可返回值的任务必须实现Callable接口;无返回值的任务必须实现Runnabel接口。

执行Callable任务后,可以获取一个Future对象,在该对象上调用get()方法就可以获取到Callable任务返回的Object了。(get()方法是阻塞的,线程无返回结果,该方法就一直等待)

10.Vector、SimpleDateFormat是线程安全类吗

答:Vector类的单个方法是原子性的,符合操作不是原子性的

SimpleDateFormat类不是线程安全的

11.哪些集合类是线程安全的

答:①Vector;②Stack;③hashtable;④enumeration;⑤StringBuffer

12.多线程中忙循环是什么

答:忙循环就是程序员用循环让一个线程等待,不像传统方法wait()、sleep()或者yied()它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。

13.什么是线程局部变量

答:ThreadLocal并非是一个线程本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更合适。线程局部变量(ThreadLocal)功能非常简单,就是为每一个使用该变量的线程都提供了一个变量值副本,是java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。

14.什么是多线程环境下的伪共存(false sharing

答:缓存系统中是以缓存行为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存航,就会无意中影响彼此的性能,这就是伪共存

15.同步和异步有何不同,在什么情况下分别使用它们?举例说明

答:如果数据将在线程间共享。例如:正在写的数据以后可能会被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取

当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效。

同步交互:指发送一个请求,需要等待返回,然后才能发送下一个请求,有个等待的过程

异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。

区别:一个需要等待,一个不需要等待

 16.Thread类中的strat()和run()方法有什么区别?

答:start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动,而调用start()方法会启动一个新的线程。

17.java中Runnable和Callable的区别

答:Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。

他们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

18.什么是线程安全?

答:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果都是一样的,而且其他变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。

19.如何在两个线程间共享数据?

答:可以通过共享对象来实现这个目的,或者是使用阻塞队列,或者使用wait()和notify()方法

20.锁池和等待池

答:锁池:假设线程A已经拥有了某个对象的锁,而其它的线程想要调用这个对象的某个Synchronized方法(或者Synchronized代码块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这个线程就进入了该对象的锁池中

等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到该对象的等待池中。

21.什么是FutureTask?

答:在java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能返回,如果运算尚未完成,get()方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnbale接口所以它可以提交给Executor来执行

22.为什么你应该在循环中检查等待条件

答:处于等待状态的线程可能会受到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下推出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因。

23. java中堆和栈有什么不同?

答:为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量、方法参数和栈调用,一个线程中存储的变量对其他线程是不可见的。而堆是所有线程共享的一片公共内存区域。对象都在堆里创建,为了提升效率,线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile变量就可以发挥作用了,它要求线程从主存中读取变量的值

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

答:创建线程需要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变成,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,他们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,java API提供了Executor框架让你可以创建不同的线程池。比如单线程吃,数目固定的线程池等

25.有三个线程T1、T2和T3,怎么确保它们按照顺序执行?

答:在多线程中有多重方法让线程按特定的顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成

26.什么是阻塞式方式?

答:阻塞式方式是指程序会一直等待该方法完成期间不做其他事情,ServiceSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到等到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

5.2 数据传输的安全性解决方案

1.接口与接口之间的调用,如何保证数据安全

答:①通信使用https

②请求签名,防止参数被篡改

③身份确认机制,每次请求都要验证是否合法

④app中使用ssl pinning 防止抓包操作

⑤对所有请求和响应都进行加解密操作

ssl pinning:它是在开发时就将服务端证书(ssl证书)一块打包到客户端里。这样在HTTPS建立时与服务端返回的证书比对一致性,进而识别出中间人攻击后直接在客户端侧中止连接。

在实际的工作中,情况差不多为以下(分两种):

       1)公司内部的接口

         公司内部的接口,当然是涉及到比较隐秘信息的时候,调用方需要持有一个私钥,调用的时候将传入的参数通过私钥进行的加密,若加密后的内容能够被公钥解密,那么则能够通过。

   2)调用第三方的接口

          在调用第三方的接口的时候,实现的方式也有两种:

          1、利用私钥加密。

         2、利用http的请求头的要求数据和格式展开访问控制,如:在调用第三方接口的时候,需要head中的内容必须是:token:xxx,sign:xxx,client_id:xxx,且sign是经过加密的内容。从而达到访问控制。

2.HTTPS的优点

 答:尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS仍是现行架构下最安全的解决方案,主要有以下几个好处:

①使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;

②HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。

③HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。

④谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密的网站在搜索结果中的排名将会更高”。

3.HTTPS的缺点

答:虽然说HTTPS有很大的优势,但其相对来说,还是存在不足之处的:

  ①HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;

  ②HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;

  ③SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。

④HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

4.HTTP与HTTPS有什么区别?

答:HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

HTTPS和HTTP的区别主要如下:

1).https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2).http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

 3).http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

 4).http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

 

5.3 redis作缓存(即门户模块)

1. Redis有哪些数据结构?

答:字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。

如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。

如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。

2. 使用过Redis分布式锁么,它是什么回事?

答:先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnxexpire合成一条指令来用的!

3. Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

答:使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

4. 使用过Redis做异步队列么,你是怎么用的?

答:一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

5. 如果有大量的key需要设置同一时间过期,一般需要注意什么?

答:如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

6. Redis如何做持久化的?

答:bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复。

如果再问aof文件过大恢复时间过长怎么办?你告诉面试官,Redis会定期做aof重写,压缩aof文件日志大小。如果面试官不够满意,再拿出杀手锏答案,Redis4.0之后有了混合持久化的功能,将bgsave的全量和aof的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性。这个功能甚至很多面试官都不知道,他们肯定会对你刮目相看。

如果对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

7. 使用redis的目的是什么?

答:项目中使用redis一般都是作为缓存来使用的,缓存的目的就是为了减轻数据库的压力提高存取的效率。

8. Redis的同步机制了解么?

答:从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

9. 是否使用过Redis集群,集群的原理是什么?

答:Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

10. 使用Redis有哪些好处?

答:(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1) 
    (2) 支持丰富数据类型,支持string,list,set,sorted set,hash 
    (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 
    (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

11. redis相比memcached有哪些优势?

答:(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 
     (2) redis的速度比memcached快很多 
     (3) redis可以持久化其数据

12. redis常见性能问题和解决方案:

答:(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 
(4) 尽量避免在压力很大的主库上增加从库 
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

13. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

答:相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

14. Memcache与Redis的区别都有哪些?

答:1)、存储方式

Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

Redis有部份存在硬盘上,这样能保证数据的持久性。

2)、数据支持类型

Memcache对数据类型支持相对简单。

Redis有复杂的数据类型。

3)、使用底层模型不同

它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

4).value大小:redis最大可以达到1GB,而memcache只有1MB

15. Redis 常见的性能问题都有哪些?如何解决?

答:1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。

2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。

4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

16, redis 最适合的场景

答:Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢? 
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点: 
   1).Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 
   2).Redis支持数据的备份,即master-slave模式的数据备份。 
   3).Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 
   (1)会话缓存(Session Cache

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?

幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

(2)全页缓存(FPC

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。

再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。

此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

(3)队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

(4)排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

(5)发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。

Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。

17、Redis集群方案应该怎么做?都有哪些方案?

答:1).codis

目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新hash节点。

2).redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。

3).在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key 进行hash计算,然后去对应的redis实例操作数据。 这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。

18、Redis集群方案什么情况下会导致整个集群不可用?

答:有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

19.redis快速的原因

答:①完全基于内存,大部分请求都是纯粹的内存操作,非常快速,数据存储在内存中,例如HashMap,优势是查找和操作的时间复杂度 都是O(1);

②数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

③采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或多线程导致的切换而消耗cpu,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

④使用异步非阻塞IO;(重点说)(同步/异步:是否主动读写数据;阻塞/非阻塞:是否需要等待.)

⑤底层自己构建了VM机制,因为一般系统调用系统函数的话会浪费一定的时间去移动和请求。

20.工作中Redis的持久化方案,你们是怎么做的

  答:持久化方案用的是RDB;RDB 是 Redis 默认的持久化方案。

在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。

21.为啥用redis做缓存,而不用HashMap做缓存

答:1).redis 数据可持久化保存,有些缓存你想重启程序后还能继续使用,而map实现不了
    2).redis 可以实现分布式部署,只要涉及到多台多进程啥的,map就实现不了

3).redis 有很多数据结构,方便操作,比如 hash set list sort-set等,在某些场景下操作起来比map方便。

22.Redis 是 nosql 数据库,是否适合存储大数据

答:Redis 是 nosql 数据库,但是 redis 是 key-value 形式的 nosql 数据库,数据是存储到内存中的,适合于快速存取,一般作为缓存使用。

并且 redis 是单线程的如果某个操作进行大数据的存储的话其他的进程都处于等待状态,这样就降低了性能。

所以在 redis 中不适合于大数据的存储。

如果是类似商品评论这样的价值不高的大批量数据,我们的做法是采用 mongodb。

23.穿透_雪崩_Redis缓存穿透和redis雪崩处理

答:(1)缓存穿透

​ 一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,应该去后端系统查询(比如数据库)。如果key对应的value不存在,并且对key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

(2)缓存雪崩

​ 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如数据库)带来很大的压力。

​ 缓存雪崩是指在我们设置缓存是采用了相同的过期时间,导致缓存在某一时期同时失效,请求全部转发到DB,DB瞬间压力过大雪崩。

解决方案

把缓存时间分散开,比如在原来的时间基础上增加一个随机值,比如1~5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

24.集群_redis怎么做集群

答:为了保证可以进行投票,需要至少3个主节点。每个主节点都需要至少一个从节点,所以需要至少3个从节点,一共需要6台redis服务器可以使用6个redis实例6个redis实例的端口号,7001~7006,因为集群中每个redis除了端口号,其他配置其实一样,所以我们可以先配置好一个集群的redis,然后赋值,修改端口号即可。6台准备完毕之后,需要开启集群策略,然后再这基础上创建集群环境。

25.删除_你们怎么处理redis缓存的数据,怎么删除的

答:redis缓存的数据有一些是常驻缓存的,当数据库中数据有变化时做数据同步。
有一些缓存是设置有效期的,当缓存到期后会自动删除。删除redis缓存使用del或者hdel命令。[备注:hdel,hashKey delete]

26.redis的事务

答:(1).redis事务:可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞.

(2).redis事务能干嘛:一个队列中,一次性、顺序性、排他性的执行一系列命令.

[备注: ACID,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)]

(3).redis监控:锁的介绍

在MySQL中我们都知道有行锁和表锁的概念,所谓的行锁也就是把我需要改的那一行给锁住,不让其他的事务去修改;而表锁就是在修改一张表的时候把整张表都锁住,不让其他的事务修改,所以行锁的效率比表锁的概念更高,那么在redis也存在锁的概念

①乐观锁(Optimistic Lock): 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新

②悲观锁(Pessimistic Lock): 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.[备注: 在Java中,synchronized的思想也是悲观锁]

③CAS(Check And Set):

(4)redis事务三阶段:

①开启:以MULTI开始一个事务

②入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面

③执行:由EXEC命令触发事务(execute,执行)

(5).redis事务三大特性:

①单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

②没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题

③不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

 

5.4. ES(搜索模块)

1.什么是ES

答:es是一个高扩展、开源的全文检索和分析引擎,它可以准实时地快速存储、搜索、分析海量的数据。

2.为什么要使用到ES

答:因为在我们商城中的数据,将来会非常多,所以采用以往的模糊查询,模糊查询前置配置,会放弃索引,导致商品查询是全表扫面,在百万级别的数据库中,效率非常低下,而我们使用ES做一个全文索引,我们将经常查询的商品的某些字段,比如说商品名,描述、价格还有id这些字段我们放入我们索引库里,可以提高查询速度。

3.Elasticsearch是如何实现Master选举的?

答:①Elasticsearch的选主是ZenDiscovery模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;

②对所有可以成为master的节点(node.master: true)根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。

③如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

补充:master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data节点可以关闭http功能。

4.Elasticsearch中的节点(比如共20个),其中的10个选了一个master,另外10个选了另一个master,怎么办?

答:1)当集群master候选数量不小于3个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题;

2)当候选数量为两个时,只能修改为唯一的一个master候选,其他作为data节点,避免脑裂问题。

5.客户端在和集群连接时,如何选择特定的节点执行请求的?

答:TransportClient利用transport模块远程连接一个elasticsearch集群。它并不加入到集群中,只是简单的获得一个或者多个初始化的transport地址,并以 轮询 的方式与这些地址进行通信。

6.详细描述一下Elasticsearch索引文档的过程。

答:协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片。

shard = hash(document_id) % (num_of_primary_shards)

①当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh;

②当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;

③在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。

④flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;

补充关于Lucene的Segement

1).Lucene索引是由多个段组成,段本身是一个功能齐全的倒排索引。

2).段是不可变的,允许Lucene将新的文档增量地添加到索引中,而不用从头重建索引。

3)对于每一个搜索请求而言,索引中的所有段都会被搜索,并且每个段会消耗CPU的时钟周、文件句柄和内存。这意味着段的数量越多,搜索性能会越低。

4).为了解决这个问题,Elasticsearch会合并小段到一个较大的段,提交新的合并段到磁盘,并删除那些旧的小段。

7.详细描述一下Elasticsearch更新和删除文档的过程。

答:①删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;

②磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。

③在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

8.详细描述一下Elasticsearch搜索的过程。

答:①搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch;

②在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。

③每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。

④接下来就是 取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

补充:

Query Then Fetch的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确,DFS Query Then Fetch增加了一个预查询的处理,询问Term和Document frequency,这个评分更准确,但是性能会变差。

9.为什么使用索引工具查询快

答:(使用了倒排索引的技术,大致介绍一下倒排索引,还有索引库中的词都是按照顺序排列,后期根据一个关键词查询的时候,可以利用类似折半查找的算法,查询效率非常高)

使用了倒排索引的技术,一般我们都是这样定义id  关键词,倒排索引是关键词  id正好相反,使用索引工具进行查询时,首先得到关键词,建立倒排索引表,关键词----索引列表包含该关键词所在的文档的id、在该文档中出现的次数、在该文档中出现的位置信息,这种由属性值确定记录的位置的方式成为倒排索引。还有索引库中的词都是按照顺序排列 ,后期根据一个关键词查询的时候,可以利用类似折半查找的算法,查询效率非常高;

10.es集群的脑裂问题

答:es集群有可能会出现脑裂问题,原因主要有两个:

          1)如果集群中节点不在同一个网段有可能是网络延迟造成的

           2)如果集群中的节点在同一个网段,有可能是主节点负载太大造成的

                   解决方案主要有两种:

       ①把主从节点的职责分离,设置三个储备主节点,node.master=true,node.data=false,从节点只存储数据,node.master=false,node.data=true

          ②增加延迟时间

              将储备主节点数最小设为n/2+1个

11.详细描述一下Elasticsearch更新和删除文档的过程。

答:①删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;

②磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。

③在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

12.在并发情况下,Elasticsearch如果保证读写一致?

答:可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;

另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。

对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。

13.如何监控Elasticsearch集群状态?

答:Marvel 让你可以很简单的通过 Kibana 监控 Elasticsearch。你可以实时查看你的集群健康状态和性能,也可以分析过去的集群、索引和节点指标。

14.Elasticsearch中的倒排索引是什么? 

答:倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页。它是搜索引擎的核心。其主要目标是快速搜索从数百万文件中查找数据。 

15.ElasticSearch中的分片是什么? 

答:在大多数环境中,每个节点都在单独的盒子或虚拟机上运行。 

1).索引 - 在Elasticsearch中,索引是文档的集合。 

2).分片 -因为Elasticsearch是一个分布式搜索引擎,所以索引通常被分割成分布在多个节点上的被称为分片的元素。

16.ElasticSearch中的副本是什么?

答:一个索引被分解成碎片以便于分发和扩展。副本是分片的副本。一个节点是一个属于一个集群的ElasticSearch的运行实例。一个集群由一个或多个共享相同集群名称的节点组成。

17.ElasticSearch中的分析器是什么?

答:在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称**册分析器,然后可以在映射定义或某些API中引用它们。

Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。

18.什么是ElasticSearch中的编译器?

答:编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。

19.Elasticsearch是如何实现高亮?

答:使用高亮碎片.先创建高亮字段碎片HighlightBuilder.Field对象.然后设置高亮字段Field进行标签设置(preTags和postTags),最后设置文本前后字节数,fragmentSize

20.如何实现搜索排名

答:当你创建了SearchQuery对象后,你可以对任意字段进行排序,里面的addSort方法,在给他一个Sort对象即可,如果在嵌套域查询中.可设置得分情况,在QueryBuilder中的nesterQuery中可设置得分情况.