吊打面试官八股文(自己总结)

119 阅读18分钟

java基础****

1. Object有哪些方法

hashcode ,equals ,wait(), tostring

 

hashcode 和eques有什么关系?****

Java中规定,hashcode相同equals不一定相同,equals相同那么hashcode一定相同,如果违反这种规则hashMap和hashSet不能正常使用

 

wait 和sleep有什么区别 ****

(1) sleep()方法线程不会释放对象锁,wait()方法线程会释放对象锁

(2) sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用

(3) sleep是Thread线程类的静态方法,而wait是Object顶级类的普通方法

 

String、StringBuilder和StringBuffer的区别?****

(1) String类是不可变类,String对象一旦创建,其值是不能修改的

(2) StringBufferr类是可变类,用synchronized同步了,是线程安全的

(3) StringBuilder类是可变类,是非线程安全的

 

== equals的区别 ****

(1) ==比较的是对象地址

(2) equals比较的是对象内容

 

Arraylist和Linkedlist有什么区别 ****

Arraylist是基于数组的,在查询效率比较高,插入删除效率比较低

Linkedlist是基于链表的,插入删除效率比较高,查询效率比较低

对于添加和删除的时候,linkedlist优于arraylist,因为arraylist在做数据的添加和删除的时候需要有数据的位置的移动

 

List数据去重的几种有效方法?****

HashSet去重

JDK1.8的distinct去重

 

BIO、NIO、AIO的区别 ****

BIO:是同步阻塞式,是传统的IO,使用简单,但是没有办法处理并发

NIO:是同步非阻塞,是传统IO的升级,服务端和客户端通过channel通道,实现一个多路复用

AIO:是NIO的升级,也可以叫NIO2,是异步非阻塞,实现回调机制

 

 

重载和重写的区别?****

重载是:方法名相同参数列表不同

重写是:方法名和参数列表都相同,一般是子类重写父类的方法

 

jdk1.7和jdk1.8有什么区别?****

(1) jdk1.8新增了lambda表达式,还有stream流

(2) HashMap底层做了改进。jdk1.7是数组加链表,jdk1.8是数组加链表加红黑树

(3) jdk1.8的接口里面可以写默认的方法

 

对称加密和非对称加密有什么区别?****

对称加密:加密和解密必须使用同一个密钥,算法有DES,AES

非对称加密:加密和解密不是同一个密钥,一般有一个公钥,一个私钥,公钥加密,私钥解密,算法有RSA

 

HashMap底层原理?****

Jdk1.7是基于数据加链表实现,jdk1.8是数据加链表,长度大于8转红黑树

发生hash碰撞时,java 1.7 会在链表的头部插入,而java 1.8会在链表的尾部插入

 

HashMap 和 HashTable 有什么区别****

HashMap 是线程不安全的,HashTable 是线程安全的;

由于线程安全,所以 HashTable 的效率比不上 HashMap;

HashMap最多只允许一条记录的键为null,允许多条记录的值为null,而 HashTable不允许;

HashMap 默认初始化数组的大小为16,HashTable 为 11,前者扩容时,扩大两倍,后者扩大两倍 +1


为什么 ConcurrentHashMap 比 HashTable 效率要高?****

HashTable 使用一把锁(锁住整个链表结构)处理并发问题,多个线程竞争一把锁,容易阻塞;

ConcurrentHashMap

JDK 1.7 中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。锁粒度:基于 Segment/segmant/,包含多个 HashEntry。

JDK 1.8 中使用 CAS + synchronized + Node + 红黑树。锁粒度:Node(首结点)(实现 Map.Entry<K,V>)。锁粒度降低了。

 

HashMap 的 table 的容量如何确定?loadFactor 是什么?该容量如何变化?这种变化会带来什么问题****

table 数组大小是由 capacity 这个参数确定的,默认是16,也可以构造时传入,最大限制是1<<30;

loadFactor 是装载因子,主要目的是用来确认table 数组是否需要动态扩展,默认值是0.75,比如 table 数组大小为 16,装载因子为 0.75 时,threshold 就是12,当 table 的实际大小超过 12 时, table就需要动态扩容;

扩容时,调用 resize() 方法,将 table 长度变为原来的两倍(注意是 table 长度,而不是 threshold)

如果数据很大的情况下,扩展时将会带来性能的损失,在性能要求很高的地方,这种损失很可能很致命。

 

HashMap到jdk1.8为什么到8转红黑树?****

之所以选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。

而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。

 

HashMap负载因子为什么选择0.75?****

如果负载因子过高,比如1的情况下,虽然空间开销减少了,提高了空间利用率

如果负载因子过低,例如0.5虽然可以减少时间的成本,但是空间利率用低

主要是时间和空间做一个权衡

 

Hashmap 与 ConcurrentHashMap 区别?****

hashmap本质是数组+链表  根据key去获取hash值 然后计算出对应的下标,如果有多个key对应同一个下标,就用链表的形式存储

ConcurrentHashMap在hashmap的基础上 ConcurrentHashMap将数据分成了多个数据段(segment 默认是16)  主要是对segment去加锁

hashmap的键值允许null值,但是ConcurrentHashMap不允许

在jdk1.7 ConcurrentHashMap是由segment数组和hashentry数组结构组成

在jdk1.8 ConcurrentHashMap放弃了segment用node+cas+synchronize保证

ConcurrentHashMap是线程安全, hashmap线程不安全

 

HashMap的线程不安全主要体现在下面两个方面 ****

1、JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况

2、JDK1.8中,当并发执行put的时候会对数据造成覆盖的情况

 

HashMap 面试问题mp.weixin.qq.com/s/GxD7ZW-me…




 

单例模式理解?****

 

 

懒汉模式使用的是双重效验锁和 volatile 来保证线程安全的,从上述代码可以看出,无论是饿汉模式还是懒汉模式,它们的实现步骤都是一样的:

1.创建一个私有的构造方法,防止其他调用的地方直接 new 对象,这样创建出来的对象就不是单例对象了。

2.创建一个私有变量来保存单例对象。

3.提供一个公共的方法返回单例对象。

懒汉模式相比于饿汉模式来说,不会造成资源的浪费,但写法要复杂一些

 

Spring****

为什么用spring?****

Spring的核心功能IOC(控制反转,依赖注入),AOP(面向切面的编程)

IOC:我们在使用过程中不用关注于对象是怎么创建的,只用应用过去,sping自动帮我们完成注入,对象的创建,spring默认创建对象是单例,这样减少了频繁创建对象,让对象重复利用,所有的对象都是放在BeanFactory 工厂的

AOP:面向切面的编程,我们可以把一些公共的东西模块化,做成一个切面,在方法的运行过程中织入进去,好处是解耦,提高代码的重复利用率

 

项目哪些地方用到了AOP?****

我们经常使用的事务@Transactional的底层就是aop去实现的,还有日志,权限认证

 

AOP的底层怎么实现的?****

Aop底层其实就是通过动态代理去实现的,分为jdk的动态代理和cglib,jdk的动态动态代理必须要实现接口,cglib是以继承的方式子类重写父类的方法增强,spring默认会优先采用jdk的动态代理,如果没用实现接口再采用cglib代理

 

@Transactional事务什么时候不生效****

(1) 方法用private,final修饰的事务不生效(因为事务底层是动态代理,加了后没用办法去重写代理方法增强)

(2) 事务默认针对RuntimeException生效,如果内部抛出的不是RuntimeException不生效

(3) 如果在本类内部调用带有一个事务的方法事务不生效

 

(4) 对加有@Transactional事务的方法try catch了的不生效(因为异常已经被捕获,相当于没用异常了)

 

Spring Bean的作用域 范围?****

singleton(单例)

Prototype(原形范围与单例范围相反,为每一个bean请求提供一个实例)

Request(在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收)

Session(与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效)

global-session

 

Spring框架中的单例Beans是线程安全的么 ****

谈到beans线程安全那么就看bean是否有多种状态,如果始终只有一种状态 就是线程安全的,否则需要自己去保证线程的安全,可以采用将singleton变为prototype

 

 

S pringMVC原理, 执行 流程 ****

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

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

(3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。

(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。

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

(6)ViewResolver会根据逻辑View查找实际的View。

(7)DispaterServlet把返回的Model传给View。

(8)通过View返回给请求者(浏览器)

 

S pring事务传播特性?****

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

PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。

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

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

PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与

PROPAGATION_REQUIRED 类似的操作。

 

Spring是如何解决循环依赖问题的?****

Springboot2.6(不会存在循环依赖)直接报错****

循环依赖就是:A依赖B ,B依赖A ,采用三级缓存

先对A进行实例化,首先创建一个A的对象,将这个对应的lambda表达式存入 三级缓冲中去,由于A依赖于B所以要填充b的属性,那么这个时候会去一级缓存中去寻找B的对象,没有找到,那么就进行创建(和A创建一模一样),先去创建B对象放入三级缓存中去然后填充A的对象属性,这时候要先去一级缓存中去寻找A如果没有找到在去2级缓冲中去找,如果二级还没有再去三级缓冲中去找(因为此时a的对象存放在了3级缓冲中,所以说在三级缓冲中查询到了,因为三级缓冲是一个lambda表达式,所以执行这个表达式,然后将返回的对象放入 二级缓冲中去,进行A的填充),A进行填充之后,去执行B的对应的表达式返回对象,并将B的对象放入一级缓存,因为此时B已经完全创建好,但是A还没有创建完成,此时吧B从一级缓冲中取出来,填充到A中去,此时就A业创建好了,就把二级缓冲中的A放入一级缓冲就完成了

 

Spring Boot 的底层原理 ****

Spring Boot 底层实际上主要的有三个注解:Configuration ,EnableAutoConfiguation

ComponentScan,它会读取META-INF下的spring.factories文件信息,通过反射的机制把bean纳入到spring的管理

 

Spring有哪些自动装配的方式?****

byName:根据名称进行自动匹配

byType:根据类型进行自动匹配

constructor:构造函数注入

autodetect:根据Bean的自省机制决定采用byType还是constructor进行自动装配,如果Bean提供了默认的构造函数,则采用byType,否则采用constructor

 

@ A utowired和@ R esource注解的区别是什么 ****

Autowired是通过bean类型注入的,如果一个接口有多个实现,那么采用Qualifier配合使用

,Resource是通过bean名称注入的

 

Spring中BeanFactory和FactoryBean的区别 ****

BeanFactory:它是一个bean工厂,spring把对象创建好后都会放入这个工厂,底层存储对象其实就是一个大的ConcurrentHashMap存储的对象实例

FactoryBean:它是一个特殊的bean,实现了这个接口,可以通过getObject获取到自定义的bean

 

Spring的Bean的生命周期****

通过refresh方法里面完成bean的创建,先会去创建BeanFactory,然后扫描包转换成beanDefinition,通过后置处理器完成,实例化,属性赋值,初始化,销毁,每个阶段可以实现不同的接口,如:初始化的时候实现InitalizingBean接口,调用afterPropertiesSet() 方法,销毁的时候实现DisposableBean接口,调用destory() 方法****

 

Spring Cloud 组件?****

Eureka 注册中心

ribbon:负载均衡策略

hystrix:熔断器

zuul:网关

config:配置中心

feign:服务调用

 

springboot和springcloud区别?****

springboot主要快速开发整合包,他主要是方便单个的微服务,而springcloud是治理框架,将每个单个的微服务结合起来,并且为他们提供配置,服务发现,路由等集成服务

 

springcloud负载均衡策略?****

随机,轮询,最小使用数,权重

 

网关的作用是什么?****

统一的请求路由,权限认证,安全校验,限流

 

Spring Cloud断路器的作用****

一个服务在调用另一个服务的时候由于网络的原因出现了问题,调用者会一直等待被调用的者的响应,当更多的请求要调用会出现更多的请求等待,所以断路器完全打开状态:一段时间内,没有得到被调用者的响应,多次检测没有恢复的迹象,这个时候断路器完全打开;断路器半开状态:有恢复的迹象,将部分的请求发给服务;如果正常调用没有出现等待请求那么处于断路器关闭。

 

什么是Spring Cloud Config****

分布式系统中,服务数量比较多,要想实现一个统一管理,实时更新,需要一个配置中心组件,在springcloud中用的是springcloudconfig去实现的,他是可以放在内存中也可以放在git仓库中的,主要有两个角色一个是connfig server和config client 使用就是1、加pom依赖 2、相关的配置文件,3、启动类加enableconfigserver

 

什么是Spring Cloud Gateway****

springcloud的gateway是网关,取代了zuul官网,在微服务中有重要的作用,常见的功能有路由,权限验证,限流控制具体是route去处理的,filters是各种过滤器。

 

什么是springcloud的熔断机制****

熔断器是起来保护的作用,和保险丝一样,到了某个领结点就会进行熔断保险丝保护电路,springcloud中的熔断机制也是如此,如果某一服务发生了崩溃那么要进行熔断机制防止整个系统都崩溃

 

Eureka和ZooKeeper区别****

  • eureka是基于AP设计的,zookeeper是基于CP设计的

  c:一致性    a:可用性   p:分区容错性

  • ZooKeeper有Leader和Follower角色,Eureka各个**节点平等 **

  • eureka可以很好的解决出现故障导致的部分结点失去联系,zookeeper如果出现故障整个服务器都会瘫痪

  • ZooKeeper只是一个进程,Eureka本质上是一个工程

  • zookeeper采用半数存活原则,eureka采用自我保护机制解决分区问题

 

Eureka保证AP****

eureka保证了可用性,eureka各个节点都是平等的,几个结点挂掉是不会影响正常工作的,如果eureka的客户端注册失败的话,会自动切换其他节点,只要还有一台eureka还在的话就能保证可用性,eureka还有 自我保护机制,如果超过了15分钟有85%的结点都没有心跳了,那么就认为客户端出现了网络故障

 

Zookeeper保证CP****

zookeeper保证了一致性,但是我们在注册信息的时候是没有办法容忍服务器宕机的。也就是说服务的可用性要高于一致性。zookeeper会出现一种情况当主节点因为网络原因与其他的结点失去联系的时候,剩余的结点会进行一个leader选举,但是选举的过程中是时间很长的达到30—120秒,在选举的过程中zookeeper是没有办法使用的,在云部署的情况下zookeeper主结点丢失这种情况的概率是很大的,虽然可以恢复但是时间长。

 

Eureka的底层原理****

eureka主要是通过心跳检测去判断的,有一个发送者和客户端,发送者会每隔30秒发送一个心跳到eureka上去,服务端会把eureka上的客户端发送的数据进行一个接受并调用的过程,如果说生产者没有发送心跳到注册中心上,那么就直接剔除掉,将所有的接口都剔除掉,如果说中途eureka发生了宕机,那么也是可以进行一个调用的,因为将原来的数据放到了一个缓存中 去,并且eureka还有自我保护机制,如果说在15分钟内检测到有85%的服务都宕机了那么这个时候就会认为是一个网络的问题导致的

 

Feign 的底层原理****

底层基于动态代理发送http请求,然后通过负载均衡算法调到对应的服务机器上

 

 hystrix实现原理

  • 隔离(线程隔离和信号量隔离)

  • 熔断

  • 降级

  • 缓存  将请求放到缓存中

隔离:线程隔离:主要是交给线程去做处理的,每个请求都要交给线程去做处理,是可以处理突发情况的(因为如果没有及时处理的数据是要放到队列中去,一点一点执行的),是异步的

信号量隔离:主要是采用原子计时器(信号量)去对请求做处理的,如果说发送一个请求,当前线程数已经大于最大线程数,那么就会采用一个拒绝,不接受请求,如果没有就计数器+1,当返回结果后在进行计数器-1,这个是立即返回给用户的,灭有办法处理突发情况,是同步的

熔断:如果说长时间没有返回给客户请求响应,那么会进行熔断开启状态,这个时候会拒绝所有的请求,过段时间会进入熔断半开状态 要接收一部分请求做出响应。

降级:忍痛割爱  要把一些服务器先停掉,等到执行完之后再将其打开

 

Mybatis****

Mybatis的一级、二级缓存 ****

 一级缓存存储的作用域是session,当session flush或者close之后,session中的缓存会失效,此时一级缓存就开启

 二级缓存和一级缓存机制相同,hashmap存储,作用域为mapper,二级缓存一般不用,因为他不好控制缓存的刷新,我们一般是用的是redis

 

MyBatis 的接口mapper可以重载么 ****

不可以,mybatis查找mapper内的方法是靠方法名,和参数无关。所以,对于mapper接口,Mybatis禁止方法重载

 

Hibernate 和 MyBatis 的区别 ****

mybatis是一个小巧方便,半自动化持久层框架,学习起来比较容易

hibernate是一个全自动化持久层框架,做sql优化难,学习也比较困难

 

#{}和${}的区别是什么?****

1)#{}是预编译处理,效率要高点,${}是字符串替换。

2)#{} 可以有效的防止SQL注入,提高系统安全性。${}有sql注入的风险

 

多线程****

实现线程的方法 有哪些?****

1、创建thread方法

2、实现runable接口

3、实现callable接口(可以获取线程的返回结果)

 

runable和callable的区别 ****

1、runable是实现run(),callable是实现call()

2、runable无返回值,callable有返回值

3、runable不抛异常,callable抛异常

 

如何让 线程的有序执行 ****

采用join

countdownlatch类,首先有countdown()和await(),当调用countdown()时,计数器不为0的时候就会执行await(),如果技术器为0则会执行下一个任务

采用 Executors的单线程池创建方式 Executors.newSingleThreadExecutor();

 

如何获取线程的返回结果?****

实现callable接口,返回Futrue,通过get方法可以获取结果

 

线程数过多会造成什么情况 ****

1、消耗资源

2、占用cpu

3、降低了稳定性

 

解决线程安全的 方式有哪些?****

1、加synchronized锁

2、加lock锁  此时采用的是Reentratlock锁

3、用ThreadLocal

4、atomic

5、提供了一些线程安全的类比如ConcurrentHashmap

 

线程池的底层原理****

创建线程池的时候,开始一个线程也没有,随着任务的提交创建线程,当前线程如果小于corePoolSize核心线程数,创建线程, 否则(当前线程大于或等于核心线程数)放入LinkedBlockingQueue队列,如果队列没有满,继续放入,如果队列满了, 判断是否小于最大线程数,如果小于继续创建线程,否则拒绝策略(默认拒绝策略抛异常)

 

 拒绝策略 有哪些?

1、使用线程解决

2、直接拒绝不抛异常

3、直接拒绝抛出异常(默认)

4、将最早的线程舍弃,将最新线程添加

 

创建线程池的方法 有哪些?****

1、单一线程池(始终只有一个线程)

2、定时线程池(在固定时间段内使用线程)

3、定长线程池(自定义设置线程大小  一般使用)

4、可缓存线程池(无限制的线程大小)


synchroized的底层原理****

synchroized是通过监视器monitor来完成的,如果monitor被占用时会处于死锁的状态,线程需要 执行monitorenter指令去尝试获取monitor的所有权,如果monitor的进入数为0,那么进入现场进入monitor,然后将进入数设置为1,此线程为monitor的所有者,如果线程已经有monitor需要重新进入,monitor为+1,如果已经占用了monitor,则该线程进入等待的状态,直到monitor的进入数为0时,再去重新获取所有权。

 

ReentratLock的底层原理****

ReentratLock主要是通过AQS+CAS来实现的

先通过CAS去获取锁,如果锁获取到了就将线程放入AQS队列中去,如果锁释放了,就会释放AQS队列中的首个线程,在通过CAS去尝试获取锁。

底层是基于AQS实现的,每个都有自己的内部类

lock的存储结构:一个int类型(用来存放锁的状态变更)  一个是双向链表(用于存储等待中的线程)

lock获取锁的过程:本质上是通过CAS来修改状态,如果获取到锁之后就更改锁的状态,没有获取到锁就放到等待链表中进行等待。

lock释放锁的过程:修改状态,调整链表

 

ThreadLocal原理 ****

当多个线程操作同一变量且互不干扰的情况下,可以使用threadlocal来解决,它会每个线程都创建一个副本,线程内部都会创建一个变量。在底层有一个巨大的map来存放线程,如果在使用完线程之后没有释放,那么会造成一个内存溢出的情况

 

ThreadLoca l 为什么会造成内存溢出 ****

因为底层是巨大的map,而map的key是弱引用,value是强引用,而进行回收的时候可以回收掉key,value是回收不掉的,解决方法可以直接使用自带的remove

 

volatile 有什么作用?****

1. 防止指令重排序,2.保证各个线程之间的可见性

 

AQS底层原理****

AQS维护volatile下的共享资源state,如果获取到锁了那么就去改变state的状态,如果没有获取到那么就进入一个阻塞的状态,放入阻塞队列中去

原理就是:AQS实际是通过CLH队列操作的,CLH队列是一个虚拟的双向队列,通过CAS去获取,获取到修改state状态,获取不到就放入阻塞队列中去

 

CAS原理****

CAS(compare and swap)是比较交换,是一种乐观锁, VAB V是内存预期要修改的变量,A是要修改的预期值,B是新值,如果用户要去改变内存中的值,那么A要去和V去做对比,如果相等了那么B的值就是A的值。

会出现一个ABA的问题,如果有一个人吧值从原来的1改成了2,后又吧2改成了1,那么别人认为这个操作是无用的,其实对于CAS来说是用的,就会出现ABA的问题,解决方式 每修改一次就给版本号上+标识

 

synchronized 和 lock 有什么区别?****

synchronized 是java语言的关键字,lock是一个类,通过这个类可以实现同步访问

synchronized我们一般是锁代码块,lock一般是我们需要创建一把锁,然后执行任务。最后需要手动释放锁

synchronized的底层是基于JVM指令完成,它会在代码块中加入monitorenter和monitorexit执行,而lock是通过代码实现的,底层基于AQS实现的

synchronized 获得锁和释放锁的方式都在块结构中,是 自动释放锁,lock需要手动释放,并且必须在finally中,否则会造成死锁

 

Mysql数据库****

事务的特性****

原子性(支持回滚,底层根据undo log,事务如果中途出现错误,会进行回滚,回滚到开始前的状态)

一致性 (事务开始前和结束后,数据库的完整性约束没有破坏)

隔离性(只允许一个事务请求同一数据,不同事务之间彼此互不干扰,底层MVCC多版本并发控制间隙锁--读写锁)

持久性 (不支持回滚,底层根据 redo log)

事务的并发问题(脏读、幻读、不可重复读)

脏读:事务A已经更新了一份数据,在这个过程中,事务B去执行了同一份数据,但是由于某些原因,被修改的数据rollback了,然后一个事务所读取的数据就不一样了(没有提交,进行了回滚)

不可重复读(一个事务中不允许多次读取数据):事务a多次读取同一个数据,事务B在事务A多次读取的过程中对数据做了更改,导致最终事务A读的数据不一致(提交成功了)

幻读:管理员A已经把学生的信息全部统计完毕了,在统计过程中,管理员B添加了一条数据,但是管理员A不知道,等管理员A执行完之后,发现有一条数据没有被统计进来,这个时候就发生了幻读(提交成功了)

不可重复读和幻读的区别:不可重复读是侧重于修改,幻读侧重于新增或删除,解决不可重复读就锁住满足条件的,解决幻读是需要锁全表

 

MySQL事务隔离级别   

读未提交(脏读、不可重复读、幻读)事务A读取到事务B未提交的数据

读已提交(不可重复读、幻读)事务A去修改数据但是不提交,事务B查询数据查询的还是原来的数据,事务A提交事务,事务B再次读取数据,读到的数据和第一次读取 的数据是不一致的

可重复读(幻读  默认的)事务A在执行的过程中不会读取到其他提交的事务,只有当前事务结束之后才可以读取到

串行化  排队依次去执行

 

binlog、redo log和undo log****

bin log:读写分离,每次操作都会记录到binlog中去,可以直接在binlog中去查询丢失的数据

redo log:保证持久性

undo log:保证原子性

 

MySQL事务实现原理****

主要是用undo log和redo log来实现的。

undo log用来恢复数据,保证了原子性

redo log用来回滚数据,保证了持久化

事务的隔离性是通过(读写锁(间歇锁)+MVCC)来实现的

MVCC的原理:将历史信息在快照内存中,其他事务发生删除修改操作,都是对他不可见的

间隙锁的原理:读取数据的该行与上一行和下一行有一个间隙的锁定,保证在此范围内读取到的数据是一致的

 

left join、right join和inner join区别 ****

left join(左连接) 返回包括左表中的所有记录和右表中联结字段相等的记录

right join(右连接) 返回包括右表中的所有记录和左表中联结字段相等的记录

inner join(内连接) 只返回两个表中联结字段相等的行

 

说一下 mysql 的行锁和表锁****

mysiam支持表锁,innodb支持行锁

表级锁:开销小,加锁快,不会出现死锁。锁定力度大,并发量最低,发生锁冲突的概率最高

行级锁:开销大,加锁慢,会出现死锁。锁定力度小,并发度最高,发生锁冲突的概率小

 

索引有哪些****

主键索引,唯一索引,普通索引,组合索引

 

 数据结构

 数组:查询效率比较高,添加,删除效率比较低

 链表:删除添加操作效率比较高,查询效率比较低

 

hash:做的是数据的比较,不能那个做区间查询  等值查

 

二叉树:

二叉树是每个节点最多有两个子节点的树。

二叉树的叶子节点有0个字节点,二叉树的根节点或者内部节点有一个或者两个字节点。

 

二叉树在极端的情况下会出现单链表的情况,所以说查询的速度还是慢

 

 

红黑树:根节点永远是黑色的

 

红黑树与AVL树的比较:

AVL是严格的平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;

红黑树是用非严格的平衡来换取增删节点时候旋转次数的降低开销;

所以简单说,如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL树,

如果搜索,插入删除次数几乎差不多,应选择红黑树

 

 B树(b-)一个节点可以存多个数据块,它的高度降低了很多,顶多达到3层高度,高度变小,IO次数变少,性能有提升

 

B+树(在B树上做了改造)

 

 

Innodb和Myisam 存储引擎区别****

1.索引区别,Innodb聚集索引(数据文件和索引文件放在一起的)

 

Innodb二级索引(非主键的索引)(建立普通索引和主键的关系,先通过普通索引找主键,然后回表找数据)

 

Myisam非聚集索引(数据文件和索引文件分开的)

 

Myisam二级索引(跟主键没有关系,都是维护独立索引树,然后叶子节点存地址,然后通过地址找数据)

 

2.Myisam不支持事务的,Innodb支持事务

3.Myisam表级锁,Innodb是行级锁(必须使用到索引列才能去使用)

4.Myisam崩溃后无法安全恢复 ,Innodb具有自动崩溃恢复功能

 

uuid为什么不适合做主键 ****

32位,空间占用率大,无序,Innodb索引文件会很大

因为我们数据库通常Innodb用存储引擎,它的索引结构是根据主键去组织的,那么占用空间会很大,并且我们建了二级索引, 二级索引它会和主键索引建立关系,先去二级索引查询主键,然后通过主键再去查询数据块(回表 )

因为无序,没法充分利用B+树特性,非叶子节点没法充分使用操作系统系统空间局部性原理,导致性能低

 

1万数据未支付,已支付,支付失败状态3种,适合建索引么?****

不适合,建索引规则必须要保证离散型最强的一列,不然重复比较多

 

sql优化****

-- 全表扫描 EXPLAIN select * from tb_test;

 

EXPLAIN select * FROM tb_test where mobile='15921484451';

EXPLAIN select * FROM tb_test where mobile=15921484451;

数据库有隐式类型转换,索引类型不一样,不走索引

EXPLAIN select * FROM tb_test where mobile like '%21%';

EXPLAIN select * FROM tb_test where mobile like '134%';

like查询前模糊匹配不走索引,like后模糊匹配可能会走索引

组合索引user_name与mobile

EXPLAIN select * FROM tb_test where user_name='大宝' and mobile='15921484451';

EXPLAIN select * FROM tb_test where user_name='大宝' ;

EXPLAIN select * FROM tb_test where mobile='15921484451';


S ql优化不走索引的情况?****

1.数据库有隐式类型转换,索引类型不一样,不走索引

2.like查询前模糊匹配不走索引,后模糊匹配可能会走索引

3.NULL列不走索引

4.not,not in ,!=,or不走索引

5.在建的索列上有函数操作,都不走索引,比如EXPLAIN select * FROM tb_test where substr(mobile,1,3)='159'

6.组合索引必须满足最左匹配原则,比如:abc,a,ab,abc,ac

 

索引覆盖****

建了user_name和mobile对应的组合索引

select * from tb_user_test where user_name='大宝' and mobile='15921484451';

索引覆盖的sql

select user_name,mobile from tb_user_test where user_name='大宝' and mobile='15921484451';

如果查询的列只包含我们建的索引列,此时不需要回表操作,叫索引覆盖

 

回表****

Innodb是聚集索引,它的索引结构是根据主键去组织的,二级索引它会和主键索引建立关系,先去二级索引查询主键,然后通过主键再去查询数据块的这个过程

 

 

mysql深度分页问题****

在查询的时候,如果查询的数据量较大的情况下,用limit查询的时候,查询的效率会慢很多,如:

select * from tb_user limit 3000000,10;

优化方案:

1.如果表有自增id,最好加上一个条件,id>,取上一页的id加上id>上一页id,可以提高效率

2.如果没有自增,可以用延迟关联处理

因为会有一个回表的操作,所以说可以先查询出他们的id,查询出来之后然后在去查询数据,通过inner join去连接

 

 

 

LIux命令****

cd 切换目录

pwd 查看当前路径

mkdir 创建目录

mv 修改目录名称

ps -ef|grep java 查看java进程

kill -9 进程号       杀掉进程

tail -400f 文件名称 查看最后400行

tail -50f ./zookeeper.out |grep '/usr/local/src/java/'  搜索zookeeper.out日志包含的/usr/local/src/java/

vi 编辑文本

cat 查看文件

find文件

chmod 777 ./start.sh 给star.sh赋所有权限

top 查看服务器资源,比如内存,cpu使用情况

 

消息队列****

 RabbitMQ的死信队列和延时队列

消息被拒,requeue设置 为 false

消息过期,队列达到最大程度 这时候会存放到死信队列中去。

设置消息过期时间:采用队列中的x-message-ttl 参数去设置,单位是毫秒

 

关于ActiveMQ、RocketMQ、RabbitMQ、Kafka一些总结和区别

 

为什么使用消息队列?****

异步(发送手机验证码),

解耦(借款系统和风控系统),

削峰(秒杀,购买商品,放入消息队列排队)

 

使用消息队列有什么缺点?****

   (1).多了一个MQ服务,可能会出现单点故障,导致系统不可用(集群)

   (2).消息队列重复消费幂等问题(一个操作无论执行多少次,都会自己业务没有任何影响)

   (3).消息队列丢失问题

   (4).消息队列顺序问题

 

消息队列如何选型?****

ActiveMQ集群模式很复杂,它的集群模式是分片的,每个机器上只存了部分数据,万一服务挂了,数据就丢了,最高并发10万以内,社区活跃度比较低,对开发的系统安全有影响

RabbitMQ集群模式,可以保证服务挂了,不丢消息,最高并发10万以内,社区活跃度比较高

RocketMQ集群模式,可以保证服务挂了,不丢消息,最高并发10万以上,社区活跃度比较高

Kafka(集群模式,打点统计,日志统计),高并发100万

 

如何保证消息队列是高可用的?****

集群

 

如何保证消息不被重复消费?****

根据业务场景做幂等(1.比如唯一的信息我们可以建唯一索引,2.监听里面做校验幂等操作,3.每个消息分配一个唯一的id,uuid,消费完redis存一下,然后后面每次都进来校验下)

 

如何保证消息的顺序性?****

 1.保证一个生产者对应着一个消费者

 2.监听器里面把消息消费放到JVM队列(LInkedBlockQueue),然后再消费本地队列

 

如何保证消息不 丢失?****

MQ发送消息到消费的整个过程分为3个阶段,生产者-MQ-消费者,

1.保证MQ服务的高可用,做集群,持久化

2.消费者在消费MQ里面的内容的时候,如果MQ保存了,MQ会有重试机制,重试还失败后会进入死信队列,然后消息丢失,这个过程可以采用ACK确认机制,手动签收消息,如果消费失败,让消息还是在MQ里面

如果消息在生产者发送到MQ的过程中,因为通讯网络问题,也可能会丢失,这个时候需要做消息持久化,我们会把消息存入到数据库,如果消费成功,把消息删除掉,当然删除消息可能会失败,后面我们可以通过定时任务轮询做补偿,然后继续忘消息队列里面发消息,那么这样可能会出现一个新问题,消息的重复消费,需要考虑幂等问题,我们消息持久化的时候都会分配一个msgId唯一标识,后面消费完了存入redis,消费之前校验一下就可以了

 

 

Redis****

为什么使用redis ****

1.支持高可用,3.0集群

2.丰富的数据类型

3.完全内存操作,速度快,支持持久化

4.存储数据大,单个key和value可以存储到1G

 

Redis 的持久化 方式 ****

redis持久化方式有RDB和AOF  ,RDB是方式是每过几秒保存的是redis数据的快照,但是可能会丢数据,AOF  保存的是所有在redis执行的命令,它会追加到一个文件里面,丢数据可能性小,但会导致文件很大,假如redis宕机了,恢复的时候会很慢,我们一般使用RDB,因为我们对redis的定位就是缓存服务器,很重要的数据我们不会存redis,比如与钱有关系。

 

redis有哪几种数据类型****

string ,hash,list,set,zset

 

redis分布式锁底层原理 ****

redis分布式锁其实就是往redis设置一个key和value同时设置一个有效时间,并且redis是单线程的,不会并发操作,(执行任务完成后),再把redis的key删除掉(解锁),但是在使用的过程中可能有2个问题,使用过程中我们必须保证设置key和value和设置时间保证它的原子性(LUA),另外还是锁超时问题,比如:上锁2秒钟,但是任务执行超过2秒,我们一般用redission框架,它底层是lua脚本实现,可以保证设置值和时间的原子性,另外还有

看门狗的机制,watch dog,我们上锁3秒,但是任务执行5秒,它会自动加时间

 

 

为什么使用分布式锁 ****

因为我们的系统是分布式的,synchronized和lock锁只能是JVM级别的,这个时候需要分布式锁,它实现的思路: redis分布式锁其实就是往redis设置一个key和value同时设置一个有效时间,并且redis是单线程的,不会并发操作,(执行任务完成后),再把redis的key删除掉(解锁),这个过程需要注意的点是,必须保证设置key和value和设置时间保证它的原子性,不然可能出现死锁,另外一方面就是锁任务自动超时问题,所有我们一般用的redisson框架,它完美的帮我们封装了分布式锁,底层是基于LUA脚本实现保证原子性,同时redission

还有watch dog,比如我们上锁3秒,但是任务执行5秒,它会自动加时间

 

    1.分布式锁{

redis查商品,3

校验随机码,

检验秒杀时间段,

商品id和随机码是不是一样,

检验每人限购数量,

检验库存,3

锁定库存(减库存)

下单消息队列

}

 

 

redis集群模式 ****

1.主从复制(缺点,主挂掉后,需要人工切换干预,不能保证服务的高可用)

2.哨兵模式(一主多从,主挂掉后,会自动选举主再提供服务,缺点:1.极端情况下网络不是很好的情况,选举需要花时间,可能服务不可用。2. 资源浪费,Redis 数据节点中 slave 节点作为备份节点 )

3.集群模式( Redis Cluster 是 3.0 版后推出的 Redis 分布式集群解决方案 )

  1. 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。

  2. 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除。

  3. 高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover

 

redis哨兵(Sentinel)模式 ****

redis节点之间通过心跳检测,从服务器(slave)每过一段时间向主服务器(master)发送一个ping命令,主服务器的响应,如果主服务器挂了,所有从服务器通信,有一种算法选举为主,再提供服务

 

 redis cluster原理

所有的redis结点都是彼此互联的

客户端连接集群的时候不需要关心分片的计算逻辑,客户端直接将key交给redis中的结点,最终由内部判断key值的正确存储位置

redis集群是吧所有的主节点都交给对应的卡槽去处理【0-16383】,由主节点去维护一批数据。数据主要是被哪个结点维护是判断对应key的取模运算,如果要迁移某个key值,必须将对应的slot(槽)一并迁移

 

redis脑裂问题 ****

redis集群由于网络的原因可能会出现脑裂的问题,脑裂就是因为主服务器、从服务器和哨兵不在同一个网络中,导致哨兵没有及时的检测到主服务的心跳,在这个时候会在从服务器中去选举一个新的主服务器,这样就有两个主服务器了就像大脑分裂一样,但是这样会导致客户依旧 在旧的主服务器中去写东西,而新的主服务器中没有东西。当网络恢复后,哨兵会把旧的主服务器变成从服务器,这个 时候在去同步数据,可能会造成一个数据的丢失

解决方法:

redis中需要加入两个配置

​               (旧版本)

      min-slaves-to-write 3    最少有3个从服务器

      min-slaves-max-lag 10    数据复制和同步的延迟不超过10秒

 

    (新版本)

      min-replicas-to-write 3

      min-replicas-max-lag 10

如果加了这两个配置的话,原来的主服务器当客户端再次进行写操作的时候会拒绝接受,此时就发送到新的主服务器中去了

 

什么是缓存和数据库双写不一致?怎么解决?****

数据的信息和缓存由于并发或者其中一个失败导致不一致

解决方案:我们一般是先修改数据库,再删除缓存,因为我们对redis的定位是缓存,redis可能会丢数据,首先保证我们的数据库必须更新,如果redis删除失败,我们采用补偿策略,比如错误了或失败了,把信息放MQ,做消费补偿

 

雪崩?****

 

缓存雪崩是指:由于缓存中的数据一下子全部都在同一时间过期了,所以发送过来的全部请求都去请求数据库,导致数据库难以承受而宕机。

解决方法:

  • 可以保证 redis高可用,建集群;

  • 设置不同的过期时间,防止全部在同一时间过期

 

穿透?****

 

 

 

 

 

 

缓存穿透是指:缓存和数据库中都没有数据,如果有人恶意访问的会先去缓存查询,此时缓存中无数据,后在去数据库中去查询,数据库中也没有,这个时候就会导致数据库宕机

解决方法:

可以在缓存中设置一个null值,让恶意的请求不会直接击垮数据库,每次访问的时候都去访问此缓存。

可以设计一个过滤器,常用的就是布隆过滤器(可以缓解,为什么是缓解,因为使用过滤器还会造成误判的情况)

击穿?****

缓存击穿是指:一条数据在查询的时候突然过期了,那么就所有的请求都打在数据库上

解决方法:

加锁,只让一个人去访问数据库并且将访问的数据存入缓存中,供其他的请求来访问

 

JVM****

jvm分区

 

Heap (堆区):主要存储new出来的对象实例,Java堆中细分为:新生代和老年代,一个新生代分为1个Eden区和2个Survivor区,说明:绝大部分对象在Eden区生成,当Eden区装填满的时候,会触发Young Garbage Collection,即YGC。垃圾回收的时候,在Eden区实现清除策略,没有被引用的对象则直接回收。依然存活的对象会被移送到Survivor区。Survivor区分为so和s1两块内存空间。每次YGC的时候,它们将存活的对象复制到未使用的那块空间,然后将当前正在使用的空间完全清除,交换两块空间的使用状态。如果YGC要移送的对象大于Survivor区容量的上限,则直接移交给老年代

 

元空间区:jdk1.7的方法区移到了元空间,比如类元信息、字段、静态属性、方法、常量等都移动到元空间区 元空间并不在虚拟机中,而是使用本地内存

 

栈:栈里面存的都是一些局部变量,比如8大基本数量类型,还有线程运行,方法运行都在栈里面,另外创建对象的时候的引用也是存在栈里面的

 

程序计数器:是一块较小的内存空间。是线程私有的。它可以看作是当前线程所执行的字节码的行号指示器

 


类加载****

类加载器有这几个:

启动类加载器:jvm启动的时候,会优先加载<JAVA_HOME>\lib这个目录的核心类库。

扩展类加载器:负责加载<JAVA_HOME>\lib\ext这个目录的类。

应用程序类加载器:负责加载我们写的代码。

自定义类加载器:根据我们的需要,加载特定的类。

下图展示了类加载器直接的层次关系,成为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。

 

它的工作过程是这样的: 

1. 应用程序类加载器收到了类的加载请求,先问扩展类加载器是否可以加载。

2. 扩展类加载器也不会直接去加载,他也是向上级启动类加载器询问是否可以加载。

3. 启动类加载器在自己负责的目录搜索了一下,发现自己找不到这个类,就说不行,你自己加载吧。

4. 应用程扩展类加载器在自己负责的目录搜索了一下,发现自己找不到这个类,就说不行,你自己加载吧。

5. 序类加载器在自己负责的目录搜索了一下,找到了这个类,把Hello类加载进来。

 

双亲委派模型一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

 

双亲委派 存在得意义是什么?****

保证系统的安全

 

垃圾回收器有哪些?****

串行垃圾回收器(Serial) :它为单线程环境设计并且只使用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境。
并行垃圾回收器(Parallel) :多个垃圾回收线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理等弱交互场景。jdk8默认的是使用的Parallel并行回收器
并发垃圾回收器(CMS) :用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程。互联网公司多用它,适用于对响应时间有要求的场景。

 

垃圾回收算法?****

标记清除 ****

复制算法:****

标记整理:****

分代收集算法:****

 

JVM调优的几种场景****

CPU占用过高****

CPU过高的原因一般是,死循环,递归,计算量大,线程数过多,怎么确定CPU飙升的问题如下:

用top命令查看cpu占用情况

 

用top -Hp命令查看线程的情况

 

可以看到是线程id为7287这个线程一直在占用cpu,把线程号转换为16进制 用jstack工具查看线程栈情况****

 

 

 

内存溢出解决****

程序发生内存泄漏后,进程的可用内存会慢慢变少,最后的结果就是抛出OOM错误。发生OOM错误后可能会想到是内存不够大,于是把-Xmx参数调大,然后重启应用。这么做的结果就是,过了一段时间后,OOM依然会出现。最后无法再调大最大堆内存了,结果就是只能每隔一段时间重启一下应用

用jstat分析gc活动情况****

jstat是一个统计java进程内存使用情况和gc活动的工具,参数可以有很多,可以通过jstat -help查看所有参数以及含义****

****

用jmap工具dump出内存快照****

 

链接:mp.weixin.qq.com/s?__biz=Mzk…