java面试题

目录

数据类型

面向对象

集合框架

多线程

Servlet

JSP

JDBC

MySQL

Spring

SpringMVC

Mybatis

SpringBoot

SpringCloud

Redis

Solr

ElasticSearch

Nginx

Dubbo&Zookeeper

FastDFS

Linux

Docker


数据类型

  1. java中基本数据类型各占多少字节?

    byte:1字节

    short:2字节

    int:4字节

    long:8字节

    float:4字节

    char:2字节

    double:8字节

    boolean:1位

  2. &和&&的区别?

    &运算符有两种用法:(1)按位与;(2)逻辑与。

    &&运算符是短路与运算

    &&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算

    面向对象

  3. 面向对象的优点?

    易拓展,易维护

  4. 接口与抽象类的区别?

    凡是用abstract修饰的类都叫做抽象类

    抽象类可以有零个或多个抽象方法,也可以包含非抽象方法

    抽象类中可以没有抽象方法,但是有抽象方法的类必须是抽象类,

    抽象类不能创建对象,必须由抽象类派生子类来实现

    接口是用interface修饰的类,接口只能被接口继承

    接口里面的方法只能是抽象方法

​ 接口里面的变量只能是常量,(public static final)

​ 接口不能实例化,只能通过多态的方式来实例化

  1. 重载(overloading)与重写(override)的区别

    方法重载:在一个类里面,方法名一样,参数列表不同,与返回值无关

​ 方法重写:在继承关系中,方法名一样,,参数列表相同

  1. 面向过程与面向对象的区别

    面向过程是指,允许在程序中定义函数或者方法。也许你觉得奇怪,难道还有语言不能定义函数方法么?早期的basic就不可以,只能用跳转来实现函数调用。

    面向对象更近一步,允许你将“过程”(函数、方法)以及它们的上下文相关的数据封装成对象,同时对象允许通过继承和派生以及类型限定符限制开发者对它的一部分的进行访问和修改。

集合框架

  1. 常用的集合类有哪些?

    • Collection接口的子接口包括:Set接口和List接口
    • Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
    • Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
    • List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
  2. List,Set,Map三者的区别?

    Collection集合主要有List和Set两大接口

    • List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
    • Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。

    Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。

    Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。

    Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap

  3. 集合框架底层数据结构

    • List
      • Arraylist: Object数组
      • Vector: Object数组
      • LinkedList: 双向循环链表
    • Set
      • HashSet(无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素
      • LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
      • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
    • Map
      • HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
      • LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
      • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
      • TreeMap: 红黑树(自平衡的排序二叉树)
  4. 哪些集合类是线程安全的?

    • vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。
    • statck:堆栈类,先进后出。
    • hashtable:就比hashmap多了个线程安全。
    • enumeration:枚举,相当于迭代器。
  5. ArrayList 和 LinkedList 的区别是什么?

    • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
    • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
    • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
    • 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
    • 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

    综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

  6. ArrayList 和 Vector 的区别是什么?

    这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合

    • 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
    • 性能:ArrayList 在性能方面要优于 Vector。
    • 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。
  7. 说一下 HashSet 的实现原理?

    HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为PRESENT,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

  8. HashSet如何检查重复?HashSet是如何保证数据不可重复的?

    向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。
    HashSet 中的add ()方法会使用HashMap 的put()方法。

    HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复

  9. hashCode()与equals()的相关规定

    1. 如果两个对象相等,则hashcode一定也是相同的
    2. 两个对象相等,对两个equals方法返回true
    3. 两个对象有相同的hashcode值,它们也不一定是相等的
    4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
    5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等
  10. HashSet与HashMap的区别

    HashMap | HashSet |
    | :——————————————————————————: | :—————————————————————————————- |
    | 实现了Map接口 | 实现Set接口 |
    | 存储键值对 | 仅存储对象 |
    | 调用put()向map中添加元素 | HashSet较HashMap来说比较慢 |
    | HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
    | HashMap相对于HashSet较快,因为它是使用唯一的键获取对象 | HashSet较HashMap来说比较慢 |

  11. BlockingQueue是什么?

    Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现

  12. Queue 中 poll()和 remove()有什么区别?

    • 相同点:都是返回第一个元素,并在队列中删除返回的对象。
    • 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。
  13. 说一下 HashMap 的实现原理?

    HashMap 基于 Hash 算法实现的

    1. 当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标
    2. 存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中
    3. 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
    4. 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。

    需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)

  14. HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现

    JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。

    JDK1.8之后相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

    JDK1.8主要解决或优化了一下问题:

    1. resize 扩容优化
    2. 引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考
    3. 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题

    | 不同 | JDK 1.7 | JDK 1.8 |
    | :———————————- | :—————————————————————————————- | :—————————————————————————————- |
    | 存储结构 | 数组 + 链表 | 数组 + 链表 + 红黑树 |
    | 初始化方式 | 单独函数:inflateTable() | 直接集成到了扩容函数resize()中 |
    | hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 |
    | 存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树 |
    | 插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |
    | 扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) |

  15. HashMap的扩容操作是怎么实现的?

    • 在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容;
    • 每次扩展的时候,都是扩展2倍;
    • 扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。
  16. HashMap是怎么解决哈希冲突的?

    1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
    2. 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
    3. 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快;
  17. HashMap 的长度为什么是2的幂次方

    为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。

    这个实现就是把数据存到哪个链表/红黑树中的算法。

  18. HashMap 与 HashTable 有什么区别?

  19. 线程安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。

  20. 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
  21. 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。
  22. 初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
  23. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
  24. 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。

  25. 如何决定使用 HashMap 还是 TreeMap?

    对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。

    假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。

  26. HashMap 和 ConcurrentHashMap 的区别

    1. ConcurrentHashMap在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
    2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
  27. Array 和 ArrayList 有何区别?

    • Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
    • Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
    • Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
  28. 如何实现 Array 和 List 之间的转换?

    • Array 转 List: Arrays. asList(array) ;
    • List 转 Array:List 的 toArray() 方法。

多线程

  1. 多线程有什么用?

    • 发挥多核CPU的优势
    • 防止阻塞
    • 便于建模
  2. 创建线程的方式

    • 继承Thread类,重写run方法,调用start运行

    • 实现Runnabel接口,实现run方法,创建Thread类对象,构造方法中传递Runnable接口的实现类对象,调用Thread类中的start方法

    • 实现Callable接口并传入引用数据类型,实现call方法,创建适配类FutureTask传递Callable的实现类对象,创建Thread类对象,构造方法中传递FutureTask接口的实现类对象,调用Thread类中的start方法
  3. start()方法和run()方法的区别

    只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。

    如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。

  4. Runnable接口和Callable接口的区别

    Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

  5. 什么是线程安全

    如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。

  6. 如何在两个线程之间共享数据

    通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的

  7. sleep方法和wait方法有什么区别

    sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器

  8. synchronized和ReentrantLock的区别

    • synchronized是关键字。
    • ReentrantLock是类,可以被继承、可以有方法
  9. ConcurrentHashMap的并发度是什么

    ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对比Hashtable的最大优势

  10. ReadWriteLock是什么

    ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

  11. 怎么唤醒一个阻塞的线程

    如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

  12. 什么是乐观锁和悲观锁

    • 乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
    • 悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。
  13. synchronized和lock的区别

     
    1. ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200821115455452.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pva2VyZGoyMzM=,size_16,color_FFFFFF,t_70#pic_center)

Servlet

  1. 说一说Servlet生命周期

    初始化:Web容器加载servlet,调用init()方法

    处理请求:当请求到达时,运行其service()方法。service()自动派遣运行与请求相对应的doXXX(doGet或者doPost)方法。

    销毁:服务结束,web容器会调用servlet的distroy()方法销毁servlet。

  2. get提交和post提交有何区别

    • get一般用于从服务器上获取数据,post一般用于向服务器传送数据
    • 请求的时候参数的位置有区别,get的参数是拼接在url后面,用户在浏览器地址栏可以看到。post是放在http包的包体中。
    • 能提交的数据有区别,get方式能提交的数据只能是文本,且大小不超过1024个字节,而post不仅可以提交文本还有二进制文件。
    • servlet在处理请求的时候分别对应使用doGet和doPost方式进行处理请求
  3. JSP与Servlet有什么区别

    Servlet是服务器端的程序,动态生成html页面发送到客户端,但是这样程序里会有很多out.println(),java与html语言混在一起

    很乱,所以后来sun公司推出了JSP.其实JSP就是Servlet,每次运行的时候JSP都首先被编译成servlet文件,然后再被编译成

    .class文件运行。有了jsp,在MVC项目中servlet不再负责动态生成页面,转而去负责控制程序逻辑的作用,控制jsp与javabean

    之间的流转。

  4. doGet与doPost方法的两个参数是什么

    HttpServletRequest:封装了与请求相关的信息

    HttpServletResponse:封装了与响应相关的信息

  5. forward和redirect的区别

    • 从地址栏显示来说
      • forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
    • 从数据共享来说
      • forward:转发页面和转发到的页面可以共享request里面的数据.
      • redirect:不能共享数据.

JSP

  1. JSP有哪些内置对象,作用是什么?

    | 名称 | 作用 |
    | —————- | —————————————————————————————— |
    | request | 代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。 |
    | response | 代表响应对象,主要用于向客户端发送数据 |
    | session | 主要用于来分别保存每个用户信息,与请求关联的会话; |
    | pageContext | 管理网页属性,为JSP页面包装页面的上下文,管理对属于JSP中特殊可见部分中已命名对象的访问,它的创建和初始化都是由容器来完成的。 |
    | application | 服务器启动时创建,服务器关闭时停止,为多个应用程序保存信息 |
    | out | 主要用于向客户端输出数据; |
    | config | 代码片段配置对象,表示Servlet的配置。 |
    | page | 处理JSP网页,是Object类的一个实例,指的是JSP实现类的实例,即它也是JSP本身,只有在JSP页面范围之内才是合法的。 |
    | exception | 处理JSP文件执行时发生的错误和异常 |

  2. <%…%>和<%!…%>的区别

    <%…%>用于在JSP页面中嵌入Java脚本

    <%!…%>用于在JSP页面中申明变量或方法,可以在该页面中的<%…%>脚本中调用,声明的变量相当于Servlet中的定义的成员变量。

  3. jsp乱码如何解决,给出三种以上的对应解决方案,并给出对应的程序案例

    • JSP页面显示乱码
      • <%@ page contentType=”text/html; charset=gb2312″%>
    • 表单提交中文时出现乱码
      • request.seCharacterEncoding(̶gb2312″)对请求进行统一编码
    • 数据库连接出现乱码
      • 要涉及中文的地方全部是乱码,解决办法:在数据库的数据库URL中加上useUnicode=true&characterEncoding=GBK就OK了。
    • 通过过滤器完成
    • 在server.xml中的设置编码格式

JDBC

  1. JDBC操作数据库的步骤 ?

    1.注册数据库驱动。
    2.建立数据库连接。
    3.创建一个Statement。
    4.执行SQL语句。
    5.处理结果集。
    6.关闭数据库连接

  2. JDBC中的Statement 和PreparedStatement的区别?

    • PreparedStatement是预编译的SQL语句,效率高于Statement。
    • PreparedStatement支持?操作符,相对于Statement更加灵活。
    • PreparedStatement可以防止SQL注入,安全性高于Statement。
  3. 说说数据库连接池工作原理和实现方案?

    工作原理:JAVA EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。
    实现方案:返回的Connection是原始Connection的代理,代理Connection的close方法,当调用close方法时,不是真正关连接,而是把它代理的Connection对象放回到连接池中,等待下一次重复利用。

  4. JDBC是如何实现Java程序和JDBC驱动的松耦合的?

    JDBC API使用Java的反射机制来实现Java程序和JDBC驱动的松耦合。所有操作都是通过JDBC接口完成的,而驱动只有在通过Class.forName反射机制来加载的时候才会出现。

  5. JDBC的DriverManager是用来做什么的?

    JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。

    然后我们会把数据库配置信息传成DriverManager.getConnection()方法,DriverManager会使用注册到它里面的驱动来获取数据库连接,并返回给调用的程序。

  6. execute,executeQuery,executeUpdate的区别是什么?

    • Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取更新的记录条数
    • Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。
    • Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者 什么也不返回DDL语句。返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。
  7. Statement中的getGeneratedKeys方法有什么用?

    有的时候表会生成主键,这时候就可以用Statement的getGeneratedKeys()方法来获取这个自动生成的主键的值了。

  8. 如何使用JDBC接口来调用存储过程?

    存储过程就是数据库编译好的一组SQL语句,可以通过JDBC接口来进行调用。

    我们可以通过JDBC的CallableStatement接口来在数据库中执行存储过程。

  9. JDBC的批处理是什么,有什么好处?

    有时候类似的查询我们需要执行很多遍,比如从CSV文件中加载数据到关系型数据库的表里。我们也知道,执行查询可以用Statement或者PreparedStatement。除此之外,JDBC还提供了批处理的特性,有了它,我们可以在一次数据库调用中执行多条查询语句。

  10. JDBC的事务管理是什么,为什么需要它?

    默认情况下,我们创建的数据库连接,是工作在自动提交的模式下的。这意味着只要我们执行完一条查询语句,就会自动进行提交。因此我们的每条查询,实际上都是一个事务,如果我们执行的是DML或者DDL,每条语句完成的时候,数据库就已经完成修改了。

    有的时候我们希望由一组SQL查询组成一个事务,如果它们都执行OK我们再进行提交,如果中途出现异常了,我们可以进行回滚。

    JDBC接口提供了一个setAutoCommit(boolean flag)方法,我们可以用它来关闭连接自动提交的特性。我们应该在需要手动提交时才关闭这个特性,不然的话事务不会自动提交,每次都得手动提交。数据库通过表锁来管理事务,这个操作非常消耗资源。因此我们应当完成操作后尽快的提交事务

  11. 如何回滚事务?

    通过Connection对象的rollback方法可以回滚事务。它会回滚这次事务中的所有修改操作,并释放当前连接所持有的数据库锁。

MySQL

  1. 数据库三大范式是什么

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

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

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

  2. MySQL存储引擎MyISAM与InnoDB区别

    • Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
    • MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
    • MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

    MyISAM与InnoDB区别

    | MyISAM | Innodb | |
    | —————————————————————————————— | —————————————————————————————— | —————————————————————————————— |
    | 存储结构 | 每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件 | 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB |
    | 存储空间 | MyISAM可被压缩,存储空间较小 | InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引 |
    | 可移植性、备份及恢复 | 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 | 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了 |
    | 文件格式 | 数据和索引是分别存储的,数据.MYD,索引.MYI | 数据和索引是集中存储的,.ibd |
    | 记录存储顺序 | 按记录插入顺序保存 | 按主键大小有序插入 |
    | 外键 | 不支持 | 支持 |
    | 事务 | 不支持 | 支持 |
    | 锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) | 表级锁定 | 行级锁定、表级锁定,锁定力度小并发能力高 |
    | SELECT | MyISAM更优 | |
    | INSERT、UPDATE、DELETE | | InnoDB更优 |
    | select count(*) | myisam更快,因为myisam内部维护了一个计数器,可以直接调取。 | |
    | 索引的实现方式 | B+树索引,myisam 是堆表 | B+树索引,Innodb 是索引组织表 |
    | 哈希索引 | 不支持 | 支持 |
    | 全文索引 | 支持 | 不支持 |

    1. MyISAM索引与InnoDB索引的区别?
    • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
    • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
    • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
    • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效
    1. InnoDB引擎的4大特性

      • 插入缓冲(insert buffer)
      • 二次写(double write)
      • 自适应哈希索引(ahi)
      • 预读(read ahead)
    2. 什么是索引?

      索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

      索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

    3. 索引有哪些优缺点?

      • 索引的优点
        • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
        • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
      • 索引的缺点
        • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
        • 空间方面:索引需要占物理空间。
    4. 索引有哪几种类型?

      主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

      唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

      普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

      全文索引: 是目前搜索引擎使用的一种关键技术。

    5. 索引的数据结构(b树,hash)

      索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

      B树索引

      mysql通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B树)索引和HASH索引。B树索引是Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引(实际是用B+树实现的,因为在查看表索引时,mysql一律打印BTREE,所以简称为B树索引)

      B+tree性质:

      • n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
      • 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
      • 所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
      • B+ 树中,数据对象的插入和删除仅在叶节点上进行。
      • B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。

      哈希索引

      简要说下,类似于数据结构中简单实现的HASH表一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法,将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞,则在对应Hash键下以链表形式存储。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Of9HG6FM-1597981269826)(C:\Users\Joker_DJ\AppData\Roaming\Typora\typora-user-images\image-20200820120938014.png)]

    6. 索引的基本原理.

      索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

      索引的原理很简单,就是把无序的数据变成有序的查询

      1. 把创建了索引的列的内容进行排序
      2. 对排序结果生成倒排表
      3. 在倒排表内容上拼上数据地址链
      4. 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
    7. 索引算法有哪些?

      索引算法有 BTree算法和Hash算法

      BTree算法

      BTree是最常用的mysql数据库索引算法,也是mysql默认的算法。因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量

       
      1. -- 只要它的查询条件是一个不以通配符开头的常量
      2. select * from user where name like 'jack%';
      3. -- 如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
      4. select * from user where name like '%jack';

      Hash算法

      Hash Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引

  1. 创建索引的原则

    • 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

    • 较频繁作为查询条件的字段才去创建索引

    • 更新频繁字段不适合创建索引

    • 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)

    • 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

    • 定义有外键的数据列一定要建立索引。

    • 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。

    • 对于定义为text、image和bit的数据类型的列不要建立索引。

  2. B树和B+树的区别

    • 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
    • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KLsPiH7D-1597981269827)(C:\Users\Joker_DJ\AppData\Roaming\Typora\typora-user-images\image-20200820121407286.png)]

  3. 使用B树的好处

    B树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。

  4. 使用B+树的好处

    由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间

  5. 数据库为什么使用B+树而不是B树

    • B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
    • B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
    • B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
    • B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
    • 增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。
  6. 事物的四大特性(ACID)介绍一下?

    1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
    2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
    3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
    4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
  7. 什么是脏读?幻读?不可重复读?

    • 脏读:某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
    • 不可重复读:在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
    • 幻读:在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
  8. 什么是事务的隔离级别?MySQL的默认隔离级别是什么?

    为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

    | 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
    | ———————— | —— | ————— | ——— |
    | READ-UNCOMMITTED | √ | √ | √ |
    | READ-COMMITTED | × | √ | √ |
    | REPEATABLE-READ | × | × | √ |
    | SERIALIZABLE | × | × | × |

    SQL 标准定义了四个隔离级别:

    • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
    • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
    • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
    • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

    这里需要注意的是:Mysql 默认采用的 可重复读(REPEATABLE_READ)隔离级别

    Oracle 默认采用的 读取已提交(READ_COMMITTED)隔离级别

    事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

    因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。

    InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。

  9. 对MySQL的锁了解吗

    当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

  10. 隔离级别与锁的关系

    在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

    在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;

    在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。

    SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。

  11. MyISAM和InnoDB存储引擎使用的锁

    • MyISAM采用表级锁(table-level locking)。
    • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

    行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。

    ​ 特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

 
  1. **表级锁** 表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
  2.  
  3. ​ 特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。
  4.  
  5. **页级锁** 页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
  6.  
  7. ​ 特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
  1. MySQL中InnoDB引擎的行锁是怎么实现的?

    InnoDB是基于索引来完成行锁

    select * from tab_with_index where id = 1 for update;

    for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列

  2. 什么是死锁?怎么解决?

    死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

    常见的解决死锁的方法

    1. 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
    2. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
    3. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

    如果业务处理不好可以用分布式事务锁或者使用乐观锁

  3. 数据库的乐观锁和悲观锁是什么?怎么实现的?

    悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。

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

    乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过version的方式来进行锁定。

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

  4. 大表怎么优化?某个表有近千万数据,CRUD比较慢,如何优化?分库分表了是怎么做的?分表分库了有什么问题?有用到中间件么?他们的原理知道么?

    1. 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
    2. 读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读;
    3. 缓存: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;
  5. MySQL的复制原理以及流程

    主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。

    主从复制的作用

    1. 主数据库出现问题,可以切换到从数据库。
    2. 可以进行数据库层面的读写分离。
    3. 可以在从数据库上进行日常备份。

    MySQL主从复制解决的问题

    • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
    • 负载均衡:降低单个服务器的压力
    • 高可用和故障切换:帮助应用程序避免单点失败
    • 升级测试:可以用更高版本的MySQL作为从库

    MySQL主从复制工作原理

    • 在主库上把数据更高记录到二进制日志
    • 从库将主库的日志复制到自己的中继日志
    • 从库读取中继日志的事件,将其重放到从库数据中

Spring

  1. 什么是spring?

    Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。

    Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是IOC控制反转DI依赖注入和面向切面编程AOP

  2. Spring 框架中都用到了哪些设计模式?

    1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
    2. 单例模式:Bean默认为单例模式。
    3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
    4. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
    5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
  3. 详细讲解一下核心容器(spring context应用上下文) 模块

    这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。

    Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。

  4. Spring 应用程序有哪些不同组件?

    • 接口 - 定义功能。
    • Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
    • Bean 配置文件 - 包含类的信息以及如何配置它们。
    • Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
    • 用户程序 - 它使用接口。
  5. 什么是Spring IOC 容器?

    控制反转即IOC,它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

    Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。

  6. 控制反转(IoC)有什么作用

    解耦,由容器去维护具体的对象

  7. IOC的优点是什么?

    • IOC 或 依赖注入把应用的代码量降到最低。
    • 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
    • 最小的代价和最小的侵入性使松散耦合得以实现。
    • IOC容器支持加载服务时的饿汉式初始化和懒加载。
  8. BeanFactory 和 ApplicationContext有什么区别?

    • BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

    • BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

    • ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能
    • 加载方式
      • BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化
      • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。
    • 创建方式
      • BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
    • 注册方式
      • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
  9. ApplicationContext通常的实现是什么?

    FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

    ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

    WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

  10. 什么是Spring的依赖注入?

    所谓依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。

  11. 有哪些不同类型的依赖注入实现方式?

    依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入,Setter方法注入和构造器注入

  12. 构造器依赖注入和 Setter方法注入的区别

    构造函数注入 | setter 注入 |
    | ————————————— | ————————————— |
    | 没有部分注入 | 有部分注入 |
    | 不会覆盖 setter 属性 | 会覆盖 setter 属性 |
    | 任意修改都会创建一个新实例 | 任意修改不会创建一个新实例 |
    | 适用于设置很多属性 | 适用于设置少量属性 |

  13. 什么是Spring beans?

    Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。

  14. 一个 Spring Bean 定义 包含什么?

    一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。

  15. 如何给Spring 容器提供配置元数据?Spring有几种配置方式

    • XML配置文件。
    • 基于注解的配置。
    • 基于java的配置。
  16. Spring支持的几种bean的作用域

    • singleton : bean在每个Spring ioc 容器中只有一个实例。
    • prototype:一个bean的定义可以有多个实例。
    • request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
    • session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
    • global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
  17. Spring框架中的单例bean是线程安全的吗?

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

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

  18. Spring如何处理线程并发问题?

    在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

  19. 解释Spring框架中bean的生命周期

    1. Spring对bean进行实例化;
    2. Spring将值和bean的引用注入到bean对应的属性中;
    3. 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
    4. 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
    5. 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
    6. 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
    7. 如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用initmethod声明了初始化方法,该方法也会被调用;
    8. 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
    9. 此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
    10. 如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
    11. 现在你已经了解了如何创建和加载一个Spring容器。但是一个空的容器并没有太大的价值,在你把东西放进去之前,它里面什么都没有。为了从Spring的DI(依赖注入)中受益,我们必须将应用对象装配进Spring容器中。
  20. Spring支持的事务管理类型, spring 事务实现方式有哪些?

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

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

  21. 说一下Spring的事务传播行为

    ① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

    ② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

    ③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

    ④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

    ⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

    ⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

    ⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

  22. 说一下 spring 的事务隔离?

    spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

    1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
    2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
    3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
    4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
    5. ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

    脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

    不可重复读 :是指在一个事务内,多次读同一数据。

    幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

  23. 什么是AOP

    AOP,一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。

  24. Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?

    AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

    • AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

    • Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

  25. JDK动态代理和CGLIB动态代理的区别

    • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
    • CGLIB,是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

    静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

  26. 解释一下Spring AOP里面的几个名词

    1. 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
    2. 连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
    3. 通知(Advice):在AOP术语中,切面的工作被称为通知。
    4. 切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
    5. 引入(Introduction):引入允许我们向现有类添加新方法或属性。
    6. 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
    7. 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
  27. Spring通知有哪些类型?

    1. 前置通知(Before):在目标方法被调用之前调用通知功能;
    2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
    3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
    4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
    5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
  28. 请解释下Spring的IOC

    所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

  29. 将Spring配置到你的应用有几种方法**

    1. 基于xml配置
    2. 基于注解配置
    3. 基于java配置
  30. 什么是基于XML配置

    SpringXML 配置的主要目的时候是使所有的 Spring 组件都可以用 xml 文件的形式来进行配置。这 意味着不会出现其他的 Spring 配置类型, Spring 的 XML 配置方式是使用被 Spring 命名空间的所支持的一系列的 XML 标签来实现的。 Spring 有以下主要的命名空间:context、beans、jdbc、tx、aop、mvc 和 aso。

  31. 什么是基于java配置

    Spring 对 Java 配置的支持是由@Configuration 注解和@Bean 注解来实现的。由@Bean 注解 的方法将会实例化、配置和初始化一个 新对象,这个对象将由 Spring 的 IoC 容器来管理。 @Bean 声明所起到的作用与 元素类似。被 @Configuration 所注解的类则表示这个类 的主要目的是作为 bean 定义的资源。被@Configuration 声明的类可以通过在同一个类的 内部调 用@bean 方法来设置嵌入 bean 的依赖关系。

  32. 什么是基于注解配置

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

SpringMVC

  1. Spring MVC的主要组件?

    • 前端控制器 DispatcherServlet

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

      • 作用:根据请求的URL来查找Handler
    • 处理器适配器HandlerAdapter

      • 作用:根据url对应的地址去执行Handler。处理器Handler(需要程序员开发)
    • 视图解析器 ViewResolver

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

      • View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
  2. 请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

    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响应用户。

    前端控制器—>处理器映射器—>处理器适配器—>处理器Controller—>ModelAndView—>视图解析器—>View—>客户端

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCMYntBU-1597981269828)(C:\Users\Joker_DJ\AppData\Roaming\Typora\typora-user-images\image-20200820131336361.png)]

  3. Spring MVC常用的注解有哪些?

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

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

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

  4. Spring MVC中函数的返回值是什么?

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

  5. Spring MVC里面拦截器是怎么写的

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

Mybatis

  1. MyBatis编程步骤是什么样的?

    1. 创建SqlSessionFactory
    2. 通过SqlSessionFactory创建SqlSession
    3. 通过sqlsession执行数据库操作
    4. 调用session.commit()提交事务
    5. 调用session.close()关闭会话
  2. 请说说MyBatis的工作原理

    1. 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
    2. 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
    3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
    4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
    5. Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
    6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
    7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
    8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
  3. Mybatis都有哪些Executor执行器?它们之间的区别是什么?

    Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

    SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

    ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

    BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

    作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

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

    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()方法的调用。这就是延迟加载的基本原理。

  5. #{}和${}的区别

    • #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
    • Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
    • Mybatis在处理时,是原值传入,就是把{}时,是原值传入,就是把时,是原值传入,就是把{}替换成变量的值,相当于JDBC中的Statement编译
    • 变量替换后,#{} 对应的变量自动加上单引号 ‘’;变量替换后,${} 对应的变量不会加上单引号 ‘’
    • #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入
    • #{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外
  6. 模糊查询like语句该怎么写

    (1)’%${question}%’可能引起SQL注入,不推荐

    (2)”%”#{question}”%” 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ‘,所以这里 % 需要使用双引号” “,不能使用单引号 ’ ‘,不然会查不到任何结果。

    (3)CONCAT(’%’,#{question},’%’) 使用CONCAT()函数,推荐

    (4)使用bind标签

     
    1. <select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
    2.   <bind name="pattern" value="'%' + username + '%'" />
    3.   select id,sex,age,username,password from person where username LIKE #{pattern}
    4. </select>
  7. 如何获取生成的主键

    设置 useGeneratedKeys=”true”

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

    第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。

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

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

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

  10. MyBatis实现一对一,一对多有几种方式,怎么操作的?

    有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的association,collection节点配置一对一,一对多的类就可以完成

    嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置association,collection,但另外一个表的查询通过select节点配置。

  11. Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?

    Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。

    其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。

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

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

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

  13. Mybatis的一级、二级缓存

    • 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

    • 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> ;

    • 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

SpringBoot

  1. Spring Boot 有哪些优点?

    1. 容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验。
    2. 开箱即用,远离繁琐的配置。
    3. 提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
    4. 没有代码生成,也不需要XML配置。
    5. 避免大量的 Maven 导入和各种版本冲突。
  2. Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

    启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

  3. Spring Boot 自动配置原理是什么?

    注解 @EnableAutoConfiguration@Configuration@ConditionalOnClass 就是自动配置的核心,

    @EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。

    筛选有效的自动配置类。

    每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能

  4. 什么是 YAML?

    YAML 是一种人类可读的数据序列化语言。它通常用于配置文件。与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML 文件就更加结构化,而且更少混淆。可以看出 YAML 具有分层配置数据。

  5. YAML 配置的优势在哪里 ?

    1. 配置有序,在一些特殊的场景下,配置有序很关键
    2. 支持数组,数组中的元素可以是基本数据类型也可以是对象
    3. 简洁

    相比 properties 配置文件,YAML 还有一个缺点,就是不支持 @PropertySource 注解导入自定义的 YAML 配置。

  6. Spring Boot 是否可以使用 XML 配置 ?

    Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过 @ImportResource 注解可以引入一个 XML 配置。

  7. spring boot 核心配置文件是什么?bootstrap.properties 和 application.properties 有何区别 ?

    • bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 boostrap 里面的属性不能被覆盖;
    • application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
  8. 如何实现 Spring Boot 应用程序的安全性?

    为了实现 Spring Boot 的安全性,我们使用 spring-boot-starter-security 依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展WebSecurityConfigurerAdapter 并覆盖其方法。

  9. Spring Boot 中如何解决跨域问题 ?

    跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

     
    1. @Configuration
    2. public class CorsConfig implements WebMvcConfigurer {
    3.  
    4. @Override
    5. public void addCorsMappings(CorsRegistry registry) {
    6. registry.addMapping("/**")
    7. .allowedOrigins("*")
    8. .allowCredentials(true)
    9. .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
    10. .maxAge(3600);
    11. }
    12.  
    13. }

    项目中前后端分离部署,所以需要解决跨域的问题。
    我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。
    当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。
    我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。

     
    1. @Configuration
    2. public class CorsConfig {
    3.  
    4. @Bean
    5. public CorsFilter corsFilter() {
    6. CorsConfiguration corsConfiguration = new CorsConfiguration();
    7. corsConfiguration.addAllowedOrigin("*");
    8. corsConfiguration.addAllowedHeader("*");
    9. corsConfiguration.addAllowedMethod("*");
    10. corsConfiguration.setAllowCredentials(true);
    11. UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
    12. urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
    13. return new CorsFilter(urlBasedCorsConfigurationSource);
    14. }
    15.  
    16. }
  10. spring-boot-starter-parent 有什么用 ?

    1. 定义了 Java 编译版本为 1.8 。
    2. 使用 UTF-8 格式编码。
    3. 继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。
    4. 执行打包操作的配置。
    5. 自动化的资源过滤。
    6. 自动化的插件配置。
    7. 针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。
  11. 运行 Spring Boot 有哪几种方式?

    1. 打包用命令或者放到容器中运行
    2. 用 Maven/ Gradle 插件运行
    3. 直接执行 main 方法运行
  12. Spring Boot 中如何实现定时任务 ?

    定时任务也是一个常见的需求,Spring Boot 中对于定时任务的支持主要还是来自 Spring 框架。

    在 Spring Boot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一个则是使用第三方框架 Quartz。

    使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。

    使用 Quartz ,则按照 Quartz 的方式,定义 Job 和 Trigger 即可。

SpringCloud

  1. 什么是Spring Cloud

    微服务只是一种项目的架构方式,或者说是一种概念,Spring-Cloud便是对这种技术的实现,对微服务面临的问题进行统一的封装处理

    优点:

    • 产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善
    • 组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;
    • Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案
    • 服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率
    • 可以更精准的制定优化服务方案,提高系统的可维护性
    • 减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发
    • 微服务可以是跨平台的,可以用任何一种语言开发
    • 适于互联网时代,产品迭代周期更短

    缺点:

    • 微服务过多,治理成本高,不利于维护系统
    • 分布式系统开发的成本高(容错,分布式事务等)对团队挑战大
  2. Spring Cloud Netflix

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

    • Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;
    • Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;
    • Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;
    • Feign:基于Ribbon和Hystrix的声明式服务调用组件;
    • Zuul:API网关组件,对请求提供路由及过滤功能。
  3. SpringBoot和SpringCloud的区别?

    SpringBoot专注于快速方便的开发单个个体微服务。

    SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,

    为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务

    SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系

    SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

  4. Spring Cloud 和dubbo区别?

    • 服务调用方式 dubbo是RPC springcloud Rest Api

    • 注册中心,dubbo 是zookeeper springcloud是eureka,也可以是zookeeper

    • 服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。

  5. 什么是Eureka

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

  6. 什么是Eureka的自我保护模式

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

  7. Eureka和ZooKeeper都可以提供服务注册与发现的功能,请说说两个的区别

    1. ZooKeeper中的节点服务挂了就要选举
      在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的,
      选举就是改微服务做了集群,必须有一台主其他的都是从
    2. Eureka各个节点是平等关系,服务器挂了没关系,只要有一台Eureka就可以保证服务可用,数据都是最新的。
      如果查询到的数据并不是最新的,就是因为Eureka的自我保护模式导致的
    3. Eureka本质上是一个工程,而ZooKeeper只是一个进程
    4. Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper 一样使得整个注册系统瘫痪
  8. 什么是Zuul网关?

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

  9. 网关与过滤器有什么区别

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

  10. Zuul与Nginx有什么区别?

    Zuul是java语言实现的,主要为java服务提供网关服务,尤其在微服务架构中可以更加灵活的对网关进行操作。Nginx是使用C语言实现,性能高于Zuul,但是实现自定义操作需要熟悉lua语言,对程序员要求较高,可以使用Nginx做Zuul集群。

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

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

  12. Ribbon是什么?

    • Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法
    • Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单的说,就是在配置文件中列出后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
  13. Ribbon底层实现原理

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

    开启客户端负载均衡。

  15. 什么是 Hystrix?

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

    Hystrix有四种防雪崩方式:

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

    • 雪崩效应是在大型互联网项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目的服务宕机崩溃.发生雪崩效应的原因有以下几点:
      • 1.单个服务的代码存在bug.
      • 2请求访问量激增导致服务发生崩溃(如大型商城的枪红包,秒杀功能).
      • 3.服务器的硬件故障也会导致部分服务不可用.
  17. 谈谈服务降级、熔断、服务隔离

    • 服务降级:当客户端请求服务器端的时候,防止客户端一直等待,不会处理业务逻辑代码,直接返回一个友好的提示给客户端。
    • 服务熔断:是在服务降级的基础上更直接的一种保护方式,当在一个统计时间范围内的请求失败数量达到设定值或当前的请求错误率达到设定的错误率阈值时开启断路,之后的请求直接走fallback方法,在设定时间后尝试恢复。
    • 服务隔离:是Hystrix为隔离的服务开启一个独立的线程池,这样在高并发的情况下不会影响其他服务。服务隔离有线程池和信号量两种实现方式,一般使用线程池方式。
  18. 什么是Feign?

    • Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易
    • 他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。
  19. Ribbon和Feign调用服务的区别

    • 调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
    • 而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。
  20. 什么是Spring Cloud Config?

    Spring Cloud Config为分布式系统中的外部配置提供服务器和客户端支持,可以方便的对微服务各个环境下的配置进行集中式管理。Spring Cloud Config分为Config Server和Config Client两部分。Config Server负责读取配置文件,并且暴露Http API接口,Config Client通过调用Config Server的接口来读取配置文件。

  21. 什么是Spring Cloud Gateway?

    • Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
    • 使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由RouteLocatorBuilder可以让你添加各种predicates和filters,predicates断言的意思,顾名思义就是根据具体的请求的规则,由具体的route去处理,filters是各种过滤器,用来对请求做各种判断和修改。
  22. 什么是RestTemplate?

    RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是spring提供的用于访问Rest服务的客户端模板工具集

  23. 使用Ribbon实现客户端负载均衡的消费者

    1、引入pom依赖

    2、启动类Application类中的RestTemplate上加上@LoadBalanced注解即可

Redis

  1. 什么是Redis

    Redis是一个使用 C 语言编写的,开源的高性能非关系型的键值对数据库。

    Redis 可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。

    与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。另外,Redis 也经常用来做分布式锁。除此之外,Redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

  2. Redis有哪些优缺点

    优点

    • 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
    • 支持数据持久化,支持AOF和RDB两种持久化方式。
    • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
    • 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
    • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

    缺点

    • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
    • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
    • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
    • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
  3. Redis有哪些数据类型

    Redis主要有5种数据类型,包括String,List,Set,Zset,Hash,满足大部分的使用要求

    | 数据类型 | 可以存储的值 | 操作 | 应用场景 |
    | ———— | ——————————— | —————————————————————————————— | —————————————————————————————— |
    | STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作 对整数和浮点数执行自增或者自减操作 | 做简单的键值对缓存 |
    | LIST | 列表 | 从两端压入或者弹出元素 对单个或者多个元素进行修剪, 只保留一个范围内的元素 | 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据 |
    | SET | 无序集合 | 添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素 | 交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一个交集 |
    | HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对 获取所有键值对 检查某个键是否存在 | 结构化的数据,比如一个对象 |
    | ZSET | 有序集合 | 添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名 | 去重但可以排序,如获取排名前几名的用户 |

  4. 什么是Redis持久化?

    持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

  5. Redis 的持久化机制是什么?各自的优缺点?

    Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:

    RDB:是Redis DataBase缩写快照

    RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。

    RDB优点:

    • 1、只有一个文件 dump.rdb,方便持久化。
    • 2、容灾性好,一个文件可以保存到安全的磁盘。
    • 3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
    • 4.相对于数据集大时,比 AOF 的启动效率更高。

    缺点:

    • 1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
    • 2、AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式完全持久化存储)保存为 aof 文件。

    AOF:持久化

    AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。

    当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

    优点:

    • 1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
    • 2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
    • 3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))

    缺点:

    • 1、AOF 文件比 RDB 文件大,且恢复速度慢。
    • 2、数据集大的时候,比 rdb 启动效率低。

    优缺点是什么?

    • AOF文件比RDB更新频率高,优先使用AOF还原数据。
    • AOF比RDB更安全也更大
    • RDB性能比AOF好
    • 如果两个都配了优先加载AOF
  6. 如何选择合适的持久化方式

    • 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,Redis持久化数据和缓存怎么做扩容?你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
    • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
    • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
    • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式
  7. Redis持久化数据和缓存怎么做扩容?

    • 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
    • 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。
  8. Redis的过期键的删除策略

    过期策略通常有以下三种:

    • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
    • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
    • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

    Redis中同时使用了惰性过期和定期过期两种过期策略。

  9. 我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?

    除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

    1. 定时去清理过期的缓存;
    2. 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

    两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

  10. Redis的内存淘汰策略有哪些

    Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

    全局的键空间选择性移除

    • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
    • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
    • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。

    设置过期时间的键空间选择性移除

    • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
    • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
  11. Redis事务的概念

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

    总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

  12. Redis事务的三个阶段

    1. 事务开始 MULTI
    2. 命令入队
    3. 事务执行 EXEC

    事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队

  13. Redis事务相关命令

    Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

    Redis会将一个事务中的所有命令序列化,然后按顺序执行。

    1. redis 不支持回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
    2. 如果在一个事务中的命令出现错误,那么所有的命令都不会执行
    3. 如果在一个事务中出现运行错误,那么正确的命令会被执行
    • WATCH 命令是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
    • MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
    • EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
    • 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
    • UNWATCH命令可以取消watch对所有key的监控。
  14. 事务管理(ACID)概述

    原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

    一致性(Consistency)
    事务前后数据的完整性必须保持一致。

    隔离性(Isolation)
    多个事务并发执行时,一个事务的执行不应影响其他事务的执行

    持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

    Redis的事务总是具有ACID中的一致性和隔离性,其他特性是不支持的。当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,事务也具有耐久性。

  15. Redis事务支持隔离性吗

    Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的

  16. 什么是哨兵?

    sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:

    • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
    • 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
    • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
    • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

    哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

    • 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
    • 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。
  17. Redis 主从架构

    单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发

    redis replication 的核心机制

    • redis 采用异步方式复制数据到 slave 节点,不过 redis2.8 开始,slave node 会周期性地确认自己每次复制的数据量;
    • 一个 master node 是可以配置多个 slave node 的;
    • slave node 也可以连接其他的 slave node;
    • slave node 做复制的时候,不会 block master node 的正常工作;
    • slave node 在做复制的时候,也不会 block 对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了;
    • slave node 主要用来进行横向扩容,做读写分离,扩容的 slave node 可以提高读的吞吐量。

    redis 主从复制的核心原理

    当启动一个 slave node 的时候,它会发送一个 PSYNC 命令给 master node。

    如果这是 slave node 初次连接到 master node,那么会触发一次 full resynchronization 全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB 快照文件,

    同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中,

    接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。

    slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。

    过程原理

    1. 当从库和主库建立MS关系后,会向主数据库发送SYNC命令
    2. 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来
    3. 当快照完成后,主Redis会将快照文件和所有缓存的写命令发送给从Redis
    4. 从Redis接收到后,会载入快照文件并且执行收到的缓存的命令
    5. 之后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致
  18. 说说Redis哈希槽的概念?

    Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

  19. Redis集群最大节点个数是多少?

    16384个

  20. Redis实现分布式锁

    Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。

    当且仅当 key 不存在,将 key 的值设为 value。 若给定的 key 已经存在,则 SETNX 不做任何动作

    SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

    返回值:设置成功,返回 1 。设置失败,返回 0 。

    使用SETNX完成同步锁的流程及事项如下:

    使用SETNX命令获取锁,若返回0(key已存在,锁已存在)则获取失败,反之获取成功

    为了防止获取锁后程序出现异常,导致其他线程/进程调用SETNX命令总是返回0而进入死锁状态,需要为该key设置一个“合理”的过期时间

    释放锁,使用DEL命令将锁数据删除

  21. 缓存雪崩

    缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    解决方案

    1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    2. 一般并发量不是特缓存穿透别多的时候,使用最多的解决方案是加锁排队。
    3. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
  22. 缓存穿透

    缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    解决方案

    1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
    2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
    3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
  23. 缓存击穿

    缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

    解决方案

    1. 设置热点数据永远不过期。
    2. 加互斥锁,互斥锁
  24. 缓存预热

    缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

    解决方案

    1. 直接写个缓存刷新页面,上线时手工操作一下;
    2. 数据量不大,可以在项目启动的时候自动进行加载;
    3. 定时刷新缓存;
  25. 缓存降级

    当访问量剧增、服务出现问题或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

    缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

    在进行降级之前要对系统进行梳理,看看系统是不是可以丢弃;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

    1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
    2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
    3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
    4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

    服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

Solr

  1. solr与关系型数据库的区别

    数据库里有简单的基于通配符的文本模糊查询,但这会导致全表扫描,性能很差,而Solr是把搜索关键字保存在一个倒排表里,搜索性能提高了N个数量级。但Solr创建索引速度相对较慢。

    Solr里更新部分字段(域)数据相对较慢,因为Solr里更新只能先删除再新增。而且在新增数据的可见性方面,数据库能立马可见,Solr近实时查询的数据可见性则稍差些。

    Solr的魅力在于它灵活的Schema机制,由于Schema.xml约束比较宽松,你甚至可以认为Solr的Schema.xml只是个摆设,每个Document可以有任意个任意类型的域,而数据库里的表的字段是提前限定的,且每一行记录拥有的字段数必须一致。

  2. 什么是Solr

    Solr是一个高性能,采用Java5开发,基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。

  3. Elasticsearch 与 Solr 的比较

    1. 二者安装都很简单;
    2. Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
    3. Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
    4. Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;
    5. Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。
    6. Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。
  4. IK分词器原理

    本质上是词典分词,在内存中初始化一个词典,然后在分词过程中逐个读取字符,和字典中的字符相匹配,把文档中的所有词语拆分出来的过程

  5. Solr的索引查询为什么比数据库要快

    Solr使用的是Lucene API实现的全文检索。全文检索本质上是查询的索引。而数据库中并不是所有的字段都建立的索引,更何况如果使用like查询时很大的可能是不使用索引,所以使用solr查询时要比查数据库快

  6. Solr的使用步骤

    1. Solr服务器搭建,因为Solr是用java5开发的,所以需要JDK和Tomcat。搭建部署
    2. 搭建完成后,我们需要将要展示的字段引入Solr的库中。配置Spring与Solr结合,工程启动的时候启动Solr
    3. 将数据库中的查询内容导入到Solr索引库,这里使用的是SpringDataSolr的客户端实现的。具体使用可以参考API
    4. 建立搜索服务,供客户端调用。调用Solr,查询内容,这中间有分页功能的实现。Solr高亮显示的实现。
    5. 客户端接收页面的请求参数,调用搜索服务,进行搜索。
  7. Solr的域

    域的简介:域相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的 Field
    (域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。

  8. Solr是解决什么问题的?

    严格来说,lucene负责数据存储,而solr只是一个引擎提供搜索和插入而已,跟数据库的解释器一样,有什么好处呢,比如一个数据库有一个字段存了1000个字,你想从这些字里面搜一个词的时候,普通的数据库只会让你使用like去查询,他会遍历每个字去模糊匹配,效率很低,而且有些是无法查询的,当然除了像一些特殊的数据库带有分词,比如postgresql,那lucene做的事情就是分词,然后去匹配分词的词中是否有你想搜的词就好了,当然了,为了提高这种检索效率和内存节省底层做了很复杂的事情,可以这么简单的认为,全文搜索这件事情上数据库是无法满足的!

ElasticSearch

  1. 为什么要使用Elasticsearch?

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

  2. 详细描述一下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)时;

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

    删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;
      磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
      在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

  4. 在并发情况下,Elasticsearch如果保证读写一致?

    可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
      另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
      对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。

  5. ElasticSearch中的集群、节点、索引、文档、类型是什么?

    • 群集是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索引和搜索功能。群集由唯一名称标识,默认情况下为“elasticsearch”。此名称很重要,因为如果节点设置为按名称加入群集,则该节点只能是群集的一部分。
    • 节点是属于集群一部分的单个服务器。它存储数据并参与群集索引和搜索功能。
    • 索引就像关系数据库中的“数据库”。它有一个定义多种类型的映射。索引是逻辑名称空间,映射到一个或多个主分片,并且可以有零个或多个副本分片。 MySQL =>数据库 ElasticSearch =>索引
    • 文档类似于关系数据库中的一行。不同之处在于索引中的每个文档可以具有不同的结构(字段),但是对于通用字段应该具有相同的数据类型。 MySQL => Databases => Tables => Columns / Rows ElasticSearch => Indices => Types =>具有属性的文档
    • 类型是索引的逻辑类别/分区,其语义完全取决于用户。
  6. Elasticsearch中的倒排索引是什么?

    倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页。它是搜索引擎的核心。其主要目标是快速搜索从数百万文件中查找数据。

  7. ElasticSearch中的分片是什么?

    在大多数环境中,每个节点都在单独的盒子或虚拟机上运行。

    • 索引 - 在Elasticsearch中,索引是文档的集合。
    • 分片 -因为Elasticsearch是一个分布式搜索引擎,所以索引通常被分割成分布在多个节点上的被称为分片的元素
  8. ElasticSearch中的副本是什么?

    一个索引被分解成碎片以便于分发和扩展。副本是分片的副本。一个节点是一个属于一个集群的ElasticSearch的运行实例。一个集群由一个或多个共享相同集群名称的节点组成。

  9. ElasticSearch中的分析器是什么?

    在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。

    Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。

  10. 什么是ElasticSearch中的编译器?

    编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。

  11. 启用属性,索引和存储的用途是什么?

    enabled属性适用于各类ElasticSearch特定/创建领域,如index和size。用户提供的字段没有“已启用”属性。 存储意味着数据由Lucene存储,如果询问,将返回这些数据。

    存储字段不一定是可搜索的。默认情况下,字段不存储,但源文件是完整的。因为您希望使用默认值(这是有意义的),所以不要设置store属性 该指数属性用于搜索。

    索引属性只能用于搜索。只有索引域可以进行搜索。差异的原因是在分析期间对索引字段进行了转换,因此如果需要的话,您不能检索原始数据。

Nginx

  1. 什么是Nginx?

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

  2. 为什么要用Nginx?

    • 跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,
    • 而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
    • 使用Nginx的话还能:
      1. 节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
      2. 稳定性高:宕机的概率非常小
      3. 接收用户请求是异步的
  3. 为什么Nginx性能这么高?

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

  4. Nginx怎么处理请求的?

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

     
    1. server { # 第一个Server区块开始,表示一个独立的虚拟主机站点
    2. listen 80; # 提供服务的端口,默认80
    3. server_name localhost; # 提供服务的域名主机名
    4. location / { # 第一个location区块开始
    5. root html; # 站点的根目录,相当于Nginx的安装目录
    6. index index.html index.htm; # 默认的首页文件,多个用空格分开
    7. }
  5. 什么是正向代理和反向代理?

    1. 正向代理就是一个人发送一个请求直接就到达了目标的服务器
    2. 反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规 则分发给了后端的业务处理服务器进行处理了
  6. 使用反向代理服务器的优点是什么?

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

  7. Nginx的优缺点?

    • 优点:
      1. 占内存小,可实现高并发连接,处理响应快
      2. 可实现http服务器、虚拟主机、方向代理、负载均衡
      3. Nginx配置简单
      4. 可以不暴露正式的服务器IP地址
    • 缺点:
      动态处理差:nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,
  8. Nginx应用场景?

    1. http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
    2. 虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
    3. 反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
    4. nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。
  9. 如何用Nginx解决前端跨域问题?

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

  10. location的作用是什么?

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

  11. location的语法能说出来吗?

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

    | 匹配符 | 匹配规则 | 优先级 |
    | ——— | —————————————— | ——— |
    | = | 精确匹配 | 1 |
    | ^~ | 以某个字符串开头 | 2 |
    | ~ | 区分大小写的正则匹配 | 3 |
    | ~ | 不区分大小写的正则匹配 | 4 |
    | !~ | 区分大小写不匹配的正则 | 5 |
    | !~
     | 不区分大小写不匹配的正则 | 6 |
    | / | 通用匹配,任何请求都会匹配到 | 7 |

  12. 限流怎么做的?

    • Nginx限流就是限制用户请求速度,防止服务器受不了
    • 限流有3种
      1. 正常限制访问频率(正常流量)
      2. 突发限制访问频率(突发流量)
      3. 限制并发连接数
    • Nginx的限流都是基于漏桶流算法,底下会说道什么是桶铜流

Dubbo&Zookeeper

  1. dubbo服务负载均衡有哪些策略?

    随机,按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。(权重可以在dubbo管控台配置)

    轮循,按公约后的权重设置轮循比率。存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

    最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

    一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

  2. Dubbo在安全机制方面是如何解决的

    Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。

  3. ZooKeeper是什么?

    ooKeeper是一个 分布式 的,开放源码的分布式 应用程序协调服务 ,是Google的Chubby一个开源的实现, 它是 集群的管理者 , 监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作 。最终,将简单易 用的接口和性能高效、功能稳定的系统提供给用户。 客户端的 读请求 可以被集群中的 任意一台机器处理 ,如果读请求在节点上注册了监听器,这个监听器也是由所 连接的zookeeper机器来处理。对于 写请求 ,这些请求会同 时发给其他 zookeeper 机器并且达成一致后,请 求才会返回成功 。因此,随着 zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降 。 有序性是zookeeper中非常重要的一个特性,所有的 更新都是全局有序的 ,每个更新都有一个 唯一的时间戳 , 这个时间戳称为 zxid ( Zookeeper Transaction Id ) 。而 读请求只会相对于更新有序 ,也就是读请求的返回 结果中会带有这个 zookeeper 最新的 zxid 。

  4. ZooKeeper提供了什么?

    1、 文件系统 2、 通知机制

  5. Zookeeper文件系统

    Zookeeper提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点 都可以设置 关联的数据 ,而文件系统中只有文件节点可以存放数据而目录节点不行。Zookeeper为了保证高吞吐和低延 迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper 不能用于存放大量的数据 ,每个节点的存 放数据上限为 1M 。

  6. Zookeeper通知机制

    client端会对某个znode建立一个watcher 事件 ,当该znode发生变化时,这些client会收到zk的通知, 然后client可以根据znode变化来做出业务上的改变等。

  7. Zookeeper做了什么?

    1、命名服务 2、配置管理 3、集群管理 4、分布式锁 5、队列管理

  8. Zookeeper工作原理

    Zookeeper 的核心是 原子广播 ,这个机制保证了 各个 Server 之间的同步 。实现这个机制的协议叫做Zab 协议 。Zab协议有两种模式,它们分别是 恢复模式(选主) 和 广播模式(同步) 。当服务启动或者在领导者崩溃 后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复 模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

  9. zookeeper是如何保证事务的顺序一致性的?

    zookeeper采用了 递增的事务 Id 来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际 上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader是否发生改变,如果有 新的leader产生出来,epoch会自增, 低 32 位用来递增计数 。当新产生proposal的时候,会依据数据库的 两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就 会开始执行。

  10. Zookeeper同步流程

    选完Leader以后,zk就进入状态同步过程。
    1、Leader等待server连接;
    2、Follower连接leader,将最大的zxid发送给leader;
    3、Leader根据follower的zxid确定同步点;
    4、完成同步后通知follower 已经成为uptodate状态;
    5、Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。

  11. zookeeper负载均衡和nginx负载均衡区别

    zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件;但是nginx的吞吐量比 zk大很多,应该说按业务选择用哪种方式。

  12. Dubbo 支持哪些协议,每种协议的应用场景,优缺点?

    • dubbo: 单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用, 以及消费者远大于提供者。传输协议 TCP,异步,Hessian 序列化。
    • rmi: 采用 JDK 标准的 rmi 协议实现,传输参数和返回参数对象需要实现 Serializable 接口,使用 java 标准序列化机制,使用阻塞式短连接,传输数 据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。 多个短连接,TCP 协议传输,同步传输,适用常规的远程服务调用和 rmi 互 操作。在依赖低版本的 Common-Collections 包,java 序列化存在安全漏洞。
    • webservice: 基于 WebService 的远程调用协议,集成 CXF 实现,提供和 原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适 用系统集成和跨语言调用。
    • http: 基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实 现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消 费者,需要给应用程序和浏览器 JS 调。
    • hessian: 集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务, Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。多 个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于 消费者,提供者压力较大,可传文件。
    • memcache: 基于 memcached 实现的 RPC 协议。
    • redis: 基于 redis 实现的 RPC 协议。
  13. Dubbo 超时时间怎样设置?

    1. 服务提供者端设置超时时间,在 Dubbo 的用户文档中,推荐如果能在服务 端多配置就尽量多配置,因为服务提供者比消费者更清楚自己提供的服务特性。
    2. 服务消费者端设置超时时间,如果在消费者端设置了超时时间,以消费者端 为主,即优先级更高。因为服务调用方设置超时时间控制性更灵活。如果消 费方超时,服务端线程不会定制,会产生警告。
  14. Dubbo 有些哪些注册中心?

    • Multicast 注册中心: Multicast 注册中心不需要任何中心节点,只要广播地 址,就能进行服务注册和发现。基于网络中组播传输实现;
    • Zookeeper 注册中心: 基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更;
    • redis 注册中心: 基于 redis 实现,采用 key/Map 存储,住 key 存储服务名 和类型,Map 中 key 存储服务 URL,value 服务过期时间。基于 redis 的发 布/订阅模式通知数据变更;
    • Simple 注册中心
  15. Dubbo 集群的负载均衡有哪些策略

    1. Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权 重。截面碰撞率高,调用次数越多,分布越均匀;
    2. RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请 求累积的问题;
    3. LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的 请求;
    4. ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求总是发 到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避 免引起提供者的剧烈变动
  16. Dubbo是什么?

    Dubbo 是一个分布式、高性能、透明化的 RPC 服务框架,提供服务自动注册、自动发现等高效服务治理方案, 可以和Spring框架无缝集成。

  17. Dubbo的核心功能?

    1. Remoting:网络通信框架,提供对多种NIO框架抽象封装,包括
      “同步转异步”和“请求-响应”模式的信息交换方式。
    2. Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多
      协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群
      支持。
    3. Registry:服务注册,基于注册中心目录服务,使服务消费方能动态
      的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少
      机器。
  18. Dubbo的核心组件?

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDefRiNy-1597981269831)(C:\Users\Joker_DJ\AppData\Roaming\Typora\typora-user-images\image-20200821111332325.png)]

  19. Dubbo服务注册与发现的流程?

    1. Provider(提供者)绑定指定端口并启动服务
    2. 指供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息
      发送至注册中心存储
    3. Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信
      息至注册中心
    4. 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至
      Consumer 应用缓存。
    5. Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调
      用。
    6. Provider 状态变更会实时通知注册中心、在由注册中心实时推送至
      Consumer
  20. Dubbo的架构设计?

    1. 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提
      供方和服务消费方的业务设计对应的接口和实现
    2. 配置层(Config):对外配置接口,以ServiceConfig和
      ReferenceConfig为中心。
    3. 服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub
      和服务器端Skeleton。
    4. 服务注册层(Registry):封装服务地址的注册与发现,以服务URL
      为中心。
    5. 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册
      中心,以Invoker为中心。
    6. 监控层(Monitor):RPC调用次数和调用时间监控。
    7. 远程调用层(Protocol):封将RPC调用,以Invocation和Result
      为中心,扩展接口为Protocol、Invoker和Exporter。
    8. 信息交换层(Exchange):封装请求响应模式,同步转异步,以
      Request和Response为中心。
    9. 网络传输层(Transport):抽象mina和netty为统一接口,以
      Message为中心。
  21. Dubbo与Spring的关系?

    Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。

  22. Dubbo 和 Dubbox 之间的区别?

    dubbox 基于 dubbo 上做了一些扩展,如加了服务可 restful 调用,更新了开源组件等。

  23. Dubbo和Spring Cloud的关系?

    Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spirng、Spirng Boot 的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是一个生态。

  24. Dubbo和Spring Cloud的区别?

    • 最大的区别:Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。
    • 而SpringCloud是基于Http协议+Rest接口调用远程过程的通信,相对来说,Http请求会有更大的报文,占的带宽也会更多。但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖。

FastDFS

  1. FastDFS执行流程

    把文件从client写入tracker,tracker先去storage获取ip地址和端口号,storage会生成id,把文件存入磁盘,浏览器获取时通过nginx做反向代理,然后浏览器才能用fastDFS工作原理

  2. fastDFS工作原理

    FastDFS架构包括 Tracker server和Storageserver。
    客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。

  3. Tracker的作用

    1. Tracker Server作用是负载均衡和调度,
    2. 通过Tracker server在文件上传时可以根据一些策略找到Storage server提 供文件上传服务。
    3. 可以将tracker称为追踪服务器或调度服务器。

    集群:

    FastDFS集群中的Tracker server可以有多台
    Tracker server之间是相互平等关系同时提供服务.
    客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。

  4. Storage的作用

    Storage Server作用是文件存储,客户端上传的文件最终存储在Storage服务器上

    集群:

    Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容 量之和
    一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,
    不同组的Storage server 之间不会相互通信
    同组内的Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件 完全一致的
    一个组的存储容量为该组内的存储服务器容量最小的那个

  5. 采用分组存储方式的好处

    灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由 tracker进行调度选择。
    一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向 扩容)
    当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。

Linux

  1. 绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?

    绝对路径: 如/etc/init.d
    当前目录和上层目录: ./ ../
    主目录: ~/
    切换目录: cd

  2. 怎么查看当前进程?怎么执行退出?怎么查看当前路径?

    查看当前进程: ps
    执行退出: exit
    查看当前路径: pwd

  3. 怎么清屏?怎么退出当前命令?怎么执行睡眠?怎么查看当前用户 id?查看指定帮助用什么命令?

    清屏: clear
    退出当前命令: ctrl+c 彻底退出
    执行睡眠 : ctrl+z 挂起当前进程fg 恢复后台
    查看当前用户 id: ”id“:查看显示目前登陆账户的 uid 和 gid 及所属分组及用户名
    查看指定帮助: 如 man adduser 这个很全 而且有例子; adduser —help 这个告诉你一些常用参数; info adduesr;

  4. Ls 命令执行什么功能? 可以带哪些参数,有什么区别?

    ls 执行的功能: 列出指定目录中的目录,以及文件
    哪些参数以及区别: a 所有文件l 详细信息,包括大小字节数,可读可写可执行的权限等

  5. 目录创建用什么命令?创建文件用什么命令?复制文件用什么命令?

    创建目录: mkdir
    创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件
    复制文件: cp 7. 文件权限修改用什么命令?格式是怎么样的?
    文件权限修改: chmod

  6. 查看文件内容有哪些命令可以使用?

    vi 文件名 #编辑方式查看,可修改
    cat 文件名 #显示全部文件内容
    more 文件名 #分页显示文件内容
    less 文件名 #与 more 相似,更好的是可以往前翻页
    tail 文件名 #仅查看尾部,还可以指定行数
    head 文件名 #仅查看头部,还可以指定行数

  7. 搜索文件用什么命令? 格式是怎么样的?

    find <指定目录> <指定条件> <指定动作>

    whereis 加参数与文件名

    locate 只加文件名

    find 直接搜索磁盘,较慢。

    find / -name “string*”

  8. 使用什么命令查看用过的命令列表?

    history

  9. Linux查看日志命令的几种方式

    • tail
    • head
    • grep
    • sed
    • cat
    • tac (反向列示)

Docker

  1. 什么是Docker?

    Docker是一个容器化平台,它以容器的形式将你的应用程序及所有的依赖项打包在一起,以确保你的应用程序在任何环境中无缝运行。

  2. 什么是Docker镜像?

    Docker镜像是Docker容器的源代码,Docker镜像用于闯将容器,使用Build命令创建镜像。

  3. 什么是Docker容器?

    Docker容器包括应用程序及所有的依赖项,作为操作系统的独立进程运行。

  4. Docker容器有几种状态?

    四种状态:运行、已停止、重新启动、已退出。

  5. DockerFile中最常见的指定是什么?

    | 指令 | 备注 |
    | ——- | ——————————— |
    | FROM | 指定基础镜像 |
    | LABEL | 功能为镜像指定标签 |
    | RUN | 运行指定命令 |
    | CMD | 容器启动时要运行的命令 |

  6. DockerFile中的命令COPY和ADD命令有什么区别?

    COPY和ADD的区别时COPY的SRC只能是本地文件,其他用法一致。

  7. Docker的常用命令?

    | 命令 | 备注 |
    | ——————- | —————————— |
    | docker pull | 拉去或更新指定的镜像 |
    | docker push | 将镜像推送到远程仓库 |
    | docker rm | 删除容器 |
    | docker rmi | 删除镜像 |
    | docker images | 列出所有镜像 |
    | docker ps | 列出所有容器 |

  8. 容器与主机之间的数据拷贝命令?

    Docker cp命令用于穷奇与主机之间的数据拷贝

    • 主机到哦容器:docker cp /www 96f7f14e99ab:/www/
    • 容器到主机:docker cp 96f7f14e99ab:/www /tmp
  9. 如何批量清理临时镜像文件?

    可以使用sudo docker rmi $(sudo docker images -q -f danging=true)命令

  10. 如何查看镜像支持的环境变量?

    使用sudo docker run IMAGE env

  11. 构建docker镜像应该遵循哪些原则?

    1. 尽量选取满足需求但较小的基础系统镜像,建议选择debian:wheezy镜像,仅有86MB大小。
    2. 清理编译生成文件、安装包的缓存等临时文件。
    3. 安装哥哥软件时候要指定准确的版本号,并避免引入不需要的依赖。
    4. 从安全的角度考虑,应用尽量使用系统的库和依赖。
    5. 使用dockerfile创建镜像时候要添加.dockerignore文件或使用干净的工作目录。
  12. 容器退出后,通过docker ps命令查看不到,数据会丢失么?

    容器退出后会处于终止(exited)状态,此时可以通过docker ps -a查看,其中数据不会丢失,还可以通过docker start来启动,只要删除容器才会清除数据。

  13. 如何停止所有正在运行的容器?

    docker kill $(sudo docker ps -q)

  14. 如何清理批量后台停止容器?

    docker rm$(sudo docker ps -a -q)

  15. 如何临时退出一个正在交互的容器的终端,而不终止它?

    按Ctrl+p,后按Ctrl+q,如果按Ctrl+c会使容器内的应用进程终止,进而会使容器终止。

  16. 很多应用容器都是默认后台运行的,怎么查看他们的输出和日志信息?

    使用docker logs,后面跟容器的名称或者ID信息

  17. Docker的配置文件放在那里。如何修改配置?

    Ubuntu系统下Docker的配置文件是/etc/default/docker,CentOS系统配置文件存放在/etc/sysconfig/docker。

  18. 如何更改docker的默认存储设置?

    Docker的默认存放位置是/var/lib/docker,如果希望将docker的本地文件存储到其他分区,可以使用Linux软连接的方式来做。

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

饰坠之罪

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值