Java常见面试题

Java面试题

  • java基础

  • spring & springMVC

  • mybatis、mybatisplus

  • springboot & springcloudAlibaba

  • redis & mongodb & mysql

  • rabbitmq & kafka

  • docker & linux

一:java基础

1.1:java 面向对象三大特性(封装,继承,多态)?

封装:隐藏对象的属性和实现细节,仅对外公开接口,增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员

继承:实现代码的复用,但特别注意的是,父类的私有属性和构造方法并不能被继承。另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。子类不能继承父类中访问权限为private的成员变量和方法

多态:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同,例如方法重写重载。

1.2:抽象类和接口的区别?

1、接口可以多实现,而抽象类只能单继承 2、抽象类可以有非抽象的方法和构造方法、变量,但是接口只能有抽象方法,静态常量。 3、抽象类和子类具有父子关系,子类能拥有父类中一些属性。接口虽然某个类实现一个接口,但是由于接口中的变量都为静态常量,不存在继承关系。

1.3: 重写和重载?

重载: 在同一个类中,方法名必须相同,参数类型、个数、顺序不同,与返回值类型,访问修饰符无关,发生在编译时。

重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。

1.4 :String、StringBuffer、StringBuilder的区别?

可变性:三个都是处理字符串的方法,String底层使用final修饰了,所以是不可变的;StringBuilder 与 StringBuffer是可变的字符串;

安全性:String 中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer 对方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是不线程安全

性能:每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的String 对象,修改内容时性能低。StringBuffer、StringBuilder 每次都会对对象本身进行操作,而不是生成新的对象并改变对象引用;StirngBuilder的效率会高一些,而StringBuffer的底层加了同步的关键字,性能会有所下降

所以呢,一般我们操作少量的字符串的时候用String ,在单线程环境下操作大量数据时使用StringBuilder,在多线程操作大量数据使用StringBuffer。

1.5:java中的集合?

java 中的集合分为单列集合和双列集合,单列集合顶级接口为 Collection,双列集合顶级接口为 Map。

Collection 的子接口有两个:List 和 Set。

Ø List 接口的特点:元素可重复,有序(存取顺序)。 list 接口的实现类如下:

Ø ArrayList:底层实现是数组,查询快,增删慢,线程不安全,效率高;

Ø Vector:底层实现是数组,查询快,增删慢,线程安全,效率低;【废弃】

Ø LinkedList:底层实现是链表,增删快,查询慢,线程不安全,效率高;

Ø Set 接口的特点:元素唯一,不可重复,无序。 Set 接口实现类如下:

Ø HashSet:底层实现 hashMap,数组+链表实现,不允许元素重复,无序。

Ø TreeSet:底层实现红黑二叉树,实现元素排序

Ø Map 接口的特点:key-value 键值对形式存储数据 Map 接口实现类如下:

Ø HashMap:底层数组+链表实现,线程不安全效率高;

Ø TreeMap:底层红黑二叉树实现,可实现元素的排序;

Ø LinkedHashMap:底层hashmap+linkedList 实现,通过 hashmap 实现 key-value 键值对存储,通过链表实现元素有序。

1.6:ArrayList和Linkedlist区别?

相同点:

1、二者都是 List 接口的实现类,具有元素可重复,有序(存取顺序)特点;

2、二者都是线程不安全,效率高;

不同点:

1、数据结构:ArrayList底层数据结构是动态数组,LinkedList底层数据结构是双向链表;

2、随机访问效率:ArrayList比 LinkedList 在随机访问的时候效率要高,因为LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

3、增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。

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

1.7:HashMap底层原理?

HashMap 底层是数组+链表(LinkedList)实现,hashMap 默认初始化容量为 16,也就 是说数组索引值为0-15,每个数组中存储一个链表。jdk1.8后当hash表的单一链表长度超过 8 个的时候,链表结构就会转为红黑树结构;当 hashmap 空间使用达到 0.75 后,会对数组进行扩容,新建数组,然后将元素拷 贝到新的数组中,每次扩容翻倍;在存储元素时有可能发生 hash 碰撞现象(即两个元素不相同,却有一样的 hash 值),这样的话,就将元素在数组中存入链表中,以链表的形式进行元素的存储,第一个entry 存在链表顶端,再有hash 值一致的entry 存入,则链接在第一个元素之后。

1.8:Java 到底是值传递还是引用传递?

Java 只支持值传递。 Java 程序员之所以容易搞混值传递和引用传递,主要是因为 Java 有两种数据类型,一种是基本类型,比如说 int,另外一种是引用类型,比如说 String或对象。 基本类型的变量存储的都是实际的值,而引用类型的变量存储的是对象的引用——指向了对象在内存中的地址。值和引用存储在 stack(栈)中,而对象存储在 heap(堆)中。

1.9:== 和equals有什么区别?

==:判断两个对象是不是内存中同一个对象。但如果有基本数据类型参与比较,无论是基本数据类型相互比较,还是基本数据和他们的封装类比较,都比较的是值,引用数据类型之间==比较的是内存地址。

equals():作用也是判断两个对象是否相等。但它一般有两种使用情况,一种这个类没有重写equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

另一种就是类重写了 equals() 方法,重写了之后就按照重写的逻辑来判断了。

一般,我们都覆盖 equals() 方法来比较两个对象的内容相等;若它们的内容相等,就认为两个对象是相等的。

1.10:java的类加载机制?

加载 ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象。

验证 目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。

准备 为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,(如static int i = 5 这里只是将 i 赋值为0,在初始化的阶段再把 i 赋值为5),这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象分配到Java堆中。

解析 这里主要的任务是把常量池中的符号引用替换成直接引用

初始化 这里是类记载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。(前面已经对static 初始化了默认值,这里我们对它进行赋值,成员变量也将被初始化)

1.11:java的类加载顺序?

1:所有的类都会优先加载基类; 2:静态成员的初始化优先; 3:成员初始化后,才会执行构造方法;

4:静态成员的初始化与静态块的执行,发生在类加载的时候;成员加载在对象创建时;类对象的以及静态块的访问,都会触发类的加载。

1.12: 手写排序算法?

//冒泡排序
public void bubbleSort(int []a){
           int len=a.length;
           for(int i=0;i<len;i++){
               for(int j=0;j<len-i-1;j++){
                   if(a[j]>a[j+1]){
                       int temp=a[j];
                       a[j]=a[j+1];
                       a[j+1]=temp;
                   }
               }
           }
       }
​
//选择排序
public void selectSort(int[]a){
        int len=a.length;
        for(int i=0;i<len;i++){//循环次数
            int value=a[i];
            int position=i;
            for(int j=i+1;j<len;j++){//找到最小的值和位置
                if(a[j]<value){
                    value=a[j];
                    position=j;
                }
            }
            a[position]=a[i];//进行交换
            a[i]=value;
        }
    }

1.13:设计模式

单例模式是保证一个类仅有一个实例,并自行提供访问该实例全局访问点的创建型模式。

//懒汉式(线程安全)
public class Singleton {  
     private static Singleton instance;     
     private Singleton (){}         
     public static synchronized Singleton getInstance() {//加线程锁
        if (instance == null) {                 //判断如果没有实例              
            instance = new Singleton();         //新建一个实例
        }  
         return instance;                   //返回实例
     }  
}

工厂模式是为创建对象提供过渡接口,根据用户需求动态创建实例的设计模式。我们项目中接入短信服务是接入的是厂家或第三方的短信渠道,可以使用如下代码思路去实现工厂模式下发短信验证码

public interface Phone {
    void make();
}
​
public class MiPhone implements Phone {
    public MiPhone() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make xiaomi phone!");
    }
}
​
public class IPhone implements Phone {
    public IPhone() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make iphone!");
    }
}
​
public class PhoneFactory {
    public Phone makePhone(String phoneType) {
        if(phoneType.equalsIgnoreCase("MiPhone")){
            return new MiPhone();
        }
        else if(phoneType.equalsIgnoreCase("iPhone")) {
            return new IPhone();
        }
        return null;
    }
}
​
//测试
public class Demo {
    public static void main(String[] arg) {
        PhoneFactory factory = new PhoneFactory();
        Phone miPhone = factory.makePhone("MiPhone");            // make xiaomi phone!
        IPhone iPhone = (IPhone)factory.makePhone("iPhone");    // make iphone!
    }
}

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己,我们使用的rabbitmq 中发布订阅(topic)消息类型就是一个典型的观察者模式使用的实例

1.14:jvm虚拟机的内存模型都有哪些?分别都存放那些数据?

堆 :是Java虚拟机内存中最大的一块,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域。被所有的线程共享。

:栈中保存基本数据类型的值和对象的引用,每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问,所有的线程不共享。

本地方法栈:功能和特点类似于虚拟机栈,是一个java调用非java代码的接口,作用是与操作系统和外部环境交互

(如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是虚拟机实现中本地方法栈的行为)

方法区:又叫静态区,方法区包含所有的class和static变量,运行时常量池都分配在 Java 虚拟机的方法区之中,被所有的线程共享。

程序计数器:标示下一条需要执行的指令的位置,分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖程序计数器完成的; 对于Java的多线程程序而言,不同的线程都是通过轮流获得cpu的时间片运行的,这符合计算机组成原理的基本概念,因此不同的线程之间需要不停的获得运行,挂起等待运行,所以各线程之间的计数器互不影响,独立存储。这些数据区属于线程私有的内存

1.15 :GC垃圾回收算法?

标记-清除算法

算法分为2个阶段:1.标记需要回收的对象,2.回收被标记的对象,当进行过标记清除算法之后,内存碎片化比较严重,出现了大量的非连续内存。当java堆需要分配一段连续的内存给一个新对象时仍然需要继续清理以满足“连续空间”的要求。所以说,这种方法比较基础,效率也比较低下。

复制算法

它将内存划分为两块相等的大小,每次使用一块,当这一块用完了,就将还存活的对象复制到另外一块内存区域中,然后将当前内存空间一次性清理掉。这样的对整个半区进行回收,不过这种算法将原有的内存空间减少为实际的一半,代价比较高,内存使用率不高

标记-整理算法

与标记清除算法一样,首先是标记对象然后清除,只不过清除后会自动整理内存碎片,将存货的对象向内存一段移动,整理出一块较大的连续内存空间。这样内存使用率高了,也是当前主推使用的GC垃圾回收算法

1.16:jdk1.8有哪写新特性?

  • 新增lambda表达式

  • Stream API 过滤集合,一下以下代码为辅助理解 把真正的函数式编程风格引入到Java中。 -

public List<Product> filterProductByPredicate(List<Product>          list,MyPredicate<Product> mp){
        List<Product> prods = new ArrayList<>();
        for (Product prod : list){
            if (mp.test(prod)){
                prods.add(prod);
            }
        }
        return prods;
 }
public void test2(){
    filterProductByPredicate(proList, new MyPredicate<Product>() {
        @Override
        public boolean test(Product product) {
            return product.getPrice() < 8000;
        }
    });
}
​
@Test
public void test(){
    proList.stream()
           .fliter((p) -> p.getPrice() <8000)
           .limit(2)
           .forEach(System.out::println);
  • 接口中的默认方法和静态方法

  • 新时间日期API

    LocalDate | LocalTime | LocalDateTime

表示日期的LocalDate ​ 表示时间的LocalTime ​ 表示日期时间的LocalDateTime

  • 函数式接口

定义了一个抽象方法的接口,就是函数式接口,并且还提供了注解:@FunctionalInterface

1.17:Java多线程创建方式?

1、继承 Thread 类

2、实现 Runnable 接口

3、实现 Callable 接口

4、通过线程池获取线程对象,实现多线程

1.18:Java线程的基本状态?

新建状态**:

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到

程序 start() 这个线程。

就绪状态:

当线程对象调用了 start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待 JVM 里线

程调度器的调度。

运行状态:

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复

杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:

如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入

阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。

死亡状态:

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。TERMINATED 执行完成

1.19:多线程中的常用方法及含义?

start():开启线程,使线程从新建进入就绪状态

sleep():线程睡眠,使当前线程休息, 需要指定睡眠时间,当执行sleep方法后进入阻塞状态,不释放cpu资源

wait(): 当前线程进入等待状态,让出CPU给其他线程,自己进入等待队列,等待被唤醒,释放cpu资源

notify():唤醒等待队列中的一个线程,唤醒后会重新进入就绪状态,准备抢夺cpu资源。

notifyAll():唤醒等待队列中的所有线程,抢夺cpu资源。

yield():让出CPU。当前线程让出CPU给其他的线程执行,但是自己也会进入就绪状态参与CPU的抢夺,因此调用yield方法后,仍然可能继续获得CPU

join():加入线程,会将调用的线程加入当前线程。等待加入的线程执行完成后才会继续执行当前线程。-

1.20:Java多线程线程池及核心参数?

ThreadPoolExecutor():(θred)是最原始的线程池创建,上面 1-3 创建方式都是对 ThreadPoolExecutor 的封装。

newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;

newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;

newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;

newSingleThreadScheduledExecutor():创建单线程池,返回ScheduledExecutorService,可以进行定时或周期性的工作调度;

newScheduledThreadPool(int corePoolSize) :和newSingleThreadScheduledExecutor()类似 ,创 建的 是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;

newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内

部会构建 ForkJoinPool,利用 Work-Stealing 算法,并行地处理任务,不保证处理顺序;核心参数:

  • corePoolSize:线程池的核心线程数

  • maximumPoolSize: 线程池最大线程数

  • keepAliveTime:空闲线程的存活时间

  • unit:空闲线程存活时间单位

  • workQueue:线程池等待队列。

  • threadFactory:线程工厂,生产线程的类。

  • handler:线程拒绝策略。当线程池满的时候,该怎么拒绝后来的线程。

1.21:Java多线程中 Synchronized & volatile理解

Synchronized :java同步锁,能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。

通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用

synchronized 修饰的方法 或者 代码块。

synchronized锁 底层实现是,它指针指向的是一个monitor对象的起始地址每个对象实例都会有一个 monitor。其中monitor可以与对象一起创建、销毁,当多个线程同时访问一段同步代码时,会先存放到 EntryList 集合中,接下来当线程获取到对象的monitor时,就会把owner变量设置为当前线程。同时count变量+1,来保证在访问时只有一个引用,释放锁时同理count -1 ;

volatile :用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。

1.22:线程池的拒绝策略/饱和策略

当请求任务不断的过来,而系统此时又处理不过来的时候,我们需要采取的策略是拒绝服务。 四种处理策略: AbortPolicy(抛出一个异常,默认的) 中止 DiscardPolicy(新提交的任务直接被抛弃) 抛弃 DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池) 抛弃最旧 CallerRunsPolicy(交给线程池调用所在的线程进行处理,即将某些任务回退到调用者) 调用者运行

1.23 :锁

二: spring & springMVC

2.1: 介绍一下spring?

关于Spring的话,我们平时做项目一直都在用,不管是使用ssh还是使用ssm,都可以整合。Spring里面主要的就三点,也就是核心思想,IOC控制反转,DI依赖注入,AOP切面编程

我先来说说IOC吧,IOC就是spring里的控制反转,把类的控制权呢交给spring来管理,我们在使用的时候,在spring的配置文件中,配置好bean标签,以及类的全路径,如果有参数,然后在配置上相应的参数。这样的话,spring就会给我们通过反射的机制实例化这个类,同时放到spring容器当中去。

我们在使用的时候,需要结合DI依赖注入使用,把我们想使用的类注入到需要的地方就可以,依赖注入的方式有构造器注入、getset注入还有注解注入。我们现在都使用@autowired或者@Resource注解的方式注入。

然后就是AOP切面编程,他可以在不改变源代码的情况下对代码功能的一个增强。我们在配置文件中配置好切点,然后去实现切面的逻辑就可以实现代码增强,这个代码增强,包括在切点的执行前,执行中,执行后都可以进行增强逻辑处理,不用改变源代码,这块我们项目中一般用于权限认证、日志、事务处理这几个地方。

2.2 : AOP的实现原理?

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

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

  • JDK 动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy 利用InvocationHandler 动态创建一个符合接口的的实例,生成目标类的代理对象。

  • 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。不过在我们的业务场景中没有代理过final的类,基本上都代理的controller层实现权限以及日志,还有就是service层实现事务统一管理

2.3 : 详细介绍下IOC容器

Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext

BeanFactory 是基础类型的 IoC 容器,提供了完整的 IoC 服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。

ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。

他俩的主要区别在于,如果 Bean 的某一个属性没有注入,则使用 BeanFacotry 加载后,在第一次调用 getBean() 方法时会抛出异常,但是呢ApplicationContext 会在初始化时自检,这样有利于检查所依赖的属性是否注入。

因此,在实际开发中,通常都选择使用 ApplicationContext

2.4:springbean的生命周期?

整体来说就4个步骤:实例化bean,属性赋值,初始化bean,销毁bean

  • 首先就是实例化bean,容器通过获取BeanDefinition对象中的信息进行实例化

  • 然后呢就是属性赋值,利用依赖注入完成 Bean 中所有属性值的配置注入

  • 接着就是初始化bean,如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

  • 最后就是销毁bean,和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑

2.5: springbean的作用域?

Spring 容器中的 bean 可以分为 5 个范围:

(1)singleton:单例模式,使-用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。 controller、service、dao层基本都是singleton的

(2)prototype:原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。

(3)request:在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。

(4)session:在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。

(5)global-session:全局作用域,在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。

2.6:事务的传播特性?

属性名称描 述
PROPAGATION_REQUIREDrequired支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将创建新事务,默认就是这个
PROPAGATION_SUPPORTSsupports支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将以非事务状态执行
PROPAGATION_MANDATORYmandatory支持当前事务。如果 A 方法没有事务,则抛出异常
PROPAGATION_REQUIRES_NEWrequires_new将创建新的事务,如果 A 方法已经在事务中,则将 A 事务挂起
PROPAGATION_NOT_SUPPORTEDnot_supported不支持当前事务,总是以非事务状态执行。如果 A 方法已经在事务中,则将其挂起
PROPAGATION_NEVERnever不支持当前事务,如果 A 方法在事务中,则抛出异常
PROPAGATION.NESTEDnested嵌套事务,底层将使用 Savepoint 形成嵌套事务

2.7:事务的隔离级别?

Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过 binlog实现的。隔离级别有四种

  • Read uncommitted (读未提交):读未提交,允许另外一个事务可以看到这个事务未提交的数据,最低级别,任何情况都无法保证。

  • Read committed (读已提交):保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新,可避免脏读的发生。

  • Repeatable read (可重复读):保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新,可避免脏读、不可重复读的发生。 mysql innerDB 默认隔离级别

  • Serializable (串行化):一个事务在执行的过程中完全看不到其他事务对数据库所做的更新,可避免脏读、不可重复读、幻读的发生。

2.8:spring中都用了哪些设计模式?

(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

(2)单例模式:Bean默认为单例模式。

(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

2.9:springMVC的执行流程?

1.客户端(浏览器)发送请求,直接请求到DispatcherServlet。

2.DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。

3.解析到对应的Handler(也就是我们平常说的Controller控制器)。

4.HandlerAdapter会根据Handler来调用真正的处理器来处理请求和执行相对应的业务逻辑。

5.处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。

6.ViewResolver会根据逻辑View去查找实际的View。

7.DispatcherServlet把返回的Model传给View(视图渲染)。

8.把View返回给请求者(浏览器)。

 

2.10:springMVC中的常用注解?

@RequestMapping:指定类或者方法的请求路径,可以使用method字段指定请求方式

@GetMapping、@PostMapping:规定了请求方式的方法的请求路径

@RequestParam:接收单一参数的

@PathVariable:用于从路径中接收参数的

@CookieValue:用于从cookie中接收参数的

@RequestBody:用于接收js对象的,将js对象转换为Java对象

@ResponseBody:返回json格式数据

@RestController:用在类上,等于@Controller+@ResourceBody两个注解的和,一般在前后端分离的项目中只写接口时经常使用,标明整个类都返回json格式的数据

2.11:分布式锁解决方案

1:基于数据库实现分布式锁;

基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

2:基于Redis 中setnx的特性来实现方式;

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0;

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

3:基于ZooKeeper的实现方式;

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock; (2)线程A想获取锁就在mylock目录下创建临时顺序节点; (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。

上面的三种实现方式,没有在所有场合都是完美的,所以,应根据不同的应用场景选择最适合的实现方式。

在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,这时候使用分布式锁就可以很好地控制资源。 当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想。

2.12:@Autowred 和 @Resource的区别?

@Autowred 默认按照类型注入,如果需要指定名称,搭配@Qualifier; 是Spring提供的注解

@Resource 默认按照名称注入,如果指定的名称不存在,按照类型查找; 是JDK自带的注

2.13:Spring 如何解决 Bean 循环依赖问题

提前暴露,就是把可以得到的对象的函数式接口放到三级缓存中,这个函数式接口(ObjectFactory)里面有一个方法getObject() 可以获取实例化完成但未初始化的半成品对象,如果产生循环依赖,就可以从这个方法中获取半成品对象进行属性的赋值。

三:mybatis & mybatisplus

3.1: mybatis 的优缺点?

Mybatis是一个半ORM(对象关系映射)的持久层框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程,使用时直接编写原生态sql。

l 优点:

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

2:很好的与各种数据库兼容;

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

4:与JDBC相比,消除了JDBC大量冗余的代码,不需要手动开关连接,能够与Spring很好的集成

l 缺点:

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

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

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

第一种方法:通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致;

第二种方法:通过<resultMap>来映射字段名和实体类属性名的一一对应的关系;

第三种方法:在实体类通过@Column注解也可以实现;

3.4 :mybatis 如何执行批量插入?

第一种就是普通的xml中insert语句可以写成单条插入,在调用方循环N次;

第二种是xml中insert语句写成一次性插入一个N条的list,语法如下(核心便签 foreach collection***)>

<sql id="Base_Column_List"> uanme,age,address</sql>
<insert id="insertBatch" >
    insert into person ( <include refid="Base_Column_List" /> ) 
    values 
    <foreach collection="list" item="item" index="index" separator=",">
        (null,#{item.name},#{item.sex},#{item.address})
    </foreach>
</insert>
insert into person(。。。)values(,,,),(。。。。),()

3.5 : Mybatis有哪些动态sql?

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

Mybatis提供了9种动态sql标签:

trim、where 、 set 、foreach、if 、choose、when 、otherwise 、bind

3.6 : MyBatis有哪写标签?

除了常见的select|insert|update|delete标签之外,还有<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签,其中<sql>为sql片段标签,通过<include>标签引入sql片段,<selectKey>`为不支持自增的主键生成策略标签。

3.7:MyBatis 一级缓存和二级缓存?

一级缓存:Mybatis在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSessio-n对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库;那什么时候会清空呢?我们执行添加,修改,删除操作的时候mybatis会自动清空一级缓存,准确的来说是我们执行commit()事务提交的时候就会清空。

MyBatis的二级缓存是Application级别的缓存,SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,那如何开启呢?参考下方

#第一步  全局配置文件 mybatis-configuration.xml 文件中加入如下代码
<!--开启二级缓存  -->
<settings>    
     <setting name="cacheEnabled" value="true"/>
</settings>
​
#第二步 其中一中方法: springboot配置开启二级缓存
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo.entity
  configuration:
    cache-enabled: true
#第二步 其中第二种方法: mapper接口中用注解
@CacheNamespace
#第三步 
让Mapper对应的实例类实现Serializable接口
public class Emp implements Serializable{}

3.8: MyBatis中${} 和#{}的区别?

${}是字符串替换占位符,#{}是是预编译参数占位符,

${}会引起sql注入风险,不安全,xml中避免使用这种方式

使用#{}可以有效的防止SQL注入,提高系统安全性。

3.9:MyBatis执行原理描述?

MyBatis的主要设计目的就是让我们对执行SQL语句时对输入输出的数据管理更加方便,

他的执行流程包括

1:读取配置文件,配置文件包含数据库连接信息和Mapper映射文件或者Mapper包路径。

2:有了这些信息就能创建SqlSessionFactory,SqlSessionFactory的生命周期是程序级,程序运行的时候建立起来,程序结束的时候消亡

3: SqlSessionFactory建立SqlSession,目的执行sql语句,SqlSession是过程级,一个方法中建立,方法结束应该关闭

4:当用户使用mapper.xml文件中配置的的方法时,mybatis首先会解析sql动态标签为对应数据库sql语句的形式,并将其封装进MapperStatement对象,然后通过executor将sql注入数据库执行,并返回结果。

5: 将返回的结果通过映射,包装成java对象。

3.10:MyBatis 和MyBatisPlus的区别?

1:mybatisPlus 是对myBatis的增强,提供单表的crud操作,类似myabtis+tkMapper

2:mybatisPlus内置丰富的条件构造器,比如,查询的QueryWrapper、LambdaQueryWrapper,修改的UpdateWrapper、

ge、gt、le、lt、isNull、isNotNull等条件构造器提供查询

四:springboot & springcloud

4.1:springboot优点?为什么选用springboot?

(1)简化配置, 它实现了自动化配置 ;@注解 java反射

(2)提供maven极简配置,各种便捷的starter启动器;

(3)基于Spring构建,使开发者快速入门,门槛很低;

(4)内置tomcat服务器,SpringBoot可以创建独立运行的应用而不需要依赖于容器;

(5)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;

4.2:如何理解springboot 的starters ?

springboot 就是把众多优秀的框架整合起来,在boot项目通过引入starter启动器来实现开箱即用,因为它的存在,项目的依赖之间的关系对我们来说变的更加简单了

4.3: springboot自动配置原理?

springboot项目的核心注解@SpringbootApplication说起了,这个注解包含了三个注解,其中一个是@EnableAutoConfiguration注解,这个注解主要是开启自动配置的,这个注解会"猜"你将如何配置 spring,前提是你已经添加 了 jar 依赖项,比如项目中引入了 spring-boot-starter-web ,这个包里已经添加 Tomcat 和 SpringMVC,这个注解就会自动假设您在开发一个 web 应用程序并添加相应的 spring 配置,springboot默认有一个spring-boot-autoconfigure包,大多数常用的第三方的配置都自动集成了,像redis、es等,这里边有一个META-INF/spring.factories文件,这里边定义了所有需要加载的bean的全路径,spring会根据反射的原理,创建这些对象,放到IOC容器中,加载时需要的参数,通过JavaConfig的方式加载配置文件中的参数然后创建了对应的对象,这就是自动配置的原理。

4.4:Spring Boot 如何实现全局异常处理?-

可以使用 @ControllerAdvice@ExceptionHandler 处理全局异常

4.5:Spring boot 核心注解?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解: @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。 @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。 @ComponentScan:Spring组件扫描,从当前类所在的包以及子包扫描,之外的包扫描不到,所以我们在开发的时候,所有的类都在主类的子包下

4.6:spring cloud 的核心组件用到哪些?详细说一下他们各自的作用及请求的流程?

我们使用的是springcloudAlibaba系列的,主要使用到gateway网关,作用过滤和路由,通过filter过滤器配置过滤规则,routes路由到不同的微服务上;nacos注册中心,管理服务的注册与发现,同时也作为配置中心来用,把项目中的配置文件统一管理,实现热更改;ribbon负载均衡,应用内的服务集群下可做负载,策略有随机、轮训和权重;feign远程调用,做服务与服务之间的远程调用;sentinel限流与熔断,配置流控规则实现限流,熔断返回托底数据。

4.7:SpringCloud和Dubbo比较,谈谈个人看法?

springCloud和Dubbo都是现在主流的微服务架构,SpringCloud是Apache旗下的Spring体系下的微服务解决方案,Dubbo是阿里系的分布式服务治理框架;从技术维度上,个人觉得其实SpringCloud远远的超过Dubbo,,Dubbo本身只是实现了服务治理,而SpringCloud现在以及后续有好多个子项目以后还可能会更多。服务的调用方式Dubbo使用的是RPC远程调用,而SpringCloud使用的是 Rest API,其实更符合微服务官方的定义,服务网关,Dubbo并没有本身的实现,只能通过其他第三方技术的整合,而SpringCloud有gateway路由网关,作为路由服务器,进行消费者的请求分发,SpringCloud还支持断路器等其他组件等。

4.8:spring、springMVC、springboot、springcloud 的区别与联系?

Spring是一个生态体系(也可以说是一个技术体系)是集大成者,包括Spring Framework、Spring Boot、Spring Cloud等;

Springframework框架他们的基础都是Spring 的 ioc和 aop, ioc 提供了依赖注入的容器, aop解决了面向横切面的编程(比如日志、事务),然后在此两者的基础上实现了其他延伸产品的高级功能。

Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用来开发 web 应用。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这就是 SpringBoot,

所以简练的说,spring是一个引擎,springMVC是基于Spring的一个MVC框架,springboot是一套快速开发整合包,Spring Cloud事实上是一整套基于Spring Boot的微服务解决方案。它为开发者提供了很多工具,用于快速构建分布式系统的一些通用模式,例如:注册中心、服务发现、限流、网关、等

spring是一个生态体系,springframework是一个一站式的轻量级的java开发框架,核心是控制反转(IoC)和面向切面(AOP),针对于开发的WEB层(springMVC)、业务层(IoC)、持久层(jdbcTemplate)等都提供了多种配置解决方案。

4.9:SpringBoot和SpringCloud的区别/关系?

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

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

4.10:feign 和 openfeign 有什么区别?

相同点:二都都有服务远程调用的功能

不同点1、Feign 本身不支持 Spring MVC 的注解,它有一套自己的注解。openfeign 支持springMVC注解,我们springcloud中用的是openfeign

4.11:feign的底层原理?

  • 首先通过@EnableFeignCleints注解开启FeignCleint

  • 根据Feign的规则实现接口,并加@FeignCleint注解

  • 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。

  • 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate

  • RequesTemplate再生成Request

  • Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp

  • 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

4.12.自定义starter?

Starter可以理解为一个可拔插式的插件,提供一系列便利的依赖描述符,您可以获得所需的所有Spring和相关技术的一站式服务。应用程序只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。用一句话描述,就是springboot的场景启动器。

1、创建一个工程id-spring-boot-starter,在pom文件中添加以下依赖,我们自定义的starter一般以xxx-spring-boot_starter命名。

2、定义一个映射配置新的的类IDProperties,添加注解ConfigurationProperties(prefix = "id"),我们的配置文件以id开头,比如id.mathine-id=110

3、创建一个服务IDService,构造函数接收IDProperties,具体代码

4、创建Configuration类IDConfiguration,添加注解Configuration和EnableConfigurationProperties,把当前类设置成配置类,并且注入IDProperties

5、重要的一步,在resources目录下创建META-INF目录,并添加文件spring.factories。在这个文件中配置EnableAutoConfiguration,具体如下:

五:redis & mongodb & mysql

5.1:redis的数据类型,以及每种数据类型的使用场景?

类型简介特性场景命令
String(字符串)二进制安全可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M什么都能存
Hash(字典)键值对集合,即编程语言中的Map类型适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)存储、读取、修改用户属性hset user1 name zhangsan #设值; hget user1 name #取值; hkeys user2 #查看所有key; hvals user2 #查看所有value; hdel user1 name #删除值; hexists user2 age #查看该值是否存在
List(列表)链表(双向链表)增删快,提供了操作某一段元素的API1、最新消息排行等功能(比如朋友圈的时间线) 2、消息队列lpush插入列表左边;rpush:右边; lrange 0 -1 取所有值 lrem 删除元素
Set(集合)哈希表实现,元素不重复1、添加、删除、查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐smembers set1 #查看所有值; sismember set1 aaa #查看是否包含该值; srem set1 bbb #移除元素
Sorted Set(有序集合)将Set中的元素增加一个权重参数score,元素按score有序排列数据插入集合时,已经进行天然排序1、排行榜 2、带权重的消息队列zadd score 50 zhangsan #存值; zrange score 0 -1 #取值; zrem score lisi #移除元素

5.2:Redis持久化机制?

RDB 和AOF两种方式

RDB持久化机制

对Redis中的数据执行周期性的持久化,持久化方式为通过快照的方式持久化

AOF机制

将每条写命令作为日志,以append-only模式写入一个日志文件,在Redis重启时,通过回放日志中的写入指令来重构整个数据,

我们项目用的是使用RDB的方式

5.3: 缓存穿透、缓存击穿、缓存雪崩、缓存预热、缓存同步?

  • 缓存穿透:

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空,这就相当于进行了两次无用的查询。像这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题

解决办法

最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空,不管是数据不存在,还是系统故障,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

  • 缓存击穿:

是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个key不停进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞.

解决办法

1:设置热点key永不过期,2:设置redis分布式锁

  • 缓存雪崩

是指在某一个时间段,缓存集中过期失效,比方说:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期,所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

解决办法:

一个简单方案就是缓存失效时间分散开,不设置固定的实效时间,采用随机失效的策略来解决。

最多的解决方案就是锁,或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上

  • 缓存预热:

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

操作方式:

1、直接写个缓存刷新页面,上线时手工操作下;

2、数据量不大,可以在项目启动的时候自动进行加载;

  • 缓存更新:

1、定时去清理过期的缓存;

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

5.4: redis集群?

Redis本身就支持集群操作redis_cluster,集群至少需要3主3从,且每个实例使用不同的配置文件,主从不用配置,集群会自己选举主数据库和从数据库,为了保证选举过程最后能选出leader,就一定不能出现两台机器得票相同的僵局,所以一般的,要求集群的server数量一定要是奇数,也就是2n+1台,并且,如果集群出现问题,其中存活的机器必须大于n+1台,否则leader无法获得多数server的支持,系统就自动挂掉。所以一般是3个或者3个以上的奇数节点。

Redis 2.8中提供了哨兵工具来实现自动化的系统监控和故障恢复功能。哨兵的作用就是监控redis主、从数据库是否正常运行,主数据库出现故障自动将从数据库转换为主数据库

我们公司搭建的redis集群是用的ruby脚本配合搭建的,我们一共搭建了6台服务器,3主3备,他们之间通信的原理是有一个乒乓协议进行通信的,我再给你说下一他们往里存储数据的机制吧,其实这个redis搭建好集群以后每个节点都存放着一个hash槽,每次往里存储数据的时候,redis都会根据存储进来的key值算出一个hash值,通过这个hash值可以判断到底应该存储到哪一个哈希槽中,取的时候也是这么取的,这就是我了解的redis集群

5.5: redis分布式锁?

这个分布式锁这里,我们原来传统的项目都在单台服务器上部署用java里的锁synchronized这个同步锁就行,但是他这个是针对对象的锁,但是我们分布式的项目需要把项目部署到多台服务器上,每台服务器的对象都不同,所以就得考虑用分布式锁,这块实现起来也比较简单,其实这个锁就是redis中的一个key-value的一对值,在使用的时候吧,首先使用setnx方法进行尝试加锁,并可以设置过期时间,如果返回1则代表加锁成功,然后立即对这个锁设置一个失效时间,防止服务宕机,锁一致存在,在处理完业务逻辑之后,删除锁就行了,其他线程就可以获取锁进行业务了

5.6:MongoDB和redis 有什么区别?

1、内存管理机制:Redis数据全部存在内存,定期写入磁盘,MongoDB数据存在磁盘,由 linux系统 mmap 实现,当内存不够时,只将热点数据放入内存,其他数据存在磁盘

2:支持的数据结构:Redis 支持的数据结构丰富,包括hash、set、list等。MongoDB数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。

3:性能:mongodb依赖内存,TPS较高;Redis依赖内存,TPS非常高。性能上Redis优于MongoDB。

4:事务支持情况:Redis事务支持比较弱,只能保证事务中的每个操作连续执行;mongodb不支持事务。

5.7 : mysql索引都有哪些?如何创建索引?

MySQL的索引有两种分类方式:逻辑分类和物理分类。

按照逻辑分类,索引可分为:

主键索引:一张表只能有一个主键索引,不允许重复,不允许为null;

唯一索引:数据列不允许重复,允许为NULL值,一张表可有多个唯一索引,但是一个唯一索引只能包含

一列,比如身份证号码,卡号都可以作为唯一索引;

普通索引:一张表可以创建多个普通索引,一个普通索引可以包含多个字段,允许数据重复,允许NULL值插入;

全文索引:让搜索关键词更高效的一种索引;

按照物理分类,索引可分为:

聚集索引:一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引,

如果还是没有的画,就采用Innodb存储引擎为每行数据内置的6字节rowid作为聚集索引。每张表只有一个聚集索引,

因为聚集索引的兼职的逻辑顺序决定了表中相应行的物理顺序。聚集索引在精确查找和范围查找方面有良好的性能表

现(相对于普通索引和全表扫描),聚集索引就显得弥足珍贵,聚集索引选择还是要慎重(一般不会让没有语义的自增

id充当聚集索引);

非聚集索引:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同(非主键的那一列),一个表中可以拥有多个非聚集

索引;

创建主键索引

alter table t add primary key add(id)

创建唯一索引

alter table t add unique(username)

创建普通索引

alter table t add indexindex_name(username)

创建全文索引

alter table t add fulltext (username)

###5.8 : mysql搜索引擎知道哪些?有什么区别?默认搜索引擎是什么?

我了解到的数据库搜索引擎有MyISAM、InnoDB、BDB、MEMORY等,对于 MySQL 5.5 及更高版本,默认的存储引擎是 InnoDB。在 5.5 版本之前,MySQL 的默认存储引擎是 MyISAM,

mysql中事务隔离级别 读未提交,读已提交,可重复读,串行化四个!默认是可重复读,我们在项目中一般用读已提交(Read Commited)这个隔离级别

• InnoDB 存储引擎:

o 支持自增长列(auto_increment),自增长列的值不能为空,如果在使用的时候为空的话就会从现有的最大值自动+1,如果有但是比现在的还大,则就保存这个值。

o 支持外键(foreignkey),外键所在的表称为子表而所依赖的表称为父表。

o 支持事务,回滚以及系统崩溃的修复能力,并且支持多版本并发控制的事务安全。

o 支持mvcc(多版本并发控制)的行级锁,就是通过多版本控制来实现的乐观锁

o 索引使用的是B+Tree

优缺点:InnoDB的优势在于提供了良好的事务处理、崩溃修复能力和并发控制。缺点是读写效率较差,占用的数据空间相对较大。

• MyISAM 存储引擎

不支持事务、支持表级锁

支持全文搜索

缓冲池只缓存索引文件,

不缓存数据文件 MyISAM 存储引擎表由数据文件(MYD)和索引文件( MYI)组成

我们项目中常用到的是innoDB,InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全,但是对比Myisam的存储引擎InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。

5.9: mysql如何查看索引是否生效?

使用 explain 执行计划查看在sql前面加入关键字explain 查询出的结果查看type类型检查是否有执行索引

举例:EXPLAIN select * from table where id=2;我们一般优化sql语句的话,type级别都要至少达到ref级别,就是每次查询必须要使用索引

explain之后返回的列 type字段描述

type类型字段

All:最坏的情况,全表扫描

Index:和全表扫描一样。只是扫描表的时候按照索引次序进行。

主要优点就是避免了排序, 但是开销仍然非常大。

Range:范围扫描,一个有限制的索引扫描。key 列显示使用了哪个索引。

当使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,

用常量比较关键字列时,可以使用 range

Ref:一种索引访问,它返回所有匹配某个单个值的行。此类索引访问只有当使用非唯一性索引或唯一性索引非唯一性前缀时才会发生。

5.10: mysql查询语句优化?

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

• 应尽量避免在 where 子句中使用is not null或<>操作符,否则引擎将放弃使用索引而进行 全表扫描。

• 应尽量避免在 where 子句中对字段进行 null值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询:selectid from t where num=0

• 应尽量避免在 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

• 以%开头的模糊查询也会导致全表扫描: select id from t where name like '%abc%',如果要提高效率的话,可以考虑全文检索来解决。

• in 和 not in 也要慎用,否则会导致全表扫描,如: select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了: select idfrom t where num between 1 and 3

• 应尽量避免在 where 子句中对字段进行表达式操作,这将导致放弃使用索引 而进行全表扫描。如:selectid from t where num/2=100应改为: select id from t where num=100*2

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

5.11: mysql存储过程和视图?

存储过程:存储程序是被存储在服务器中的组合SQL语句,经编译创建并保存在数据库中,用户可通过存储过程的名字调用执行。存储过程核心思想就是数据库SQL语言层面的封装与重用性。使用存储过程可以较少应用系统的业务复杂性,但是会增加数据库服务器系统的负荷,所以在使用时需要综合业务考虑。

视图:视图本身是一张虚拟表,不存放任何数据。在使用SQL语句访问视图的时候,获取的数据是MySQL从其它表中生成的,视图和表在同一个命名空间(因为表和视图共享数据库中相同的名称空间,因此,数据库不能包含具有相同名称的表和视图)。视图查询数据相对安全,视图可以隐藏一些数据和结构,只让用户看见权限内的数据,使复杂的查询易于理解和使用。

5.12: mysql行转列与列转行?

mysql中行转列我们用的是内部函数实现过,函数是cast when then else end 这种

列转行使用 union 关键字 把多个select语句结果捏合到一个结果来实现

###5.13: 分库分表中解释一下垂直和水平2种不同的拆分?

垂直拆分:是将单表,或者是有关联的表放在一个数据库,把原有的一个数据库拆分成若干个数据库。

水平拆分:是将一个很大的表,通过取模,按照日期范围等等拆分成若干个表.

###5.14 : 分库分表中垂直分库方案会带来哪些问题?

垂直分库如果没有按照合理的业务逻辑去拆分,后期会带来跨库join,分布式事务等;

跨库join会导致查询性能低下,分布式事务下会导致因数据不一致造成很多脏数据的存在。

###5.15: 搭建mycat的核心配置文件有哪些?

schema.xml,配置逻辑库表,分片和读写分离

rule.xml,具体的分片规则和分片算法

server.xml,配置默认的数据库和用户,表权限

###5.16 : 什么叫垂直切分?什么叫混合切分?项目中有没有可能只用水平切分?

垂直拆分是将同样的系统按照应用场景(调用方)进行拆分。

比如一个交易系统的支付模块,上游有用户支付和商家支付两个调用流程。按照垂直拆分的规则就可以将支付模块拆分为用户支付和商家支付。

混合切分:项目组中如果有水平切分,那项目组里的开发方式就叫混合切分。或者项目组里就是单纯的垂直切分。

5.17:数据库设计三范式

第一范式:每个列都不可以再拆分。 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。 在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我 们经常会为了性能而妥协数据库的设计。

六:rabbitmq & kafka

6.1: rabbitmq的特性及为什么要使用?

特性:

  • 消息确认机制 (自动确认和手动确认) 默认使用自动确认

    • 自动确认: acknowledge='none'

    • 手动确认: acknowledge='manual'

  • 持久化

    • 交换器持久化、队列持久化和消息的持久化

  • 过期时间(TTL)

    • 跟Redis的过期时间一样

6.2:为什么要使用:

  • 异步处理 : 调用者无需等待

    将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间

  • 应用程序解耦合 : 解决了系统之间耦合调用的问题

    MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。

  • 削峰 : 抵御洪峰流量,保护了主业务

    并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常.

    比如:

    在下单的时候就会往数据库写数据,但是在高峰期时候,并发量会突然激增,这时候直接访问数据库数据库会卡死. 这时候可以通过mq将消息保存起来,然后按照自己的消费能力来消费,这样慢慢写入数据库。

6.2:如何保证数据一定被发送?

使用本地消息表(mysql)和定时任务(quartz)和MQ的响应机制(ComfirmFallback,ReturnFallback)

在发送消息时,在本地的mysql数据库中进行记录一条待发送信息.一个定时任务不断检查,是否发送成功,如果发送成功,将记录状态修改.如果发送失败者再次发送

6.3: rabbitmq如何保证消息不丢失?保证数据一定会被消费,如何保证?(两者一个意思)

RabbitMQ中,消息丢失可以简单的分为三种:生产者丢失消息,消费者丢失消息和消息队列丢失消息

  • 生产者丢失消息

解决方案: 从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息

  • 消息队列丢失消息

解决方案: 消息持久化。durable设置为true

  • 消费者丢失消息

解决方案:

设置为手动ack确认机制,当消费者出现异常或者服务宕机时,MQ服务器不会删除该消息,而是会把消息重发给绑定该队列的消费者,

消息不重复消费

解决方案:使用本地消息去重表,每次消费先到表中查询是否消费的标识,如果消费过就直接ack回滚。

6.4: rabbitmq实现数据同步后,消费者挂掉这么办,回滚代码这么实现?

实现ReturnCallback接口重写returnedMessage方法,回滚发送方数据同步操作,具体回滚可以读取发送方记录表中业务ID然后进行回退操作。

6.5: 消息堆积是如何产生,如何解决消息堆积?

当消息生产的速度长时间,远远大于消费的速度时。就会造成消息堆积,比如

  • 生产者突然大量发布消息

  • 消费者挂掉

    解决

    • 增加消费者的多线程处理

    • 增加多个消费者

6.6: rabbitmq常用的消息模式?

RabbitMQ工作模式:

1、简单模式 HelloWorld

一个生产者、一个消费者

2、工作队列模式 Work Queue

一个生产者、多个消费者(竞争关系)

3、发布订阅模式 Publish/subscribe

需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列

4、路由模式 Routing

需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

5、通配符模式 Topic

需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

6.7: rabbitmq什么时候变成死信队列?

  • 消息拒绝并且没有设置重新入队

  • 消息过期

  • 消息堆积,并且队列达到最大长度,先入队的消息会变成DLX 死信队列交换机

6.8:kafka中的broker 是干什么的

broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站

6.9:kafka中的 zookeeper 起到什么作用,可以不用zookeeper么

zookeeper 是一个分布式的协调组件,早期版本的kafka用zk做meta信息存储,consumer的消费状态,group的管理以及 offset的值。考虑到zk本身的一些因素以及整个架构较大概率存在单点问题,新版本中逐渐弱化了zookeeper的作用。新的consumer使用了kafka内部的group coordination协议,也减少了对zookeeper的依赖,

但是broker依然依赖于ZK,zookeeper 在kafka中还用来选举controller 和 检测broker是否存活等等。

6.10:kafka 为什么那么快

顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。

Zero-copy 零拷技术减少拷贝次数

Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。

Pull 拉模式 使用拉模式进行消息的获取消费,与消费端处理能力相符。

七:docker & linux

7.1:linux常用命令

除pwd、cd、ls、vim、tar -zxcf、unzip 等这些太low的入门级命令外

查看僵尸进程

ps -ef | grep java (先查java进程ID)

kill -9 PID(生产环境谨慎使用)

查看日志

ps -ef | grep '日志关键字' --color 以红色字样筛选过滤打印日志中关键字

tail /cat -n 10 test.log 查询日志尾部最后10行的日志;

tail -100f test.log 实时监控100行日志;

tail -f grep test.log 打印实时日志

head -n 10 test.log 查询日志文件中的头10行日志;

head -n -10 test.log 查询日志文件除了最后10行的其他所有日志;

7.2:docker中基本命令

docker search xxx 在docker容器中搜索xxx镜像

docker pull 拉取镜像到docker容器

docker run -it 安装并运行拉取的容器

docker ps -a 查看docker中运行的容器

docker stop xxxx 停止docker容器中某个镜像

docker start xxxx 启动docker容器中某个镜像

docker exec -it xxxx /bin/bash 进入docker容器中某个镜像 exit

###7.3:Dockerfile中最常见的指令是什么?

Dockerfile中包括FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME等10个指令。

1.FROM

格式为FROM image或FROM image:tag,并且Dockerfile中第一条指令必须是FROM指令,且在同一个Dockerfile中创建多个镜像时,可以使用多个FROM指令。

2.MAINTAINER

格式为MAINTAINER user_name user_email,指定维护者信息

3.RUN

格式为RUN command或 RUN["EXECUTABLE","PARAM1","PARAM2".....],前者在shell终端中运行命令,/bin/sh-c command,例如:/bin/sh -c "echo hello";后者使用exec执行,指定其他运行终端使用RUN["/bin/bash","-c","echohello"]

每条RUN指令将当前的镜像基础上执行指令,并提交为新的镜像,命令较长的时候可以使用\来换行。

4.CMD

支持三种格式:

CMD["executable","param1","param2"],使用exec执行,这是推荐的方式。

CMD command param1 param2 在/bin/sh中执行。

CMD["param1","param2"]

提供给

ENTERYPOINT

的默认参数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值