栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 前沿技术 > 云计算 > 云平台

java面试笔记(社招)

云平台 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java面试笔记(社招)

近期面试各家互联网大厂、金融基金、证券、车企等公司,总结了一下相关面试笔记,内容概括性的,应对面试,深度和广度需要各位自行研究,面试时候如遇到此类问题,核心可参考以下解答,如果铺开和阐述看个人沟通能力!

JVM

java内存模型和系统内存模型

系统内存模型包括:代码段、方法区、堆

java内存模型即使java进程模型,进程中包括共享区域和线程独占区域

共享区域中包括:java堆(存放实例化的对象实例,垃圾回收操作区域)、方法区(存放数据加载的相关信息,包括常量、静态参数、入口)

线程独占区域包括:java虚拟机栈(类型栈帧(每个方法的执行都会生成一个栈帧),局部变量表-编译的时候已经分配好内存空间,不会改变)、本地方法区、程序计数器

垃圾回收:采用引用计数法(对象中添加引用计数器,对对象的引用进行计数)和可达性分析法对垃圾进行标记;

回收策略:标记清除、复制、标记整理、分代收集;回收器:serial、Parnew、G1

垃圾回收器 

Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;

新生代收集器:Serial(复制)、ParNew、Parallel Scavenge(复制);

老年代收集器:Serial Old(标记-整理)、Parallel Old(标记-整理)、CMS(标记-清除);

整堆收集器:G1(整体:标记-整理;局部:复制)

GC Root

堆和方法是进程存储,用于线程共享,jvm虚拟栈、程序计数器、本地方法栈是线程独有,栈中本地变量,方法区中静态变量,本地方法栈变量,正在运行的线程可以作为GC Root(GC root是只有被引用而未引用)

gc时STW(stop the work)

不暂停应用的时候,存在垃圾回收状态不停变化的情况,到时垃圾回收不彻底

线程

线程:Runnable、Thread、Callable

run()和start()调用最大区别是线程数不一样

1---new Thread().start();

2---New Thread(new Runnable()).start();

3---FatureTask f=new FatureTask(new Callable());

New Thread(new FatureTask()).start();

f.get()

线程池

线程池有点:1避免线程池创建和销毁;2避免线程间资源抢夺导致阻塞;3提供线程定时执行,间隔执行功能

线程池中submit和execute区别:1submit有返回值,execute无返回值;2

Executor执行接口(execute);ExecutorService提交接口(submit,实现了返回值);AbstractExecutorService执行和提交整合合并;ThreadPoolExecutor(普通线程池类)

线程类型

newSingleThreadExecutor 

reentranctlock和synchronized

1.底层实现

Synchronized是JVM层面的锁,是Java关键字,通过monitor对象来完成。

ReentranLock是API层面的锁底层使用AQS。

2.释放锁

synchronized不需要用户手动释放锁。

ReentranLock可以手动释放锁,一般通过lock和unlock方法配合try/finally语句。

3.中断

synchronized是不可中断类型的锁

Reentranlock则可以中断,可通过tryLock(long timeout,TimeUnit unit)设置超时方法

4.公平锁

synchronized为非公平锁

ReetranLock默非公平锁,可以new ReentrantLock(true),true:公平锁 false:非公平锁

5.绑定条件Condition

synchronized不能绑定

ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精确唤醒,而不是像synchronized通过object类的wait()/notify()/notifyAll()方法要随机唤醒一个线程要么唤醒全部线程。

6.锁的对象

synchronzied锁的是对象,锁是保存在对象头里面,根据对象头数据来标识是否有线程获得锁/争抢锁。

ReetrantLock锁是线程,根据进入的线程和int类型的state标识锁的获得/争抢

分布式锁实现方式

①数据库层-->乐观锁(version即cas);悲观锁(基于数据库级别的for update)

②基于redis原子操作-->setnx+expire实现

③基于zookeeper-->基于interProcessMutex实现(分布式的公平可重入互斥锁,类似于单个JVM进程内的ReentrantLock(fair=true),利用临时顺序节点的顺序性)

zookeeper的节点有4中类型:永久节点,永久顺序节点,临时节点,临时顺序节点

④redisson是对redis进行封装的客户端,实现了很多锁和其他相关功能,其中分布式锁redLock实现原理:

redLock核心思想是n个节点(相互独立)中的n/2+1个节点获取到锁,切节点获取锁的时间远小于失效时间,才算获取锁成功,如果因为某些原因获取锁失败,则所有节点都需要进行解锁操作(包括为获取到锁的节点)

1.基于数据库

2.基于redis实现分布式锁

2.1:setnx+expire

缺点:获取锁是非阻塞,非公平锁,不支持需要公平锁的场景,redis主从存在延迟,在master宕机发生主从切换时,可能会导致锁失效

2.2:基于Redlock算法实现分布式锁。

在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。我们确保将在N个实例上使用与在Redis单实例下相同方法获取和释放锁。

需要在所有节点都释放锁就行,不管之前有没有在该节点获取锁成功。

3.基于zookeeper实现分布式锁

利用临时节点与 watch 机制。每个锁占用一个普通节点 /lock,当需要获取锁时在 /lock 目录下创建一个临时节点,创建成功则表示获取锁成功,失败则 watch/lock 节点,有删除操作后再去争锁。

临时节点好处在于当进程挂掉后能自动上锁的节点自动删除即取消锁。

缺点:所有取锁失败的进程都监听父节点,很容易发生羊群效应,即当释放锁后所有等待进程一起来创建节点,并发量很大。

分布式事务(2pc、3pc、TCC)

2pc是一个非常经典的强一致、中心化的原子提交协议。这里所说的中心化是指协议中有两类节点:一个是中心化协调者节点(coordinator)和N个参与者节点(partcipant)。

第一个阶段为请求/表决阶段,第二阶段:提交/执行阶段(正常流程)

缺点:性能问题。执行过程,节点都处于阻塞状态。

协调者单点故障问题。事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,会导致参与者收不到提交或回滚的通知,从而导致参与者节点始终处于事务无法完成的中间状态。

丢失消息导致的数据不一致问题。在第二个阶段,如果发生局部网络问题,

3pc

引入超时机制。同时在协调者和参与者中都引入超时机制。

在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

1、协调者向参与者发送commit请求

2、协调者根据参与者的反应情况来决定,任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断

3、该阶段进行真正的事务提交

补偿事务(TCC)

TCC(Try-Confirm-Cancel)又称补偿事务。其核心思想是:"针对每个操作都要注册一个与其对应的确认和补偿(撤销操作)"。它分为三个操作:

Try阶段:主要是对业务系统做检测及资源预留。

Confirm阶段:确认执行业务操作。

Cancel阶段:取消执行业务操作。

TCC事务的处理流程与2PC两阶段提交类似,不过2PC通常都是在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。这种分布式事务的实现方式的优势在于,可以让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。

AQS

AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

1.isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

2.tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。

3.tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。

4.tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

5.tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false

ReentrantLock:state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

CountDownLatch:任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。

addWaiter(Node)此方法用于将当前线程加入到等待队列的队尾,并返回当前线程所在的结点。

Node结点是对每一个访问同步代码的线程的封装,其包含了需要同步的线程本身以及线程的状态,如是否被阻塞,是否等待唤醒,是否已经被取消等。变量waitStatus则表示当前被封装成Node结点的等待状态,共有4种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE。

 ①CANCELLED=1:表示线程因为中断或者等待超时,需要从等待队列中取消等待;

②SIGNAL=-1:当前线程thread1占有锁,队列中的head(仅仅代表头结点,里面没有存放线程引用)的后继结点node1处于等待状态,如果已占有锁的线程thread1释放锁或被CANCEL之后就会通知这个结点node1去获取锁执行。

③CONDITION=-2:表示结点在等待队列中(这里指的是等待在某个lock的condition上,关于Condition的原理下面会写到),当持有锁的线程调用了Condition的signal()方法之后,结点会从该condition的等待队列转移到该lock的同步队列上,去竞争lock。(注意:这里的同步队列就是我们说的AQS维护的FIFO队列,等待队列则是每个condition关联的队列)

④PROPAGTE=-3:表示下一次共享状态获取将会传递给后继结点获取这个共享同步状态。

独占锁(exclusive)和共享锁(shared)在实现上的区别

    独占锁的同步状态值为1,即同一时刻只能有一个线程成功获取同步状态。共享锁的同步状态>1,取值由上层同步组件确定。

    独占锁队列中头节点运行完成后释放它的直接后继节点。共享锁队列中头节点运行完成后释放它后面的所有节点。

    共享锁中会出现多个线程(即同步队列中的节点)同时成功获取同步状态的情况。

重入锁:重入锁指的是当前线成功获取锁后,如果再次访问该临界区,则不会对自己产生互斥行为。Java中对ReentrantLock和synchronized都是可重入锁,synchronized由jvm实现可重入机制,ReentrantLock的可重入性基于AQS实现。同时,ReentrantLock还提供公平锁和非公平锁两种模式。重入锁的基本原理是判断上次获取锁的线程是否为当前线程,如果是则可再次进入临界区,如果不是,则阻塞。

数据库锁

    数据库锁可以分为行锁、页锁、表锁,其中Innodb支持行锁,行锁是粒度最小的锁,系统开销大,并发量大,冲突发生小,表锁粒度最大,系统开销小,并发小,发生冲突高,页锁介于行锁和表锁之间。行锁按照使用方式可以分为共享锁-读锁和排他锁-写锁,共享锁其他线程可以读不可写,排他锁其他线程不可读不可写。Record lock锁住的时索引,如果Innodb没有索引,会自动生成一个聚簇索引。Gap lock间隙锁,实在索引的间隙之间加锁,这是repeatable read隔离级别可以防止幻读的主要原因。next-key lock是record lock和gap key结合机制,即锁记录索引本身也在索引间隙之间加锁。MyISAM是一次性加载全部的锁,要不全成功,要不就失败,所以不存在死锁现象,而Innodb是逐步加锁的过程,比如一个事务在锁定主键索引,等待另外相关索引,另一个事务锁定非主键索引,等待主键索引。避免死锁,1不同程序并发读取多张表,尽量约定相同顺序读取2同一个事务尽可能一次性获取全部锁3容易出现死锁场景,升级锁粒度,降低死锁概率。

分布式锁

1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;

2、高可用的获取锁与释放锁;

3、高性能的获取锁与释放锁;

4、具备可重入特性;

5、具备锁失效机制,防止死锁;

6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

分布式锁实现方式:数据库分布式、缓存分布式、zookeeper分布式锁

数据库分布式锁具备缺点

1、这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。

2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。

3、这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。

4、这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

解决方案:

1、数据库是单点?搞两个数据库,数据之前双向同步。一旦挂掉快速切换到备库上。

2、没有失效时间?只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。

3、非阻塞的?搞一个while循环,直到insert成功再返回成功。

4、非重入的?在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。

java

集合(知识点较为基础,自行可对应学习,需要注意ConcurrentHashMap在1.7和1.8版本以后的区别,也可按自己能力去看源码,后面有时间我会针对该点写一篇分享)

看到Array就是数组结构,有角标,查询速度很快。

看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();

看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。

看到tree就是二叉树,就要想到排序,就想要用到比较。

比较的两种方式:

一个是Comparable:覆盖compareTo方法;

一个是Comparator:覆盖compare方法。

currententHashMap

动态代理

1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

spring

spring boot自动装配原理

通过@springbootapplication--->@enableautoconfiguration-->@inport(autoconfigurationimportselector)-->@import(register)-->

   SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories文件中的所有自动配置类,并对其加载,这些自动配置类都是以AutoConfiguration结尾来命名的。它实际上就是一个JavaConfig形式的IOC容器配置类,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性,如server.port。

*Properties类的含义:封装配置文件的相关属性。

*AutoConfiguration类的含义:自动配置类,添加到IOC容器中。

spring事务

spring提供了三个接口使用事务:TransactionDefinition(事务定义)、PlatformTramsactionManager(事务管理器)、TransactionStatus(事务运行状态)

事务类型

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

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

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

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

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

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

PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

IOC和AOP

Spring中IOC原理是通过xml解析、工厂模式、反射实现

IOC思想给予IOC容器完成,IOC容器底层就是工厂模式

Spring提供IOC容器两种实现方式

1-BeanFactory:IOC容器基本实现,是Spring内部使用接口,不建议使用

 1.1-*加载xml解析配置时不创建对象,在getBean使用时创建

 1.2-不支持国际化功能

 1.3-applicationContext扩展了底层ResourceLoader接口

2-ApplicationContext:BeanFactory子接口,提供更多功能,建议使用

3-Bean管理就是1-创建对象;2-注入属性;

4-Bean管理操作有基于xml配置文件实现和基于注解方式实现

AOP动态代理,分为有接口(使用JDK动态代理)和无接口情况(使用CGLIB动态代理)

JDK动态代理既是创建接口实现类代理对象, 增强类的方法

CGLIB动态代理既是创建当前类的子类代理对象,增强累的方法

AOP主要有4个部分组成:1-连接点(类中被增强的方法,存在多个);2-切入点(实际被真正增强的方法);3-通知(前置通知、后置通知、环绕通知、异常通知、最终通知);4-切面(通知具体实现逻辑)

循环依赖(该点建议能力足够的话,去深入学习下)

spring进行扫描->反射后封装成beanDefinition对象->放入beanDefinitionMap->遍历map->验证(是否单例、是否延迟加载、是否抽象)->推断构造方法->准备开始进行实例->去单例池中查,没有->去二级缓存中找,没有提前暴露,没有->

生成一个objectFactory对象暴露到二级缓存中->属性注入,发现依赖Y->此时Y开始它的生命周期直到属性注入,发现依赖X->X又走一遍生命周期,当走到去二级缓存中找的时候找到了->往Y中注入X的objectFactory对象->完成循环依赖。

1、为什么要使用X的objectFacory对象而不是直接使用X对象?

利于拓展,程序员可以通过beanPostProcess接口操作objectFactory对象生成自己想要的对象

2、是不是只能支持单例(scope=singleton)而不支持原型(scope=prototype)?

是。因为单例是spring在启动时进行bean加载放入单例池中,在依赖的bean开始生命周期后,可以直接从二级缓存中取到它所依赖的bean的objectFactory对象从而结束循环依赖。而原型只有在用到时才会走生命周期流程,但是原型不存在一个已经实例化好的bean,

所以会无限的创建->依赖->创建->依赖->

3、循环依赖是不是只支持非构造方法?...。

是。类似死锁问题[doge]

spring常用注解

1、@Controller:用于标注控制器层组件

2、@Service:用于标注业务层组件

3、@Component : 用于标注这是一个受 Spring 管理的组件,组件引用名称是类名,第一个字母小写。可以使用@Component(“beanID”) 指定组件的名称

4、@Repository:用于标注数据访问组件,即DAO组件

5、@Bean:方法级别的注解,主要用在@Configuration和@Component注解的类里,@Bean注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。引用名称是方法名,也可以用@Bean(name = "beanID")指定组件名

6、@Scope("prototype"):将组件的范围设置为原型的(即多例)。保证每一个请求有一个单独的action来处理,避免action的线程问题。

由于Spring默认是单例的,只会创建一个action对象,每次访问都是同一个对象,容易产生并发问题,数据不安全。

7、@Autowired:默认按类型进行自动装配。在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

8、@Resource:默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配。

简单点说,就是,能够明确该类是一个控制器类组件的,就用@Controller;能够明确是一个服务类组件的,就用@Service;能够明确该类是一个数据访问组件的,就用@Repository;不知道他是啥或者不好区分他是啥,但是就是想让他动态装配的就用@Component。

@Controller、@Service、@Component、@Repository都是类级别的注解,如果一个方法也想动态装配,就用@Bean。

当我们想按类型进行自动装配时,就用@Autowired;当我们想按名称(beanID)进行自动装配时,就用@Resource;当我们需要根据比如配置信息等来动态装配不同的组件时,可以用getBean("beanID")。

IOC

IoC的主要实现方式有两种:依赖查找、依赖注入。

依赖查找,主要是容器为组件提供一个回调接口和上下文环境。这样一来,组件就必须自己使用容器提供的API来查找资源和协作对象,控制反转仅体现在那些回调方法上,容器调用这些回调方法,从而应用代码获取到资源。

依赖注入,组件不做定位查询,只提供标准的Java方法让容器去决定依赖关系。容器全权负责组件的装配,把符合依赖关系的对象通过Java Bean属性或构造方法传递给需要的对象。

Spring依赖注入的方式主要有四个,基于注解注入方式、set注入方式、构造器注入方式、静态工厂注入方式。推荐使用基于注解注入方式,配置较少,比较方便。

AOP

AOP主要一般应用于签名验签、参数校验、日志记录、事务控制、权限控制、性能统计、异常处理等。

切面(Aspect):共有功能的实现。如日志切面、权限切面、验签切面等。在实际开发中通常是一个存放共有功能实现的标准Java类。当Java类使用了@Aspect注解修饰时,就能被AOP容器识别为切面。

通知(Advice):切面的具体实现。就是要给目标对象织入的事情。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际开发中通常是切面类中的一个方法,具体属于哪类通知,通过方法上的注解区分。

连接点(JoinPoint):程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出等。Spring只支持方法级的连接点。一个类的所有方法前、后、抛出异常时等都是连接点。

切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。

目标对象(Target):那些即将切入切面的对象,也就是那些被通知的对象。这些对象专注业务本身的逻辑,所有的共有功能等待AOP容器的切入。

代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象本身业务逻辑加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。目标对象被织入共有功能后产生的对象。

织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译时、类加载时、运行时。Spring是在运行时完成织入,运行时织入通过Java语言的反射机制与动态代理机制来动态实现。

spring类加载机制

OverridingClassLoader 是 Spring 自定义的类加载器

DecoratingClassLoader 很简单,内部维护了两个集合,如果你不想你的类被自定义的类加载器管理,可以把它添加到这两个集合中,这样仍使用 JDK 的默认类加载机制。

java的SPI(service provider interface)

ava依赖外部的api包(类似jar),在外部依赖包中Meta-INF/service/下有对应依赖包的访问文件,java应用在启动的时候会加载该访问文件,在使用的时候有针对性的调用对应外部api服务接口,可实现插拔式的服务,即为SPI。同理spring boot的SPI也是同样的原理。

orm框架

当前 Java ORM 框架产品有很多,常见的框架有 Hibernate 和 MyBatis,其主要区别如下。

1) Hibernate

Hibernate 框架是一个全表映射的框架。通常开发者只要定义好持久化对象到数据库表的映射关系,就可以通过 Hibernate 框架提供的方法完成持久层操作。

2) MyBatis

MyBatis 框架是一个半自动映射的框架。这里所谓的“半自动”是相对于 Hibernate 框架全表映射而言的,MyBatis 框架需要手动匹配提供 POJO、SQL 和映射关系,而 Hibernate 框架只需提供 POJO 和映射关系即可。

与 Hibernate 框架相比,虽然使用 MyBatis 框架手动编写 SQL 要比使用 Hibernate 框架的工作量大,但 MyBatis 框架可以配置动态 SQL 并优化 SQL、通过配置决定 SQL 的映射规则,以及支持存储过程等。对于一些复杂的和需要优化性能的项目来说,显然使用 MyBatis 框架更加合适。

MyBatis 框架可应用于需求多变的互联网项目,如电商项目;Hibernate 框架可应用于需求明确、业务固定的项目,如 OA 项目、ERP 项目等。

spring拦截器过滤器

1.二者适用范围不同。Filter是Servlet规范规定的,只能用于Web程序中,而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。

2.规范不同。Filter是在Servlet规范定义的,是Servlet容器支持的,而拦截器是在Spring容器内的,是Spring框架支持的。

3.使用的资源不同。同其他代码块一样,拦截器也是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象(各种bean),而Filter不行。

4.深度不同。Filter只在Servlet前后起作用,而拦截器能够深入到方法前后、异常跑出前后等,拦截器的使用有更大的弹性。

spring事务传播机制

   事务传播有7种类型,常用4种required,required_new,nexten,supports,not_support,never,mandatory

spring框架中单例bean是否线程安全

看bean状态,无状态安全,有状态不安全

spring设计模式

观察者aop,工厂beanfactory,适配器,单例、监听器、

spring事务失效

数据库不支持事务;事务注解只能public方法或类;不带事务的方法调用带事务的方法;内部调用-service没有被spring管理

一下问题都问到过,建议理解

1、spring mvc工作流程?

2、spring核心?

3、spring的事务实现原理?隔离级别?

4、spring的bean、生命周期?

5、如何理解spring boot的starter?

bean Factory和applicationContext区别

    1beadfactory为顶层bean容器接口,2applicationcontext为beanfactory子接口,2beanfactory的bean创建不是在bean容器启动实例化,

6、spring cloud核心组件?

7、spring中后置处理器作用?

mysql

explain、

explain用于查询mysql语句执行过程情况,主要查询的信息包括以下参数:id、select_type、type、portitions、ref、possibles_keys、key、filtered、extra

id:查询序列号,由大到小

select_type:查询类型,simple(不适用子查询或union查询)、primary(包含子查询时,最外层查询为primary)、union(级联查询时union后面第二个sql的查询)、dependent_union(级联查询union后面第二个sql的查询)、

union_result(级联查询,union后面的查询统一称为union_result)

type:all(full table scan)、range(检索给定范围行)、index(full index scan,和all区别在于index是搜索索引树)、ref(列和常量用于查找索引列的值)、eq_ref(使用索引为唯一索引)、const、system、null

extra:解决查询的详细信息

using where:仅通过索引实现检索

using temporary:使用临时表存储结果集,常见排序和分组

using filesort:无法通过索引完成的排序操作

索引

mysql索引:索引是一种数据结构,存储在磁盘,结构有—>树、b+tree、链表、hash、红黑树

B树:节点具有相同深度;每个节点都存储有数据;叶节点指针为空;节点数据由左—>右自增(因为叶节点没有指针,范围查询不具有优势)

B+树:非叶节点存索引,不存储数据;数据存储在叶节点;叶节点存在指针;叶节点数据由左—>右自增

Hash:不支持范围查询;不支持索引完成排序和模糊查询;不支持多列联合索引;存在hash碰撞问题

MyISAM索引实现:索引属于非聚簇索引(非聚集索引)

InnoDb:索引属于聚簇索引(聚集索引),主键索引存储的是索引+数据,非主键索引存储的是对应主键索引;innodb一定存在主键,未设置的话,mysql会默认生成主键(影响性能—(因为默认主键为uuid,占空间,范围查询没有优势),建议手动生成主键)

联合索引遵守最左匹配原则(包含部分联合索引),如果搜索条件包含联合索引所有要素,这进行全值匹配,命中索引

mysql原则、隔离级别

mysql原子性、一致性、持久性、隔离性

读未提交(脏读)、读已提交(脏读、不可重复度)、可重读(幻读)、可串行化

脏读(读取未提交的数据)、不可重复度(多次读取(已提交、未提交)数据,结果不一致)、幻读(多次查询的数据行数不一致)

索引无效

没有符合最有前缀原则、字段进行了隐式数据类型转换、走索引没有全表扫描效率高

条件下推

    索引下推即在不适用ICP情况下,在使用非主键索引进行查询时,存储引擎通过索引检索到数据,然后返回给数据库服务器判断是否符合条件(where),引入ICP情况下,如果存在某些被索引的列的判断条件时,数据库服务器将这部分判断转移给存储引擎,由存储引擎判断条件是否符合传递数据库服务器。减少存储引擎查询基础表的次数,也就是减少数据库服务器接收存储引擎的数据次数。

回表查询

    回表查询即针对Innodb索引而言,Innodb索引分为两类,聚簇索引和普通索引,聚簇索引即叶节点存储行记录,普通索引叶节点存储主键值,普通索引查询时,先查询子节点的主键值,然后根据主键值查询聚簇索引子节点的行记录,这种查询两次表即为回表查询。

MVCC

    上述更新前建立undo log,根据各种策略读取时非阻塞就是MVCC,undo log中的行就是MVCC中的多版本,这个可能与我们所理解的MVCC有较大的出入,一般我们认为MVCC有下面几个特点:

每行数据都存在一个版本,每次数据更新时都更新该版本

修改时Copy出当前版本随意修改,个事务之间无干扰

保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)

就是每行都有版本号,保存时根据版本号决定是否成功,听起来含有乐观锁的味道。。。,而Innodb的实现方式是:

事务以排他锁的形式修改原始数据

把修改前的数据存放于undo log,通过回滚指针与主数据关联

修改成功(commit)啥都不做,失败则恢复undo log中的数据(rollback)

二者最本质的区别是,当修改数据时是否要排他锁定,如果锁定了还算不算是MVCC? 

Innodb的实现真算不上MVCC,因为并没有实现核心的多版本共存,undo log中的内容只是串行化的结果,记录了多个事务的过程,不属于多版本共存。但理想的MVCC是难以实现的,当事务仅修改一行记录使用理想的MVCC模式是没有问题的,可以通过比较版本号进行回滚;但当事务影响到多行数据时,理想的MVCC据无能为力了。

比如,如果Transaciton1执行理想的MVCC,修改Row1成功,而修改Row2失败,此时需要回滚Row1,但因为Row1没有被锁定,其数据可能又被Transaction2所修改,如果此时回滚Row1的内容,则会破坏Transaction2的修改结果,导致Transaction2违反ACID。

理想MVCC难以实现的根本原因在于企图通过乐观锁代替二段提交。修改两行数据,但为了保证其一致性,与修改两个分布式系统中的数据并无区别,而二提交是目前这种场景保证一致性的唯一手段。二段提交的本质是锁定,乐观锁的本质是消除锁定,二者矛盾,故理想的MVCC难以真正在实际中被应用,Innodb只是借了MVCC这个名字,提供了读的非阻塞而已。

MyISAM和Innodb,MyISAM不支持主外键,事务,只锁定表,只缓存索引,表空间小。Inndb支持主外键,事务,行锁,缓存行记录,表空间大,MyISAM关注性能,适合读,Innodb关注事务。

Mysql中mvcc在Innodb中得到支持,Innodb为每行记录实现三个隐藏字段:6字节的事务id(DB_TRX_ID)、7字节的回滚指针(DB_ROLL_PTR)、隐藏的id,为了支持事务,Innodb引入redo log(保存执行的sql语句到指定log文件),当mysql执行recovery时重新执行redo log记录的sql语句即可,当执行sql语句时,redo log会被首先写入log buffer,执行commit时,log budder的内容会视情况刷入磁盘。Undo log用于回滚,copy事务前的数据到undo buffer,在合适时间把undo log内容刷到磁盘,undo buffer和redo buffer

事务以排他锁的形式修改原数据(现将sql语句保存在redo log,commit时直接读取redo log),把修改前的数据保存在undo log,通过回滚指针与主数据关联,修改成功则不做其他处理,如果失败则恢复undo log的数据。

mysql缓存

    MySQL缓存机制即缓存sql 文本及缓存结果,用KV形式保存再服务器内存中,如果运行相同的sql,服务器直接从缓存中去获取结果,不需要再去解析、优化、执行sql。在表的结构或数据发生改变时,查询缓存中的数据不再有效,查询缓存值的相关条目将被清空。对于频繁更新的表,查询缓存不合适。对于一些不变的数据且有大量相同sql查询的表,查询缓存可以大大提高查询的性能

1、B树和B+树区别?

2、Innodb中B+树有什么特点?

3、什么是Innodb中的page?

4、Innodb中的B+树是如何产生的?

5、什么是聚簇索引?

6、Innodb是如何支持范围查找能走索引的?

7、什么是联合索引?对应的B+树是如何产生的?

8、什么是最左前缀原则?

9、为什么要遵守最左前缀原则才能利用到索引?

11、什么是覆盖索引?

12、有哪些情况会导致索引失效?

13、order by导致索引失效?

14、MyISAM和Innodb区别?

15、mysql事务基本特征、隔离级别?

16、事务并发可能引发什么问题?

18、谈一下mysql中的死锁?

19、mysql集群如何搭建?

20、mysql慢查询如何优化?

21、mysql锁类型有哪些?

22、mysql主从同步原理?

24、mysql哪些索引?

25、mysql中的扰动函数?

缓存异常

缓存穿透:先通过缓存层获取数据,获取不到再去数据层获取数据(解决方案:数据层同步缓存层,代价太高;)

缓存击穿:读取缓存层和数据层都不存在的数据,导致数据大量访问数据层(解决方案:缓存层和数据层加上布隆过滤器)

缓存雪崩:某一时刻,缓存层数据全部失效(有效期到期或缓存宕机),导致请求大量进入数据层(解决方案:缓存数据加上随机有效期)

redis

1、redis单线程为什么这么快?

    1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

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

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

    4、使用多路I/O复用模型搭配非阻塞IO;

    5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

2、redis的持久化机制?

    redis提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)。

    RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

    AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

3、redis的过期键删除策略?

    定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作

     惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键

    定期删除: 每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多个过期键,以及要检查多少个数据库,则由算法决定。

    定时删除策略对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存,定时删除策略的缺点是,它对CPU时间是最不友好的:在过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分CPU时间。定时删除策略的缺点是,它对CPU时间是最不友好的:在过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分CPU时间。定期删除策略是前两种策略的一种整合和折中。

5、redis和mysql如何保证数据一致

    1、先删数据库,后删缓存,会出现数据成功、缓存失败现象;2、先删缓存,后删数据库,会出现缓存成功,用户读取数据库旧数据,然后再更新的数据库;3、双删操作(缓存-数据库-缓存);4、基于订阅binlog同步机制

6、redis集群方案?

    哨兵-哨兵个数为2n+1,目的为了投票选举、集群-至少3主3从、单节点

8、redis线程模型?

    多个 socket(套接字) 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket(套接字),会将 socket (套接字)产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

9、redis有哪些数据结构?有哪些典型应用场景?

     string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)

     使用场景:String-->常规key-value缓存应用。常规计数: 微博数, 粉丝数

                   hash-->存储部分变更数据,如用户信息等

10、redis主从复制的核心原理?

Redis具有高可靠性,具有两层含义:一是数据尽量少丢失,二是服务尽量少中断。AOF和RDB保证了前者,而对于后者,redis的做法是增加副本冗余,将一份数据同时保存在多份实例上。即使有一个实例出现了宕机,需要一段时间才能恢复,其他实例也可以对外提供服务,不会影响业务使用。

redis提供了主从库模式,以保证数据副本的一致性,主从库之间采用的是读写分离的方式。

数据同步分三个阶段:1.建立连接,协商同步;2.主库同步数据至从库;3.主库发送缓存区新增数据给从库

13、常见缓存淘汰算法?

惰性算法-->查询时发现key过期则删除,对性能友好,内存不友好

定时算法-->定时任务更新过期key,对内存友好,对性能不友好

定期算法-->在过期key中选择删除,allkeys-lru、allkeys-lfu、allkeys-random、volatilekeys-ttl、volatilekeys-random、volatilekeys-lru、volatilekeys-lfu等

16、redis事务实现?

     redis为我们的事务提供了四个指令:multi(开启事务),discard(丢弃),exec(执行),watch(监视)

17、redis高可用方案?

11、redis集群策略

12、布隆过滤器原理,优缺点?

14、分布式系统中常用的缓存方案?

kafka

kafka为什么快?

kafka消息可靠性

   副本数据同步策略,全部同步才发送ack,延迟高,需要n+1副本

   LSR机制,为了防止Kafka在选择第二种数据同步策略时,因为某一个follower故障导致leader一直等下去,Leader维护了一个动态的in-sync replica set (ISR)。ISR:同步副本,和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给生产者发送ack。

   ack应答机制,为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据

   故障处理,leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据

主读主写,每个broker中都有一个leader和多个fllower,leader负责读写,fllower负责数据同步,当leader宕机则从fllower中选举一个作为新的leader,保证高可用和可靠性

4、kafka如何避免重读或丢读?

kafka不支持读写分离

    1.提高数据一致性2每个brker中leader个数固定,本身已实现负载均衡

kafka选举策略

   broker控制器选举,分区多副本选举,消费组选举

kafka高性能高吞吐

    磁盘顺序读写

传统的磁盘读写是随机读写,导致数据是分散开的,不在一起,不仅将预读浪费掉了,还需要进行多次的寻道和旋转延迟,从而浪费过多的时间。

而Kafka采取的是顺序读写,磁盘会预读,预读就是从读取的起始地址连续读取多个页面,时间主要都花费在了传输上,从而使得磁盘的处理效率更接近于内存。

比起内存,使用磁盘的好处更体现在容量上,从而保证消息的堆积。

    零拷贝

传统的数据赋值是:

先将磁盘文件中的数据先读取到内核缓冲区,

再拷贝到用户缓冲区,

再将用户缓冲区的数据拷贝到socket的发送缓冲区,

然后再发送到网卡进行传输;

零拷贝:

直接从磁盘文件中读取到内核缓冲区,再由内核缓冲区直接给到网卡,再返回给消费者进程,从而避免了传统数据复制过程中CPU将数据在内核缓冲区和用户缓冲区的拷贝流程

分区分段加索引

partition由多个大小相等的segment file组成,每次进行的文件操作也是直接操作的segment file。为了进一步查询优化,Kafka默认为分段后的数据文件建立了以.index结尾的索引文件。这种分区分段+索引的设计不仅提升了数据读取的效率,同时也提高了数据操作的并行度(类似于分段锁)。

批量压缩,批量读写

多条消息一起压缩,从而降低带宽。

直接操作page cache(页高速缓冲存储器),而不是JVM,避免了对象的创建和GC耗时,因此读写效率更高

8、kafka是pull?push?优劣势分析?

kafka的rebalance机制

  每个消费者订阅broker的 partition数据,当消费者数量改变或partition数量改变,都需要重新分配订阅的主题数量,这个过程就是rebalance

10、kafka消息丢失场景有哪些?如何规避?

zookeeper

1、zk的watch机制是什么?

2、zk的命名、配置、管理如何实现?

3、zk的数据模型和节点类型?

4、zk的击穿、穿透、雪崩、预热解决方案?

网络

TCP三次握手和四次挥手

1.建立连接,发哦送给你syn包,等待服务器确认

2.服务器接收syn包,确认syn包,发送syn+ack响应

3.客服端接收syn+ack响应,发送ack确认

1.客户端发送释放报文

2.服务器接收释放报文,发出确认应答

3.客户端接收服务端确认应答,等待服务端发送释放报文

4.服务端处理完,发送释放报文

5.客户端接收后,发送ack确认

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/898787.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号