当前位置: 首页 > article >正文

八股文面试题总结(包含主流的面试经典题)

Java基础

1、JDK和JRE的区别是什么** JDK是Java开发工具包,JRE是Java运行时环境,二者的区别在于

JRE是Java程序运行所必须的,它包含jvm和一些Java的基础类库

JDK是Java程序开发所必须的,它包含JRE和一些开发工具

总结一下就是:JDK包含JRE,如果仅仅是运行Java程序,只要有JRE即可;如果是要开发Java程序,则必须要有JDK

2、&和&&的区别是什么 &和&&都是逻辑运算符,都可以实现取并的效果,即符号两边的表达式都为true,结果才是true

不一样的是&&有短路的功能,即当符号前面的表达式为false时,后面的表达式将不再执行,而&没有这个功能

另外,&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作

3、final finally finalize区别是什么 这是Java提供的三个关键字,虽然长的差不多,但是其实没什么联系,使用场景也完全不同

final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、 修饰变量表示该变量是一个常量不能被重新赋值。

finally一般作用在try-catch代码块中,在处理异常的时候,无论程序是否出现异常,写在finally中的代码都会被执行,一般用来释放一些资源

finalize是Object类的一个方法,它会在一个对象被垃圾回收的时候自动被垃圾回收器来调用

4、String、StringBuffer、 StringBuilder 的区别

这几个都是关于字符串的类,他们的区别点有下面几个

可变性:String是不可变对象,其它的都是可变对象 线程安全:String和StringBuffer是线程安全的,StringBuilder是线程不安全的 效率:StringBuilder最高,StringBuffer居中,String效率最低 使用场景:少量字符串的操作使用String,大量字符串的频繁操作在多线程下使用StringBuffer,单线程下可以使用StringBuilder

5、有哪些创建对象的方式 1、new 2、反射 3、克隆 4、json反序列化

6、重写和重载的区别是什么 重载和重写都是用于描述方法间的关系的,但是他们的含义和场景确大不相同

重写是存在于子父类之间的,一般用在父类的方法无法满足子类需求时,子类重写方法来自定义方法功能

它要求子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型

重载是存在于同一个类中的,一般用在功能相似的方法需要接收不同的参数时,

它要求多个方法具有相同的名字,但方法具有不同的参数列表

7、this和super的应用场景是什么 this代表的是当前对象,一般用于在一个方法中调用本对象的成员变量或其它方法

super代表是父类对象,一般在本对象和父对象出现成员名称冲突时,强行调用父对象的成员,也经常用于调用父类的构造方法

8、throw和throws的区别是什么 throws:用在方法的声明上,声明当前方法可能抛出的异常

throw:写在方法里,真正的抛出一个异常,抛出自定义异常。创建对象自定义抛出异常

9、应该使用什么数据类型来计算价格 1、如果不是特别关心内存和性能的话,使用 BigDecimal

2、否则使用预定义精度的 double 类型

3、用int也可以,在表中存储的单位是分(元角分的分),比如一个删除两块五,在表中存250

10、"==" 与 equals 的区别 ==是一个运算符,equals 是 Object 类的方法 用于基本类型的变量比较时:==比较的是值是否相等,equals不能直接用于基本数据类型的比较,需要转换为其对应的包装类型。 用于引用类型的比较时。==和 equals 都是比较栈内存中的地址是否相等。但是通常会重写 equals 方法去实现对象内容的比较。

11、接口和抽象类的区别 它们的共同点是:都不能实例化对象

它们的不同点是:

  1. 抽象类一般用于抽取子类中的共同方法和属性,接口一般用于指定实现类的规范

  2. 抽象类可以有构造方法,作用是用给抽象父类中中的属性赋值;接口中不能有构造方法

  3. 接口中不能含有静态代码块,而抽象类可以有静态代码块

  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

12、说出几个常见的异常 Java中的异常分为运行时异常和编译时异常两大类:

运行时异常都是 RuntimeException 类及其子类异常,这类异常的特点是不强行要求程序员进行处理,常见的有

NullPointerException 空指针异常,调用了未经初始化的对象或者是不存在的对象

IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生

ClassCastException 数据类型转换异常

NoSuchMethodException 方法不存在异常

非运行时异常,也叫编译异常,是 Exception 的子类但不是 RuntimeException的子类,类型上都属于及其子类

它要求程序员在编写代码的过程中提供异常处理方案,否则编译不通过,常见的有:IOException和SQLException等

13、Java 反射有了解吗 反射是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法,并且可以调用它的任意一个方法

它主要应用于大量的框架底层,比如 Spring/Spring Boot、MyBatis 等等

14、浅拷贝和深拷贝区别

浅拷贝:只复制基础类型的数据,对象属性值复制地址,不复制值

深拷贝:要复制的对象所引用的对象都复制了一遍,也就是说把teacher中的属性和值也会一一复制

集合系列

1.说一说集合类的体系结构 我们常见的集合主要有两大类,分别是单列集合和双列集合

2、聊聊集合类的底层数据结构 集合主要分为单列集合和双列集合

双列集合都是Map的实现类,主要有HashMap、LinkedHashMap和TreeMap

HashMap: JDK1.8之前是由数组+链表组成的,JDK1.8之后,为了提升效率,在当链表的长度>8,并且数组长度>=64的时候,链表就会转换为红黑树 LinkedHashMap:继承自HashMap,在HashMap的基础上增加了一条双向链表,来保持键值对的插入顺序。 TreeMap:底层是红黑树

单列集合主要是List和Set

List有ArrayList和LinkedList,ArrayList底层是数组,查询快,增删慢;LinkedList底层是双向链表,查询慢,增删快

Set有HashSet、LinkedHashSet和TreeSet,它的实现原理和对应的Map是一样的,底层都是用的对应Map的key实现

3、ArrayList和LinkedList的区别 ArrayList和LinkedList都是Java中的单列结合,都是有序的,可重复的

不同点有下面几个:

底层数据结构不同:ArrayList 底层是动态数组,查询快,增删慢;而LinkedList底层是双向链表,LinkedList查询慢,增删快 占用内存空间不同:LinkedList比ArrayList更占内存,这是因为它的每个节点除了存储数据,还存储了前后节点的引用两个引用

4、HashMap和HashTable区别 HashMap和HashTable都是Map的子类,都可以存储键值对的数据,区别点在于HashTable是线程安全,HashMap则不是

HashTable的线程安全是通过底层在每个方法上添加synchronized 实现的,因此它的效率要比HashMap低

如果碰到需要线程安全的场景,我们则会使用ConcurrentHashMap,而不用HashTable,所以我对它的了解也就这些

ConcurrentHashMap相比较HashTable来说他用到了乐观锁结合悲观锁一起使用的

5、HashMap的底层原理 HashMap底层数据结构是在JDK1.8之前是数组+链表实现,在JDK1.8之后是数组+链表+红黑树

下面我以map中存储对象的流程给您说一下它的实现原理把

当我们创建一个HashMap的时候,JDK就会在内存中创建一个长度为16的数组

当我们调用put方法像HashMap中保存一个元素的时候,它会先调用key的hashCode方法计算出key的hash值

然后使用得到hash值对数组长度取余,找出当前对象的元素在数组中的位置

接下来,它会判断算出的位置上是否有元素,如果没有,就会将此元素直接存储到当前位置上

如果算出的位置上有元素或者是有链表,它会再调用key的equals方法跟存在元素的key做比较

如果有一个比较得到的结果为true,则会进行值的覆盖,如果都为false,则会将元素追加在链表的末尾

当然,为了降低Hash冲突和链表长度,HashMap还做了一些优化

当元素的数量超过数组大小与加载因子的乘积的时候,就会执行扩容,扩容为原来的2倍,并将原来数组中的键重新进行hash运算,然后分配到新数组中

当链表的长度>8,并且数组长度>=64的时候,链表就会转换为红黑树,当红黑树结点数小于6时将再次转回为链表。

6、HashMap是怎么解决哈希冲突的

当我们调用put方法像HashMap中保存一个元素的时候,它会先调用key的hashCode方法计算出key的hash值然后使用得到hash值对数组长度取余,找出当前对象的元素在数组中的位置,不同的元素计算完毕之后,就可能会被分配到数组中的同一个位置上,这就是所谓的哈希冲突。

我知道的一种解决方式是开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从 hash 表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中

7、HashMap的扩容机制是怎样的 HashMap的扩容机制是指当HashMap中的元素个数超过数组长度乘以负载因子时,就会重新分配一个更大的数组,并将原来的元素重新计算哈希值并插入到新的数组中。

在JDK1.8中,底层是调用resize方法实现扩容的,它的默认做法是:当元素个数超过数组长度的0.75倍时触发扩容,每次扩容的时候,都是扩容为原来的2倍, 扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。

8、为何HashMap的数组长度一定是2的次幂? 1、效率高 2、可以尽量避免hash冲突

9、说一下HashSet的实现原理? HashSet是基于HashMap实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,由于HashMap的键是不能重复的,所有HashSet 不允许重复的值。

10、HashSet如何检查重复? 存储数据的时候有一个equals方法去跟存在的每个值进行比较,表较结果有一个相等,则直接丢弃;不相等,就会被挂在老元素的后面。

线程池

1、创建线程有几种方式 我知道的创建线程的方式大体上可以分为四种:

1)继承Thread类并重写run方法创建线程 2)实现Runnable接口并重写run方法 3)实现Callable 接口并重写call方法 4)使用线程池创建 2、runnable和callable的区别

Runnable接口run方法无返回值;Callable接口call方法有返回值,也 3、start和run的区别 run(): 封装了要被线程执行的代码,本质上就是一个普通方法,可以被调用多次

start(): 用来启动线程,底层会自动去执行run方法中的代码,start方法只能被调用一次

也就是启动线程的时候,只能调用start方法,如果调用的run方法,不会启动新线程,而是当普通方法调用执行

4、notify和 notifyAll的区别 这两个方法都是用户唤醒被wait方法休眠的线程的,区别点在于:

notifyAll:唤醒所有wait的线程 notify:随机唤醒一个 wait 线程 5、sleep 和 wait 的区别 (方法归属不同):sleep是Thread 的静态方法,而wait是Object的成员方法 (醒来时机不同): sleep会在指定的时间后自动苏醒,而wait需要其他线程的唤醒 (锁特性不同):sleep不会释放锁,而wait会释放锁 (使用限制不同):wait必须用在synchronized代码块中,而sleep无此限制 6、说一下线程的状态及转换 在我的理解中,线程共分为7种状态,分别是:新建、就绪、运行、终止以及阻塞、等待、死亡

它们之间的转换关系是这样的:

当线程new出来之后,没有start之前就会处于新建状态 当线程执行start方法之后,就进入就绪状态 当就绪的线程一旦获取到了cpu的执行权,就可以进入运行状态 当线程执行完了run方法之后,就进入了死亡状态 这是一条正常的流程,但是代码在运行状态下可以因为一些原因进入到其它状态,比如说:当进行抢锁操作时,抢锁失败就会进入到阻塞状态 当代码调用了wait或者sleep方法时,就会进入等待状态

这是我对线程状态及其转换的理解

7、现在有T1,T2,T3三个线程,如何保证它们按顺序执行? 用join()方法实现

具体来说就是:可以在t2之前调用t1.join(),在t3之前调用t2.join()

8、synchronized的实现原理是怎样的 在Java中,每个对象都隐式包含一个 monitor(监视器)对象,加锁的过程其实就是竞争 monitor 的过程,

当线程进入字节码monitorEnter指令之后,线程将持有monitor对象,执行monitorExit时释放 monitor 对象

如果在这个过程中,其它线程就会阻塞等待获取该monitor对象

1、线程池的执行流程

当我们提交一个任务到线程池中,线程池的处理流程如下:

首先判断线程池里的核心线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。 如果核心线程都在执行任务,则判断工作队列是否已满,如果没满,则将新提交的任务存储在这个工作队列里。 如果工作队列满了,则判断线程数是否小于最大线程数,如果是,则创建临时线程直接执行任务 如果线程数已经到达了最大线程数,则会执行对应的拒绝策略逻辑 2、线程池的核心参数 线程池在创建的时候最大支持传入7个参数,分别是:

核心线程数、最大线程数、临时线程的空闲时间、临时线程的空闲时间单位、工作队列长度、创建线程的工厂、线程池的拒绝策略

3、线程池的拒绝策略有哪些 拒绝策略是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略,官方提供的有4种:(不用背英文,直接说中文)

AbortPolicy:直接抛出异常,默认策略

CallerRunsPolicy:用调用者所在的线程来执行任务

DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务

DiscardPolicy:直接丢弃任务

4、了解Executors创建线程池吗

了解过,Excutors是JDK提供的一个可以创建线程池的工具类,它可以创建4 种线程池

但是用它创建的线程池有的没有限制最大线程数,有的没有限制阻塞队列的长度,这样的话,极大可能导致OOM

因此我们公司不允许我们使用它,而是使用自己传递参数的方式创建线程池

5、如何确定线程池的核心线程数 线程池的核心线程数跟任务的性质有很大关系对于CPU密集型时,任务可以少配置线程数,推荐配置为CPU核数+1,这样可以使得每个线程都在执行任务 IO密集型时,即该任务需要大量的IO,大部分线程都阻塞,则需要多配置线程数,推荐配置为CPU核数的2倍

什么是代码重排序 重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段

join、yield方法有什么区别 join:在一个线程中调用另一个线程的Join方法是为了顺序执行线程,如,在线程A 中调用了B.join,那么线程A将等待线程B运行完后在执行A。

yield:让出运行机会给别的线程,但是当前线程也可能继续得到机会

用户线程和守护线程有什么区别 用户线程: 是虚拟机启动的线程中的普通线程,当所有用户线程结束运行后,虚拟机才会停止运行,即使还有一些守护线程在运行。 守护线程: 是在程序中创建的线程,它的作用是为其他线程提供服务。当所有的用户线程结束运行后,守护线程也会随之结束,而不管它是否执行完毕。守护线程通常用于执行一些辅助性任务,如垃圾回收、缓存清理等,它们不需要等待所有的任务完成后再退出。

同步方法和同步块,哪个是更好的选择? 在 Java 中,同步方法和同步块都是用于保护 共享资源9、避免线程之间互相干扰的机制。它们都可以用来实现线程安全的操作,但在不同的情况下,选择哪一种更好取决于具体的需求和场景。 1.同步方法:在 java 中,可以使用 synchronized 关键字来创建同步方法。当一个线程进入同步方法时,它就会获得对象的锁,其他线程将无法进入该方法,直到锁被释放。同步方法适合于简单的同步需求,能够提供较为简洁的代码,并且可以确保整个方法的执行都是线程安全9的。 2.同步块:同步块是在方法内部使用 synchronized 关键字来创建的一段同步代码块。同步块允许更加灵活地控制锁的粒度,只对需要同步的部分代码进行同步,减少了锁的粒度,提高了并发性能。同步块适合于需要对代码片段进行精确控制的情况。 在选择同步方法和同步块时,需要考虑具体的情况 如果整个方法都需要同步,则可以选择同步方法,简洁明了, 如果只需要对方法中的部分代码进行同步,或者需要对多个对象进行同步,可以选择同步块。

volatile关键字作用 内存可见性:保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。 屏蔽JVM指令重排序(防止JVM编译源码生成class时使用重排序):指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果是正确的,但是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。非常经典的例子是在单例方法中同时对字段加入voliate,就是为了防止指令重排序。

Lock和synchronized的区别

  1. synchronized 是Java中的关键字;Lock是一个interface接口

  1. synchronized关键字可以直接修饰方法,也可以修饰代码块;而lock只能修饰代码块

  2. synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生; Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁

  3. Lock可以让等待锁的线程响应中断interrupt,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断

  • 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。

MySQL系列

0、MySQL的版本

5.5 5.7 8.x(我们用的是)

1、内连接和外连接的区别

内连接指的是使用左表中的每一条数据分别去连接右表中的每一条数据,仅仅显示出匹配成功的那部分

外连接有分为左外连接和右外连接

左外连接: 首先要显示出左表的全部,然后使用连接条件匹配右表,能匹配中的就显示,匹配不中的显示为null 右外连接: 首先要显示出右表的全部,然后使用连接条件匹配左表,能匹配中的就显示,匹配不中的显示为null

2、drop、delete与truncate区别 这个关键字都是MySQL中用于删除的关键字,区别在于:

delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作 drop 主要用于删除数据表、表中的列、索引等结构 truncate 是直接把表删除,然后再重建表结构 这三种方式在效率方面drop 最高、truncate 其次、delete最低,但是drop和truncate 都不记录日志,无法回滚

3、union与union all的区别 union和union all都是MySQL中用于合并多条select语句结果的关键字,它会将前后两条select语句的结果组合到一个结果集合中,区别在于UNION ALL会返回所有结果,UNION会去掉重复的记录

4、char和varchar的区别 char和varchar是MySQL中的字符串类型,区别在于下面几方面:

最大长度:char最大长度是255字符,varchar最大长度是65535个字符 占用长度:char是定长的,不足的部分用隐藏空格填充,varchar是不定长的 空间使用:char会浪费空间,varchar会更加节省空间 查找效率:char查找效率会很高,varchar查找效率会更低 因此我们如果存储固定长度的列,例如身份证号、手机号建议使用char

其它不定长度的建议使用varchar,使用varchar的时候也要尽量让声明长度贴近实际长度

注意:varchar(50)中50的涵义是最多存放50个字符,varchar(50)和varchar(200)存储hello所占空间一样

5、事务的四大特性

事务是有一组操作,要不全部成功要不全部失败,事务的四大特性指的是原子性、一致性、隔离性、持久性

原子性:事务是最小的执行单位,不允许分割,同一个事务中的所有命令要么全部执行,要么全部不执行 一致性:事务执行前后,数据的状态要保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的 隔离性:并发访问数据库时,一个事务不被其他事务所干扰,各并发事务是独立执行的 持久性:一个事务一旦提交,对数据库的改变应该是永久的,即使系统发生故障也不能丢失

6、并发事务(多个事务同事执行)带来的问题 并发事务下,可能会产生如下的问题:

脏读:一个事务读取到了另外一个事务没有提交的数据 不可重复读:一个事务读取到了另外一个事务修改的数据 幻读(虚读):一个事务读取到了另外一个事务新增的数据

7、事务隔离级别 事务隔离级别是用来解决并发事务问题的方案,不同的隔离级别可以解决的事务问题不一样

读未提交: 允许读取尚未提交的数据,可能会导致脏读、幻读或不可重复读 读已提交: 允许读取并发事务已提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 可重复读: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 可串行化: 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。 上面的这些事务隔离级别效率依次降低,安全性依次升高,如果不单独设置,MySQL默认的隔离级别是可重复读

原理:我知道是MVCC(多版本并发控制),有日志文件和3个隐藏的列处理的

8、数据库建表三大范式 三大范式是指导设计表的原则

第一范式:表中的每一列不能再进行拆分,也就是每一列都应该是原子的:列不可再拆分 第二范式:一张表只做一件事,不要将多个层次的数据列保存到一张表中 :一张表只做一件事 第三范式:数据不能存在传递关系,说的通俗点就是一张表拆成两个表使用外键建立关系:使用外键

在现有的程序设计中认为第三范式是可以不遵守的,也就是通过添加冗余字段,来减少多表联查或计算,我们称为反三范式

9、索引的分类

1)、从物理存储角度上分为聚集(聚簇)索引和非聚集(非聚簇)索引

聚集索引指的是数据和索引存储在同一个文件中

非聚集索引指的是数据和索引存储在不同的文件中

2)、从逻辑角度上分为普通、唯一、主键和联合索引,它们都可以用来提高查询效率,区别点在于

唯一索引可以限制某列数据不出现重复,主键索引能够限制字段唯一、非空

联合索引指的是对多个字段建立一个索引,一般是当经常使用某几个字段查询时才会使用,它比对这几个列单独建立索引效率要高

10、索引的创建原则 我们在建立索引的时候应该遵循下面这些原则:

主键字段、外键字段应该添加索引 经常作为查询条件、排序条件或分组条件的字段需要建立索引 经常使用聚合函数进行统计的列可以建立索引 经常使用多个条件查询时建议使用组合索引代替多个单列索引 除此之外,下面这些情况,不应该建立索引

数据量小的表不建议添加索引 数据类型的字段是TEXT、BLOB、BIT等数据类型的字段不建议建索引 不要在区分度低的字段建立索引,比如性别字段、年龄字段等

11、索引失效的情况 索引失效指的是虽然在查询的列上添加了索引,但是某些情况下,查询的时候依旧没有用到索引,常见的情况有

使用like关键字时,模糊匹配使用%开头将导致索引失效 使用连接条件时,如果条件中存在没有索引的列会导致索引失效 在索引列上进行计算、函数运算、类型转换将导致索引失效 使用 !=、not in、is null、is not null时会导致索引失效 使用联合索引时,没有遵循最左匹配原则会导致索引失效

12、如何知道索引是否失效

MySQL中自带了一个关键字叫explain,它可以加在一个sql的前面来查看这条sql的执行计划

在执行计划中,我们主要观察两列的结果,一列是type,一列是extra

第一个type是重要的列,显示本次查询使用了何种类型,常见的值从坏到好依次为:all、index、range、ref、eq_ref 、const、system

all表示全表扫描数据文件返回符合要求的记录,其他值标识用到索引了

除了type之外我们需要关注一下extra列,如果出现using index最好了,它表示列数据仅仅使用了索引中的信息而没有回表查询

13、MyISAM和InnoDB的区别 MyISAM和InnoDB是目前MySQL中最为流行的两种存储引擎,它们的区别有这几方面:

MyISAM不支持事务,每次操作都是原子的;InnoDB支持事务,支持事务的四种隔离级别 MyISAM不支持外键,InnoDB支持外键 MyISAM仅仅支持表级锁,即每次操作是对整个表加锁;InnoDB支持行级锁,因此可以支持写并发 MyISAM属于非聚集性索引,它的数据和索引不在同一个文件中;InnoDB属于聚集性索引,它的数据和索引在同一个文件中 MyISAM中主键和非主键索引的数据部分都是存储的文件的指针;InnoDB主键索引的数据部分存储的是表记录,非主键索引的数据部分存储的是主键值

14、索引的数据结构是什么 在MySQL中索引使用的数据结构是B+Tree,B+树是基于B树的变种,它具有B树的平衡性,而且树的高度更低

B+树非叶子节点不存在数据只存索引,因此其内部节点相对B树更小,树的高度更小,查询产生的I/O更少 B+树查询效率更高,B+树使用双向链表串连所有叶子节点,区间查询效率更高 B+树查询效率更稳定,B+树每次都必须查询到叶子节点才能找到数据,而B树查询的数据可能不在叶子节点,也可能在,这样就会造成查询的效率的不稳定

15、什么是覆盖索引,什么是回表?

覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到 。

如果没有全部找到会回表查询

通过非聚簇索引找到对应的主键值,到聚簇索引中查找整行数据,这个过程就是回表

主键索引就是聚簇索引

16数据库中的锁有哪些

MySQL中的锁从不同维度可以分为不同的种类

1)、从锁的粒度上可以分为表锁和行锁

表锁指的是会锁定修改数据所在的整个表,开销小,加锁快,锁定粒度大,发生锁冲突概率高

行锁指的是会锁定修改数据所在的行记录,开销大,加锁慢,锁定粒度小,发生锁冲突概率低

2)还有两种概念上的锁是悲观锁和乐观锁

悲观锁是指一个事务在修改数据的时候,总是认为别人也会修改此数据,所以强制要使用锁来保证数据安全

乐观锁是指一个事务在修改数据的时候,总是认为别人不会修改此数据,因为不加任何锁

这种情况下万一在当前事务修改的时候,数据被其它事务也修改了,机会出现问题,此时常用的方案是:

给数据表中添加一个version列,每次更新后都将这个列的值加1,读取数据时,将版本号读取出来

在执行更新的时候,会先比较版本号,如果相同则执行更新,如果不相同,说明此条数据已经发生了变化,就放弃更新或重试

17、MySQL日志类型 MySQL的很多功能都是依靠日志来实现的,比如事务回滚,数据备份,主从复制等等,常见的日志有下面几个

binlog归档日志

负责记录对数据库的写操作,一般用在主从复制过程中记录日志,从库拷贝此日志做重放实现数据同步

redolog重做日志

用于确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘

在重启 mysql 服务的时候,根据 redo log 进行重做,从而达到事务的持久性这一特性

undo log 回滚日志

保存了事务发生之前的数据的一个版本,可以用于回滚

18、MySQL主从复制的流程 主从复制用于MySQL主从集群的主节点向从节点同步数据,主要是依靠MySQL的binLog实现的,大体流程分为三步:

Master 主库在事务提交时,会把数据变更记录在二进制日志文件 BinLog中 从库读取主库的二进制日志文件 Binlog ,写入到从库的中继日志 RelayLog slave重做中继日志中的事件,将改变反映它自己的数据

19、谈谈你对sql的优化的经验 我在企业中优化Sql大体分为三步:

第一:查找问题sql,主要手段是开启mysql的慢查询日志,它会将执行时间较长的sql记录记录下来 找到sql之后,我会分析出现问题的原因,原因很多,主要字段类型选择错误、sql语句效率低、索引失效等等 根据问题不同,我会再去定具体的解决方案 简单给您说几个常见的把

确定选择的引擎是否合适

myisam适合于查询为主,增删较少,无事务要求的数据表

Innodb适用于有事务处理,或者包括很多的更新和删除的数据表

第二:表设计是否合理

单表不要有太多字段,建议在20以内

合理的加入冗余字段可以提高查询速度

确定字段的数据类型是否合适

数值型字段的比较比字符串的比较效率高得多,字段类型尽量使用最小、最简单的数据类型

设置合适的字符串类型(char和varchar)char定长效率高,varchar可变长度,效率稍低,varchar的长度只分配真正需要的空间

尽量使用TIMESTAMP而非DATETIME,尽量设计所有字段都得有默认值,尽量避免null

第三:确定sql的书写是否有的题

SELECT语句务必指明字段名称,避免直接使用select *

SQL语句中IN包含的值不应过多

可以用内连接,就尽量不要使用外连接

使用连接连接查询来代替子查询

适用联合(UNION)来代替手动创建的临时表

表数据比较多的时候是否添加了合适的索引

表的主键、外键必须有索引

经常出现在where子句中的字段,特别是大表的字段,应该建立索引

经常用于排序、分组的字段,应当建立索引

加上索引之后,还应该使用Explain来确认索引是否生效

如果上面的几项都没有问题,那可能就是因为服务器性能或者数据量过大导致的查询慢,此时可以考虑读写分离

也就是我们搭建一个MySQL的主从集群,让1个主节点负责写入数据,多个从节点负责查询数据,已分摊查询压力

Maven 和mybatis

0、Maven的作用(好处)

1)统一项目结构

2)可以做项目构建(编译 compile、测试test、打包package)

3)依赖管理

1、Maven常见命令

clean:清除编译后的文件就是删除target文件夹

compile:编译项目

test: 执行junit测试代码

package:把项目打成jar包

install:把打好的包放到本地仓库

deploy:把打好的包放到私服上

2、说下Maven生命周期

Maven的命令是有先后顺序的,执行后面的命令会自动执行前面的命令

举例: 我执行了package命令就自动会执行 compile test

3、jar包冲突怎么办?(等讲到Maven高级再说)

版本锁定

4、Mybatis中#{}和${}的区别 ${}表示的是字符串拼接,#{}是预编译处理,是占位符。

${}存在SQL注入问题,#{}可以有效的防止SQL注入。

4、${}有SQL注入问题是不是就不会用到了

当然这也不是说${}就没有使用场景

比如:如果sql中需要动态传递表名或者字段名,那就只能使用${}了,这种情况的值是程序员自己传过去的,不是页面直接传的

5、当实体类中的属性名和表中的字段名不一样 ,怎么办 1、开启驼峰映射:这种方式可以处理掉字段和属性满足驼峰转换规则的那部分

2、字段起别名:可以在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致

3、手动映射:mybatis提供了@Results注解 和在xml中提供了resultMap标签,它可以通过来自定义映射关系来保证字段和属性的映射

6、MyBatis动态SQL了解吗 动态SQL是为了解决SQL语句灵活性不足的问题而提出的一种技术,它可以根据条件拼接SQL语句以不同的查询需求

MyBatis常用的动态SQL标签有:

<if> 用来判断 符合条件的会拼接到sql中

<where> 如果没有条件时可以去除where关键字 还可以去除第一个And或or关键字

<set> 去除最后一个逗号

<foreach > 用来批量处理的 比如批量删除拼接 in后面的值

<sql> 定义一个sql片段 就是提取公共的sql <include> 引入sql片段

7、Mybatis的一级缓存和二级缓存?

一级缓存是sqlSession级别的,在同一个sqlSession中执行同样的sql语句时如果有缓存直接读取缓存中的数据

二级缓存是namespace(Mapper)级别的

一级缓存是默认开启的 二级缓存需要手动开启,一般二级缓存不用

前端

1、有哪些常用的事件(可以直接说过中文名)

离焦事件 onblur

聚焦事件 onFocus

鼠标移上去 onmouseover

鼠标移出去 onmouseout

点击事件 onclick

表单提交 onsubmit

2、设置样式的时候有哪些常用的选择器

标签选择器

标签名{}

class类选择器

.class名字{}

id选择器

#id值{}

3、你们用的VUE哪个版本?

VUE3

4、v-if和v-show的区别

都是可以控制显隐、

v-if控制的话在浏览器查看源码是看不到隐藏的内容

v-show控制的话在浏览器查看源码是可以看到隐藏的内容 display样式控制

5、VUE的生命周期

VUE对象从创建到销毁 会自动触发8个函数,叫做钩子函数

我们经常用mounted 打开页面时执行查询数据的方法

6、TypeScript是什么

主要是控制JavaScript中的类型

7、做过前端吗? 我们用的是ElementPlus和VUE3,管理类型的页面是没有问题的

spring系列

1、谈谈你对SpringIOC的理解 IOC,也叫控制反转, 对象以前是我们程序员自己new的,但是现在对象都是由Spring创建并放入到了IOC容器中,如果需要使用某个对象的时候直接从容器中获取;

两个细节:1、Spring通过哪种方式创建的:反射

                2、创建的对象放入到哪里了:ConcurrentHashMap,作为IOC容器

2、Spring中有哪些依赖注入方式

依赖注入指的是Spring给对象中属性进行赋值的过程,主要包括两种方式:

1、@Autowired :根据类型注入

2、构造器依赖注入:构造器注入是指容器调用一个类的构造器创建对象时,直接传入给属性赋值

Setter方法注入:Setter方法注入是指容器在创建对象完成后,通过调用属性的Setter 方法,可以属性赋值,这种是早期有xml配置时经常用。

3、你用过哪些Spring注解 我们常用的Spring注解主要分类下面几大类:

1、创建对象:@Component、@Controller、@Service、@Repository

它们都可以标注在自己开发的类上,Spring会使用注解标注的类创建出对象,然后放入容器

2、依赖注入:@Autowired

标注在属性或者属性对应的set方法上,Spring会根据被标注属性的类型自动对属性进行赋值

3、依赖注入:@Qualifier

和@Autowired一块使用,在同一类型的bean有多个的情况下Spring会根据name进行选择注入

4、配置类:@Configuration、@Bean

主要标注在配置类中,用于声明配置类和向Spring容器中放入一些配置有关的对象

5、当然还有一些平时用的不是特别多的

比如:声明注解扫描的@ComponentScan,声明Bean的作用域的@Scope,用于切面编程的@Around,@Pointcut等等

4、SpringBean的作用域有几种 在Spring中作用域是用来对象的存活范围的,它支持5种作用域

第一种是单例,配置为单例的对象会跟随Spring容器创建而创建,跟随Spring容器销毁而销毁,在Spring容器中无论获取多少次单例对象,得到的都是同一个,这也是Spring中的对象的默认作用域

第二种是多例,配置为多例的对象在每次获取的时候才会创建,而且每次获取到的都不一样

还有三种分别是request、session和application,目前已经基本不再使用

其实,在我们平时的开发过程中,对象基本上都是配为单例的,这样可以有效的节省资源,只有单例对象存在线程安全问题时,才考虑调整为多例。

5、Spring中的bean线程安全吗 Spring中的Bean主要分为单例和多例,默认是单例的,如果单例bean有成员变量,多线程操作时要同时修改这个成员变量的值就有可能出现线程不安全情况,解决方式有两种:

1)将Bean的作用域由单例改为多例,这样虽然解决了,但是不推荐,因为每个线程就产生一个对象,并发量大的时候可以造成内存溢出; 2)将需要的可变成员变量保存在ThreadLocal中, ThreadLocal本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。

6、谈谈你对SpringAOP的理解 AOP,又叫面向切面编程,就是在不改变原代码的基础上对原方法做增强,比如做事务管理、日志、性能监视、安全检查等等;

Spring AOP是基于动态代理的,它底层同时支持JDK和CGLIB的代理方式,并且会根据被代理类是否有接口自动选择最合适的代理方式

7、AOP的代理有几种方式 JDK动态代理和CGLIB动态代理

JDK动态代理只能对有接口的类进行代理,而且效率较高 CGLIB可以对任意的类进行动态代理,但是效率上不如JDK 因此在进行代理时,如果被代理类有接口,就用JDK;如果没有接口,就用CGLIB

8、Spring的通知类型有哪些

四大通知+环绕通知 总共有5种

四大通知指的是

前置通知:在某切点之前执行的通知 返回后通知:在某切点正常完成后执行的通知 抛出异常后通知:在某切点抛出异常退出时执行的通知 后置通知:在某切点退出的时候执行的通知(不论是正常返回还是异常退出)

环绕通知:可以替代四大通知

9、Spring事务传播行为有几种 事务传播行为是为了解决业务层方法之间互相调用的事务问题。

当事务方法被另一事务方法调用时,必须指定事务应该如何传播。

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

Spring支持7个种事务传播行为的:

必须事务:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务

必须新事务:创建一个新的事务,如果当前存在事务,则把当前事务挂起

强制事务:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

支持事务:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

不支持事务:以非事务方式运行,如果当前存在事务,则把当前事务挂起

强制无事务:以非事务方式运行,如果当前存在事务,则抛出异常

嵌套事务:如果当前存在事务,则创建一个当前事务的嵌套事务来运行;如果当前没有事务,则创建一个事务

嵌套事务是已存在事务的一个子事务,嵌套事务开始执行时,将取得一个保存点,

如果这个嵌套事务失败,将回滚到此保存点

嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交

10、Spring中的事务是如何实现的

在方法上添加@Transactional注解就可以实现控制事务,是基于Spring的AOP机制的

他的原理就是要保证在同一个方法中使用的是同一个Connection对象,并且把这个Connection对象的事务提交方式改成手动提交,这样可以统一管理事务的提交和回滚

怎么保证多个方法之间使用的是同一个Connection的?把Connection放到了ThreadLocal中

11、什么情况下会导致事务失效 方法上明明有@Transactional但是事务没有生效 1)、 事务的传播行为会导致事务失效,举例 A方法调用B方法时 B方法的事务传播行为是REQUIRES_NEW A方法事务回滚不会把B方法的事务回滚 2)、 rollbackFor指定的异常没有包含抛出的异常 3)、 以无事务方法调用本类中的事务方法 4)、 使用了异步--多线程

12、Spring中的设计模式有哪些 工厂模式:Spring使用工厂模式通过 BeanFactory和 ApplicationContext创建 bean 对象 单例模式: Spring 中的 bean 默认都是单例的 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术 模板方法:用来解决代码重复的问题。比如 RestTemplate、jdbcTemplate、 JpaTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 ApplicationListener。

13、Spring是怎么解决循环依赖的

循环依赖是指A依赖B,B又依赖A

这就是涉及到SpringIOC容器的三级缓存了

singletonObjects:一级缓存,存放完全初始化好的 Bean 的集合,从这个集合中取出来的 Bean 可以立马返回

earlySingletonObjects:二级缓存,存放创建好但没有初始化属性的 Bean 的集合,它用来解决循环依赖

singletonFactories:三级缓存,存放单实例 Bean 工厂的集合 singletonsCurrentlyInCreation:存放正在被创建的 Bean 的集合

14、SpringBean的生命周期

具体的内容记不清了但是基本概括下来就是5点内容

构造 Bean 对象 设置 Bean 属性 执行初始化方法(如果有就执行) Bean 调用 销毁 Bean

中间如果实现了一些Aware相关接口的话可以自动执行接口带的实现方法

15、SpringMVC的常用注解有哪些 我们常用的Springmvc注解主要分类下面几大类:

1、用于声明Bean到Springmvc容器:@Controller、@RestController

区别在于后者还可以将返回的集合或对象转换为JSON直接返回

2、设置请求路径:@RequestMapping、@GetMapping、@PostMapping 、@PutMapping、@DeleteMapping

第一个是通用的,可以接收各种类型的请求;后面四个只能直接对应类型的请求

3、接收请求参数:

@RequestBody: 接收请求体中的json数据

@PathViriable:接收请求路径中的参数

@RequestHeader:接收请求头中的参数

@RequestParam:一般用于给参数设置默认值或者完成请求参数和controller方法参数的映射

16、SpringMVC如何处理统一异常 SpringMVC的异常处理底层是通过AOP实现的,它的核心思想是将异常处理的代码和业务逻辑代码分离开来

使用它之后,我们在自己的业务代码中不需要在处理异常,有异常直接就上抛到框架中

框架就会将异常交给自定义的全局异常处理器中统一处理

自定义全局异常处理器,会用到两个注解:

@RestControllerAdvice 标注在类上,声明被标注的类是一个用于专门处理异常的类 @ExceptionHandler 标注在异常处理类中的方法上,声明被标注的方法可以处理哪些异常

springboot系列

1、项目中为什么选择SpringBoot SpringBoot简化了Spring,可以快速搭建企业级项目,而且开发起来效率也会更高,它的主要优点如下:

版本锁定:SpringBoot在父工程中进行了大量常见依赖的版本锁定,省去了我们查找依赖版本和解决版本冲突的麻烦 简化依赖:SpringBoot以功能化的方式将需要的依赖进行组装,并且允许程序员以starter的方式进行引入 简化配置:SpringBoot实现了大量依赖框架的默认配置项,程序员无须再进行自己配置 简化启动方式:SpringBoot内置了一个tomcat,使用它开发的程序无需再进行tomcat部署,可直接运行 2、SpringBoot的自动装配原理 自动装配就是自动地把其他组件中的Bean装载到IOC容器中,不需要开发人员再去配置文件中添加大量的配置,我们只需要在SpringBoot的启动类上添加一个@SpringBootApplication的注解,就可以开启自动装配,SpringBootApplication底层最重要的一部分是@EnableAutoConfiguration这个注解来实现的,它作用是:

读取所有jar包中两个指定配置文件中的所有配置类(xxxxAutoConfiguration),配置类中需要使用@Conditional注解来声明配置成立的必要条件

3、SpringBoot的核心注解是哪个 SpringBoot的核心注解在启动类上,叫@SpringBootApplication,主要组合包含了以下3个注解:

@SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能 @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项 @ComponentScan:Spring组件扫描,默认会扫描启动类所在的包

4、SpringBoot中的starter是干什么的 当项目足够复杂时,因为涉及的组件太多了,就需要引入很多的依赖,此时管理依赖就变的很麻烦

此时SpringBoot的starter就派上用场了,每个starter都可以为我们提供某个服务场景所需要的一系列依赖

在导入starter之后,SpringBoot主要帮我们完成了两件事情:

相关组件的自动导入 相关组件的自动配置

5、SpringBoot读取配置的方式有几种 SpringBoot常见的读取配置信息的方式有两种:

使用@Value配合EL表达式(@Value(“${name}”))注解直接注入对应的值 使用@ConfigurationProperties注解把对应的值绑定到一个配置对象,然后将配置对象注入到需要的地方 推荐使用使用第二种方式,在配置比较多的情况下,操作简单,可读性好

6、如何理解拦截器 拦截器是Spring提供的一种拦截机制,目的是实现对指定请求路径进行拦截,然后做成指定的处理

自定义一个拦截器需要实现HandlerInterceptor,并重写接口中定义的3个方法

preHandle: 这个方法在Controller处理请求之前被调用,通过方法的返回值可以确定是否放行请求 postHandle:这个方法在Controller处理请求之后被调用 afterCompletion:这个方法将在整个请求结束之后被调用,此方法主要用于进行资源清理

7、拦截器和过滤器的区别是什么 拦截器和过滤器都可以实现请求的拦截处理,不同点有下面几个:

技术栈所属不同:过滤器属于JavaWeb技术,依赖Servlet容器;而拦截器是属于Spring的技术 拦截范围不同:过滤器可以拦截所有请求,而拦截器主要是针对发往controller请求 拦截位置不同:过滤器在前端控制器前拦截行,而拦截器在前端控制器后拦截

8、SpringBoot项目如何实现方法的异步调用 异步调用指的是a方法在调用b方法的时候,无需等待b方法执行完毕,就可以继续向下执行

一般用在a方法无需使用b方法返回结果的场景下,可以在一定程度上提高运行效率

在SpringBoot中使用异步调用是很简单的,只需要做两个操作就可以了

在启动类上添加@EnableAsync注解,开启异步调用支持 在被调用的方法上添加@Async注解

9、SpringBoot中如何实现定时任务 在SpringBoot中使用定时任务主要有两种方式,一个就是使用SpringTask,另一个则是使用第三方框架Quartz

SpringTask主要是通过@Scheduled注解来实现定时任务触发的,格式如下

主要属性如下:

fixedRate:按一定的频率执行任务,参数类型为long,单位 ms fixedDelay:上一次任务执行完后多久再执行,参数类型为long,单位 ms initialDelay:延迟多久再第一次执行任务,参数类型为 long,单位 ms cron:使用cron表达式指定任务在特定时间执行

10、SpringBoot中如何解决跨域问题 跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制

当浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议任一不同,都属于跨域 SpringBoot解决跨域很简单,执行添加一个配置类实现WebMvcConfigurer接口然后重写addCorsMappings方法即可@Configuration

Redis系列

1、项目中为什么用Redis

操作速度快:Redis的数据都保存在内存中,相比于其它硬盘类的存储,速度要快很多 数据类型丰富:Redis支持 string,list,set,Zset,hash 等数据类型,基本满足我们开发中的各种使用场景

2、Redis的数据类型有哪些 Redis最常见的数据类型有5种,分别是String、List、Hash、Set、ZSet,下面给您详细介绍一下:

String:简单的 key-value 类型,最大能存储512MB数据。场景:计数、缓存文章标题、微博内容等

List:底层是链表,特点是:增删容易,随机访问困难。场景:发布与订阅或者说消息队列

Hash:类似于Java中的HashMap,适合存储对象。场景:系统中对象数据的存储

Set:是一种无序集合,可以方便的求交、并、差集。 场景:共同关注、共同粉丝、共同喜好等功能

ZSet:相比于set来讲,多了1个权重参数 score,元素会按照score进行排序。场景:各种排行榜,弹幕消息

3、Redis为什么这么快 Redis之所以运行速度比较快,主要是由于这样一些原因:

纯内存操作:Redis的绝大部分请求是纯粹的内存操作,非常快速

单线程:Redis的核心部分是单线程运行的,避免了不必要的上下文切换,也不存在线程切换导致的 CPU消耗

使用 I/O 多路复用模型和非阻塞 IO

什么是 I/O 多路复用

I/O多路复用是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源 目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能 其中Redis的网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求,比如,提供了连接应答处理器、命令回复处理器,命令请求处理器 在Redis6.0之后,为了提升更好的性能,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

4、Redis的持久化方案 两种数据持久化的方式,分别是RDB和AOF

RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为RDB文件,默认是保存在当前运行目录。

AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件

5、如何保证Redis数据与MySQL一致 我们用的是同步双写或双删 即在程序更新完新增MySQL之后后立即同步更新redis

或者mysql数据发生改变后直接删除Redis中的数据,什么时候用时再加入缓存中

6、什么是缓存穿透, 怎么解决

而缓存穿透指的是请求一直在查询一个数据库中不存在的数据,这样缓存中没有,请求就会到达数据库,而数据库也没有,也就没法缓存

所以每一次请求都会直接到数据库中查询,这就极有可能导致数据库被压垮

常用的解决方案有两个:

1)查询返回的数据为空,仍把这个空结果进行缓存,但过期时间尽量设置稍短一些

2)使用布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定存在的数据会被这个 bitmap 拦截掉,从而避免了对DB的查询 7、Redis的过期删除策略有哪些 Redis的过期删除策略指的是当Redis中的key过期之后在什么时候进行删除的处理方案,常用的删除策略就两个:

惰性删除:只会在取出 key 的时候才对数据进行过期检查,过期了就删除 定期删除:每隔一段时间抽取一批 key执行删除过期 key 操作 两者相比,定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是定期删除+惰性/懒汉式删除。

8、Redis的内存淘汰策略有哪些 Redis的内存淘汰策略指的是当Redis的内存已经存满,又有新的数据需要保存时的处理方案,官方提供了8种淘汰策略:no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。 volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰 volatile-random:从已设置过期时间的数据集中任意选择数据淘汰 volatile-lfu:从已设置过期时间的数据集中挑选最不经常使用的数据淘汰 allkeys-lru:在所有的数据集中选择最近最少使用的数据淘汰 allkeys-random:在所有的数据集中任意选择数据淘汰 allkeys-lfu:在所有的数据集中选择最不经常使用的数据淘汰

其实主要记住:可以删除最近最少使用的,或者提前删除即将过期的

9、Redis集群有哪些方案 在Redis中提供的集群主要有三种,分别是主从、哨兵和分片集群

主从集群主要用来解决Redis的并发问题,一般是一个主节点负责数据写入,多个从节点负责数据读取,主节点的数据会实时同步给从节点

哨兵集群主要用来解决Redis的高可用问题,哨兵会监控集群中节点的状态,并在主节点出现问题时进行重新选主

分片集群主要用来解决Redis的海量数据存储问题,它要求有多个主节点,然后数据写入的数据会经过计算落到其中一个上

在这个计算的过程中Redis引入了哈希槽的概念,Redis集群有16384个哈希槽,每个 key通过CRC16校验后对16384取模来决定放置哪个槽

而分片集群的每个节点负责一部分 hash 槽,这样就可以计算出一个key会出现在哪个节点上了,查询的时候也是同时的方式来定位即可

10、什么是缓存预热 缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。

避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。

如果不进行预热,那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中,对数据库造成流量的压力。

缓存预热解决方案主要有下面几个:

数据量不大的时候,工程启动的时候进行加载缓存动作 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新 数据量太大的时候,优先保证热点数据进行提前加载到缓存

11、什么是缓存击穿,怎么解决

缓存击穿指的是对于一个设置了过期时间的key,在其缓存失效的瞬间,有大量的请求访问这个它,这些请求在缓存找不到就会直接到数据库,导致数据库被压垮

常用的解决方案有两个:

使用互斥锁:当缓存失效时,不立即去数据库查询,而是先去获取一把全局锁,那个线程获取到了,就去数据库查询,获取不到的就等待重试查询缓存

修改设置key有效期的逻辑,大体如下:

在设置key的时候,不给它设置过期时间,而是单独设置一个过期时间字段一块存入缓存中

当查询的时候,从redis取出数据后判断时间是否过期,如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据

12、什么是缓存雪崩,怎么解决

缓存雪崩指的是大量的key在某一时刻同时失效,这样大量的请求全部转发到DB,DB 瞬时压力过重雪崩

解决方案也很简单,就是在设置key的过期时间的时候,尽量加一些随机值,这样缓存过期时间的重复率就会降低

13、用过Redis的事务吗 Redis中本身是没有事务的概念的,但是他有几个命令组合起来能实现类似于事务的效果。也就是说,Redis事务的本质是一组命令的集合。

这里用到的命令主要有5个,分别是:

MULTI:用来组装一个事务 EXEC:执行一个事物 DISCARD:取消一个事务 WATCH:用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行 UNWATCH:取消 WATCH 命令对所有key的监视 总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。Reids中,单条命令式原子性执行的,但事务不保证原子性,且没有回滚。

Linux和docker命令

简单的有:

  1. 目录结构 根目录/ /usr/local /root

  2. 查看目录 ls -la ls -l --->ll pwd

  3. 切换目录 cd ./ ../ / /root cd /usr cd ../usr

  4. 创建目录 mkdir /a/b/c -pv

  5. 删除目录(文件) rm -rf /

  6. 复制目录(文件) cp 源文件 放的位置

  7. 剪切目录(文件) mv 源文件 放的位置

高级一点的有:

  1. 编辑文件 touch 文件 vi vim i------>esc : w q cat 文件

  2. 查看文件 tail -f

  3. 文件压缩 (tar.gz) tar zcvf 压缩包

  4. 文件解压 tar zxvf 压缩包

  5. 文件授权 chmod 777 -R test.java

  6. 查看端口(比如8080) netstat -no|grep 8080

  7. 查看进程(比如tomcat) ps -ef|grep tomcat

  8. 强杀进程 kill -9 进程id

=======================Docker=====================

docker search 镜像名称 -----------------> 从网上搜索镜像 docker pull 镜像名称:tag ----------------->从网上下载镜像 docker images ----------------->查看宿主机上已经有的镜像 docker run -d --name=容器名称 -p 宿主机端口号:容器的端口号 -v 宿主机目录:容器的目录 镜像名称 -----------------> 以哪个镜像启动容器 docker ps -----------------> 查看正在运行的容器 docker ps -a -----------------> 查看所有的容器 docker rm 容器名称 or 容器ID -----------------> 删除容器 docker rm -f 容器名称 or 容器ID -----------------> 强制删除容器 docker stop|start|restart 容器名称 -----------------> 停止|启动|重启容器 docker load -i 镜像.tar -----------------> 解压镜像的压缩包 docker exec -it 容器名称 bash | sh ----------------->以交互式方式进入容器中 docker inspect 容器名称 ----------------->查看容器的详细信息 docker logs 容器名称 ----------------->查看容器的运行日志

Dockerfile -----------------> 制作镜像 Docker-compose.yml -----------------> 批量管理容器 docker compose up -d 启动容器 docker compose down 关闭并移除容器 docker compose stop 仅仅关闭容器

springcloud系列

1、怎么保证消息的可靠性(怎么保证消息不丢失)?

消息发送方:开启消息确认机制,如果消息发送失败,记录日志(log文件,table表)

MQ本身:交换机、队列、发送的消息都要持久化 使用SpringBoot开发 默认都是持久化的

消息消费方:开启确认机制 auto模式 以及设置重试次数,到达重试次数后把消息放到一个error队列中

2、消息重复消费怎么解决(怎么保证消息幂等性)?

发送消息时给消息设置id(在消息转换器那里设置),消费成功后把消息id放到Redis中,再次消费时先从Redis根据消息id获取,如果获取到了,证明这个消息已经消费过了,如果没有获取到证明没有消费过,继续消费就可以了

3、消息出现堆积怎么办?

1)、消费方开启多线程 2)、消费方集群部署 3)、Lazy Queue惰性队列

4、是否了解死信队列,有什么用?

1、超时未处理的消息会变成死信,死信需要发送到死信交换机,再路由到死信队列,实现延迟任务

最终借助了MQ的插件实现了延迟任务

5、在项目中你们使用的是MQ的哪种模式?

Direct :消息发送到交换机、通过key路由到队列 Topic:key支持通配符 #

6、你们为什么不用RocketMQ | Kafka?

不知道啊!!!!!!!!!!领导定的啊~!!!!!!!!!!!!11

1、常用SpringCloud的组件?

1)、Nacos 注册中心和配置中心 2)、OpenFeign 远程调用 3)、LoadBalancer 负载均衡的 Ribbon(2021之前的版本) 4)、Gateway 网关 5)、Sentinel 服务保护 限流 现成隔离 熔断 降级方法(需要写代码) 6)、Seata 分布式事务(涉及到多个数据源) XA : 执行SQL后事务不提交,统一提交或回滚 AT : 执行SQL后事务提交,并记录数据改变之前和之后的数据放到undo_log表中

2、OpenFeign工作原理 Feign是SpringCloud技术栈中用于远程调用的一个HTTP客户端,主要作用是将远程服务调用格式本地方法调用格式统一成一致的

Feign的工作步骤如下:

1)、首先需要在SpringBoot的启动类上添加@EnableFeignClients 注解开启对Feign的支持

2)、创建一个Feign的Interface接口,接口上需要添加一个@FeignClient的注解,注解中value值是需要调用的微服务的名字

3)、在接口中创建方法,创建的方法要和即将调用的方法 请求路径、请求方式, 请求参数、返回类型保持一致 3、什么是Sentinel(他能做什么)

他是SpringCloudAlibaba下的一个用来做服务保护的组件

限流、线程隔离、熔断、还可以支持降级方法

4、LoadBalancer的负载均衡策略有哪些 官方提供了好几种负载均衡策略

轮询策略:按照一定的顺序依次调用服务实例 权重策略:根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。 随机策略:从服务提供者的列表中随机选择一个服务实例

5、Nacos作为注册中心工作原理 Nacos是SpringCloudAlibaba技术栈的一项技术,在项目中主要用作服务注册中心和服务配置中心

Nacos做服务注册中心主要具备下面这些能力

服务注册:服务提供者会将自己的地址信息注册到Nacos中,在nacos中形成一张服务清单

服务发现:服务消费者会从Nacos中查询服务提供者的信息,并且缓存到本地,并且每隔30s更新一次

当服务提供者的地址发生变化之后,Nacos也会主动推送最新的地址信息给消费者

服务续约:服务提供者会间隔一定时间就给Nacos发送心跳,表明自己在线服务剔除:当nacos一段时间内接收不到服务微服务的续约请求时或者收到微服务的下线请求时,就会将服务地址从服务清单中删除

jvm基本认知

  1. JVM的主要组成部分有哪些? 作用是什么? 类加载器(ClassLoader): 负责将字节码文件加载到内存中 运行时数据区(Runtime Data Area): 用于保存java程序运行过程中需要用到的数据和相关信息 执行引擎(Execution Engine): 字节码文件并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎将字节码翻译成底层系统指令 本地库接口(Native Interface): 会被执行引擎调用参与字节码的翻译

  2. JVM运行时数据区包含几部分? 每部分的作用是什么?

  3. java中都有哪些类加载器? 3 启动类加载器 扩展类加载器 应用程序类加载器

  4. 什么是双亲委派模型? 双亲委派模型针对的是 Java 虚拟机中三个类加载器的,这三个类加载器分别是:

启动类加载器(Bootstrap ClassLoader)

扩展类加载器(Extension ClassLoader)

应用程序类加载器(Application ClassLoader)

  1. 类装载的执行过程是怎样的? 类从被加载到虚拟机内存中开始,直到卸载出内存为止,整个生命周期包括了7个阶段:加载、验证、准备、解析、初始化、使用、卸载

    1. 加载: 这个阶段会在内存中生成一个代表这个类的java.lang.Class对象

    2. 验证: 这个阶段的主要目的是为了确保Class文件包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

    3. 准备: 这个阶段正式为类变量分配内存并设置类变量的初始值,注意这里的初始值指的是默认值,而不是代码=后的实际值

    4. 解析: 这个阶段将符号引用替换为直接引用,比如方法中调用了其他方法,方法名可以理解为符号引用,而直接引用就是使用指针直接引用方法

    5. 初始化: 这个阶段是执行类构造器方法的过程,是类加载的最后一步,到了这一步Java虚拟机才开始真正执行类中定义的Java程序代码(字节码)

    6. 使用: 这个节点程序在运行

    7. 卸载: 这个阶段类Class对象被GC

  2. 怎么判断对象是否可以被回收? 可达性分析算法 Roots 通过一系列为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链 当一个对象到GC Roots没有任何引用链相连时,则证明该对象是不可用的。

  3. JVM有哪些垃圾回收算法? 标记-清除 标记-整理-清除 复制 分代


http://www.kler.cn/news/340506.html

相关文章:

  • Oracle 表空间异构传输
  • Spring Boot集成RBloomFilter快速入门Demo
  • 【无标题】获取网页文本
  • OpenJudge | Disk Tree
  • vue 条件渲染
  • Python的pandas库基本操作(数据分析)
  • 【LeetCode: 344. 反转字符串 | 双指针模拟】
  • Golang 进阶1 —— 面向对象
  • 图的最短路径算法
  • threads_created增加过大?
  • TLS 加密的原理和过程
  • C++实现字符串 trim,C++实现字符串split, C++如何分割字符串为数组,C++如何去除字符串两边的空格
  • (笔记)第三期书生·浦语大模型实战营(十一卷王场)–书生基础岛第3关---浦语提示词工程实践
  • 如何使用pymysql和psycopg2执行SQL语句
  • 使用XML实现MyBatis的基础操作
  • pandas的用法
  • Github界面学习
  • C++ 函数重载
  • 手动更换SSL证书教程及注意事项
  • 【论文阅读】AUTOREGRESSIVE ACTION SEQUENCE LEARNING FOR ROBOTIC MANIPULATION