面试题锦--

1、JAVA基础

异常分类

分为两大类

  • Error:java运行时系统的内部错误和资源消耗尽错误,一旦出现这种错误,那就得停止服务了
  • Exception:分为编译器报错CheckedException(受检异常/可捕获异常)、运行时报错RuntimeException(非受检异常/运行时异常)

RuntimeException: I/O导致的IOException、SQLException
CheckedException: try/catch 可捕获的

"a==b"和"a.equals(b)"有什么区别?

poll() 方法和remove()方法的区别?

ArrayList与LinkedList的区别?

LinkedList是单项链表还是双向链表

TreeMap采用的是什么树实现的?

java中(NIO),直接缓冲区和非直接缓冲区的区别?(JVM)?

TCP协议与UDP协议的区别?

ByteBuffer与StringBuffer的区别?

Java 中,怎么获取一个文件中单词出现的最高频率?

解释一下里氏替换原则?

构造器注入和setter依赖注入,哪种方式更好?

SOA(面向服务)和微服务架构有什么区别?

spring cloud 解决了哪些问题?

根据 Spring Cloud 的官方网站, Spring Cloud 为开发人员提供了快速构建分布 式系统中一些常见模式的工具( 例如配置管理, 服务发现, 断路器, 智能路由, 领导选举, 分布式会话, 集群状态)。
在使用 Spring Boot 开发分布式微服务时,我们面临的问题很少由 Spring Cloud 解决。
在使用 Spring Boot 开发分布式微服务时,我们面临的问题很少由 Spring Cloud 解决。
与分布式系统相关的复杂性 – 包括网络问题,延迟开销,带宽问题,安 全问题。
处理服务发现的能力 – 服务发现允许集群中的进程和服务找到彼此并进 行通信。
解决冗余问题 – 冗余问题经常发生在分布式系统中。
负载平衡 – 改进跨多个计算资源(例如计算机集群,网络链接,中央处 理单元)的工作负载分布。
减少性能问题 – 减少因各种操作开销导致的性能问题。

访问者模式是什么东西,解决什么问题?

回答思路:访问者模式是一种分离对象数据结构与行为的方法,通过这种分离,可以为一个已存在的类增加新的操作而无须为它们进行修改,在spring中就有对这个设计模式的实现案例,在java中我们会通过注解@Value通过占位符对类中的属性赋值,而且是解析的Properties 文件中的值映射到类的成员变量上,只需要修改Properties文件。在spring中每个对象都会被解析成BeanDefinition ,然后访问者模式中,会用Spring 的 BeanDefinitionVisitor 用来访问 BeanDefinition,访问的具体调用就是BeanDefinitionVisitor.visitBeanDefinition(bd);封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。于要更新的表有状态字段,并且刚好要更新状态字段的这种特殊情况,并非所有场景都适用。

四种引用讲讲,分别使用在哪些地方

高手回答:
java根据其生命周期的长短又将引用类型分为强引用、软引用、弱引用、虚引用;

强引用:new一个对象就是强引用,例如 Object obj = new Object();当JVM的内存空间不足时,宁愿抛出OutOfMemoryError使得程序异常终止也不愿意回收具有强引用的存活着的对象;

软引用的生命周期比强引用短一些。软引用是通过SoftReference类实现的。当JVM认为内存空间不足时,就会去试图回收软引用指向的对象;软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收! 这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

弱引用是通过WeakReference类实现的,它的生命周期比软引用还要短。在GC的时候,不管内存空间足不足都会回收这个对象,也同样适用于内存敏感的缓存。如果一个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来记住此对象。或者想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候就应该用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响。

虚引用,是通过PhantomReference类实现的。任何时候可能被GC回收,就像没有引用一样;无法通过虚引用访问对象的任何属性或者函数。虚引用仅仅只是提供了一种确保对象被finalize以后来做某些事情的机制

一个对象是多少字节?在堆区都存了什么东西?对象头里面都有啥?

高手回答:
对象在内存中的存储的布局可以分为三块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding);对象头由 Markword + 类指针kclass(该指针指向该类型在方法区的元类型) 组成;普通对象头在32位系统上占用8bytes,64位系统上占用16bytes。64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。
MarkWord用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。在64位的虚拟机(未开启压缩指针)为64bit。
kclass存储的是该对象所属的类在方法区的地址,所以是一个指针,默认Jvm对指针进行了压缩,用4个字节存储,如果不压缩就是8个字节。 关于Compressed Oops的知识,大家可以自行查阅相关资料来加深理解。
实例数据部分就是成员变量的值,其中包含父类的成员变量和本类的成员变量。也就是说,除去静态变量和常量值放在方法区,非静态变量的值是随着对象存储在堆中的。
用于确保对象的总长度为8字节的整数倍。HotSpot要求对象的总长度必须是8字节的整数倍。由于对象头一定是8字节的整数倍,但实例数据部分的长度是任意的。因此需要对齐补充字段确保整个对象的总长度为8的整数倍。

CMS和G1讲讲流程

高手回答:
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。采用的是"标记-清除算法",整个过程分为4步;
(1)初始标记,标记GC Roots能直接关联到的对象 Stop The World-- ->速度很快
(2)并发标记,就是从GC Roots开始找到它能引用的所有其它对象的过程
(3)重新标记,Stop The World 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间要短。
(4)并发清除,在整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,因此,从总体上看,CMS收集器的内存回收过程是与用户线程一起并发执行的。
G1收集器的工作过程也可以分为四步:
初始标记、并发标记、最终标记跟CMS的前三步基本一致;
筛选回收,对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划

Spring怎么通过参数控制某个classA生成对应个数的A实例?

参考思路:过scop注解来控制,在scop注解中包含4个选项,分别为:
@Scope(“prototype”):多实例,IOC容器启动创建的时候,并不会创建对象放在容器在容器当中,当你需要的时候,需要从容器当中取该对象的时候,就会创建
@Scope(“singleton”):单实例 IOC容器启动的时候就会调用方法创建对象,以后每次获取都是从容器当中拿同一个对象
@Scope(“request”):同一个请求创建一个实例
@Scope(“session”):同一个session创建一个实例

限流策略有哪些,滑动窗口算法和令牌桶区别,使用场景

参考思路:限流算法常用的几种实现方式有如下四种:计数器、滑动窗口、漏桶和令牌桶;
● 计数器:
○ 思想:在固定时间窗口内对请求进行计数,与阀值进行比较判断是否需要限流,一旦到了时间临界点,将计数器清零。
○ 问题:计数器算法存在“时间临界点”缺陷。比如每一分钟限制100个请求,可以在00:00:00-00:00:58秒里面都没有请求,在00:00:59瞬间发送100个请求,这个对于计数器算法来是允许的,然后在00:01:00再次发送100个请求,意味着在短短1s内发送了200个请求,如果量更大呢,系统可能会承受不住瞬间流量,导致系统崩溃
● 滑动窗口:
○ 思想:滑动窗口算法将一个大的时间窗口分成多个小窗口,每次大窗口向后滑动一个小窗口,并保证大的窗口内流量不会超出最大值,这种实现比固定窗口的流量曲线更加平滑。
○ 问题:没有根本解决固定窗口算法的临界突发流量问题
● 漏桶:
○ 思想:漏桶算法是首先想象有一个木桶,桶的容量是固定的。当有请求到来时先放到木桶中,处理请求的worker以固定的速度从木桶中取出请求进行相应。如果木桶已经满了,直接返回请求频率超限的错误码或者页面
○ 适用场景:漏桶算法是流量最均匀的限流实现方式,一般用于流量“整形”。例如保护数据库的限流,先把对数据库的访问加入到木桶中,worker再以db能够承受的qps从木桶中取出请求,去访问数据库。
○ 问题:木桶流入请求的速率是不固定的,但是流出的速率是恒定的。这样的话能保护系统资源不被打满,但是面对突发流量时会有大量请求失败,不适合电商抢购和微博出现热点事件等场景的限流。
● 令牌桶:
○ 思想:令牌桶是反向的"漏桶",它是以恒定的速度往木桶里加入令牌,木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌,如果能够得到令牌则继续执行后续的业务逻辑。如果没有得到令牌,直接返回访问频率超限的错误码或页面等,不继续执行后续的业务逻辑。
○ 适用场景:适合电商抢购或者微博出现热点事件这种场景,因为在限流的同时可以应对一定的突发流量。如果采用漏桶那样的均匀速度处理请求的算法,在发生热点时间的时候,会造成大量的用户无法访问,对用户体验的损害比较大

SpringBoot启动过程

回答思路:SpringBoot启动时通过执行main方法中的SpringApplication.run方法去启动的,在run方法中调用了SpringApplication的构造方法,在该构造方法中加载了META-INFA\spring.factories文件配置的ApplicationContextInitializer的实现类和ApplicationListenerr的实现类,ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用。ApplicationListener当springboot启动时事件change后都会触发。
SpringApplication实例构造完之后会调用它的run方法,在run方法中作了以下几步重要操作:

  1. 获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法
  2. 准备环境,并且把环境跟spring上下文绑定好,并且执行environmentPrepared()方法
  3. 创建上下文,根据项目类型创建上下文
  4. 执行spring的启动流程扫描并且初始化单实列bean
    同时通过@SpringBootApplication注解将ClassPath路径下所有的META-INF\spring.factories文件中的EnableAutoConfiguration实例注入到IOC容器中

synchronized的介绍、锁升级过程

高手回答:
synchronized是Java中的关键字,底层是JVM实现的一种同步锁,synchronized能同时保证可见性,原子性,有序性;
synchronized有三种方式来加锁,不同的修饰类型,代表锁的控制粒度:
● 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
● 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
● 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
在 JDK 1.6之前,synchronized 还是一个重量级锁,是一个效率比较低下的锁,但是在JDK 1.6后,Jvm为了提高锁的获取与释放效率对(synchronized )进行了优化,引入了偏向锁和轻量级锁,从此以后锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁),并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级,也就是说只能进行锁升级(从低级别到高级别),不能锁降级(高级别到低级别),意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。
偏向锁:
当一个线程访问加了同步锁的代码块时,会在对象头中存储当前线程的ID,后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁。如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了,引入偏向锁是为了不存在线程竞争的情况下尽量减少不必要的加解锁。
轻量级锁:
当一个线程获取到该锁后,另一个线程也来获取该锁,这个线程并不会被直接阻塞,而是通过自旋来等待该锁被释放。自旋就是让线程执行一段无意义的循环。分为自适应自旋锁和固定次数自旋锁,前者是自旋次数位动态的,JVM通过之前这把锁的获得情况来自动的选择增加或者减少自旋次数直至阻塞。后者即固定次数自旋,超过则阻塞。
自旋锁设计的原因:Java的线程是映射到操作系统原生线程之上的,阻塞或唤醒线程要操作系统从用户态和核心态之间切换, 线程刚刚进入阻塞状态,这个锁就被其他线程释放了,则需要操作系统来唤醒,浪费资源。
重量级锁:由对象内置锁ObjectMonitor实现:
objectMonitor流程:
● 所有期待获得锁的线程,在锁已经被其它线程拥有的时候,这些期待获得锁的线程就进入了对象锁的entry set区域(监控区)。
● 所有曾经获得过锁,但是由于其它必要条件不满足而需要wait的时候,线程就进入了对象锁的wait set区域(待授权区) 。
● 在wait set区域的线程获得Notify/notifyAll通知的时候,随机的一个Thread(Notify)或者是全部的Thread(NotifyALL)从对象锁的wait set区域进入了entry set中。
● 在当前拥有锁的线程释放掉锁的时候,处于该对象锁的entryset区域的线程都会抢占该锁,但是只能有任意的一个Thread能取得该锁,而其他线程依然在entry set中等待下次来抢占到锁之后再执行。

介绍一下AQS

高手回答:
AQS全名:AbstractQueuedSynchronizer,是并发容器J.U.C(java.util.concurrent)下locks包内的一个类。它实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列。底层实现的数据结构是一个双向链表。
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

cookie和session的区别

答:●session在服务器端Cookie在客户端(浏览器)
●session 的运行依赖session id,而session id是存在cookie中的,也就是说,如果浏览器禁用了cookie , 同时session也会失效(但是可以通过其它方式实现,比如在url中传递session _id )
●session 可以放在文件.数据库、或内存中都可以。
●用户验证这种场合一般会用 session
●cookie不是很安全,别人可以分析存放在本地的COOKI E并进行COOKIE欺骗考虑到安全应当使用session.
●session会在一 定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE.
●单个cookie保存的数据不能超过4K ,很多浏览器都限制个站点最多保存20个cookie。

sleep和wait分别是那个类的方法 ,有什么区别

答:●sleep和wait
sleep是Thread类的方法
C wait是Object类的方法
●有什么区别
( sleep0方法(休眠)是线程类( Thread )的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会
( CPU )让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)。
( wait()是Object类的方法,调用对象的wait0方法导致当前线程放弃对象的锁(线程皆停执行) ,进入对象的等待池
( wait pool) ,只有调用对象的notify(0方法(或ntifll()方法)时才能唤翟等待池中的线程进入等锁池( lockpool ) 如果线程重新获得对象的锁就可以进入就绪状态

volatile关键字的如何保证内存可见性

答:volatile关键字的作用
保证内存的可见性
防止指令重排
注意:volatile并不保证原子性
内存可见性
ovolatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以volatile关键字的作用之一就是保证变量修改的实时可见性。
当且仅当满足以下所有条件时,才应该使用volatile变量
对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
该变量没有包含在具有其他变量的不变式中。
volatile使用建议
在两个或者更多的线程需要访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile。
由于使用volatile用蔽掉了JMM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
volatile和synchronized区别
volatile不会进行加锁操作:
volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。
volatile变量作用类似于同步变量读写操作:
从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。
volatile不如synchronized安全:
在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更难以理解。仅当volatile变量能简化代码的实现以及对同步策路的验证时,才应该使用它。一般来说,用同步机制会更安全些。
volatile无法同时保证内存可见性和原则性:
加锁机制{即同步机制)既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性,原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count+ +"、 "count = count+1”。

CPU使用率过高应该如何排查?

答:1.使用top 定位到占用CPU高的进程PIDtop 指令通过ps aux | grep PID命令
2.获取线程信息,并找到占用CPU高的线程ps -mp 进程ID -o THREAD,tid,time | sort -rn
3.将需要的线程ID转换为16进制格式printf “%x\n” tid
4.打印线程的堆栈信息jstack pid |grep tid -A 30

当用户提交了一个任务,接下来这个任务执行过程?

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

2、MySql

数据库三范式?

  • 第一范式:字段具有原子性、不可再分
  • 第二范式:每个实例或者行可以被唯一区分
  • 第三范式:一个数据库表中不包含已在其他表中已包含的非主键关键字

MySql中ACID事务特性?

  • A:原子性,要么成功要么失败,回滚数据-undolog
  • C:一致性,数据库中的完整性约束不能被破坏
  • I:隔离性,多个事务对行数据的操作透明的,互不干扰(MVCC+锁)
  • D:持久性,一旦事务提交,结果永久性(redolog+双写缓冲)

MySql的事务隔离级别?

  • 读未提交=》脏读
  • 读已提交=》不可重复读:RC事务每次读建立一个readView
  • 可重复度=》幻读:RR 事务第一次查询建立一个ReadView
  • 串行化=》最严格级别,事务串行执行,消耗资源

RC(不可重复读)和RR(可重复读)的主要区别

  • RR的间隙锁会导致锁定范围扩大
  • 条件列未使用到索引,RR锁表,RC锁行
  • RC半一致性,读可以增加update操作并发性

InnoDB的行锁到底锁住了什么?

  • 锁住索引
  • 无索引时,-rowid,全表扫描,把每一个隐藏的-rowid都锁住,表锁

隔离性是如何实现的?

MVCC:效果是建立一个快照,同一个事务无论查询多少次都是相同的数据
规则:

  • 一个事务能看到的版本
    第一次查询之前,已经提交的数据;
    本事务的修改

  • 一个事务不能看到的数据版本
    在本事务第一次查询之后建立的事务
    活跃的未提交事务的修改

快照ReadView的结构和判断规则?

| m_ids:列表当前活跃的事务 |min_trx_id 活跃事务中最小的m_id |max_trx_id 系统分配的下一个事务ID|create_trx_id生成视图的事务ID

判断规则:

  • 从数据的最早版本开始判断undolog
  • 数据版本的trx_id==create_trx_id本事务修改可以访问
  • 数据版本的trx_id
  • 数据版本的trx_id>max_trx_id,这个版本是生成ReadView之后才开启的事务的建立,不可以访问
  • 数据版本中的trx_id在min_trx_id和max_trx_id之间,则需要看trx_id是否在m_ids之中,如果在不可以,如果不在则可以
  • 如果单前版本不可见,就在undolog中找下一个版本

mysql update执行过程?

  • 客户端发起请求
  • server层将数据修改
  • 存储引擎将修改结果保存到内存中
  • 存储引擎记录redolog,并将这条记录的状态设为prepare
  • 存储引擎告知server层,进行事务提交
  • server层记录binlog
  • server层提交事务
  • redolog将这条记录状态,由prepare变为committed

服务崩溃回复的时候如何判断是否提交事务

  • redolog 和 binlog中都无此记录,则回滚
  • binlog中无数据,redolog中有记录但是prepare状态,则回滚
  • binlog中有此记录,redolog中prepare状态,则提交
  • binlog中有此记录,redolog有记录,已为commit状态,不处理因为已提交

mysql数据库优化案例?

连接层面

  • 使用druid连接池,减少客户端使用连接数
  • 增大服务器可连接数,也就是最大并发数,尝试修改
  • 设置链接过期时间,尽快回收连接

架构层面

  • redis缓存
  • 基于主从复制实现的读写分离
  • 分库分表:垂直分库/水平分库

优化查询

  • 判断某条数据是否存在利用 索引 limit
  • 记录慢查询日志,默认是关闭的

表结构和存储引擎的优化

  • 字段类型能用enum枚举则用枚举,充分利用 tinint
  • 避免回表
  • 计量用not null
  • 存id,文件,利用文件ID,避免blob
  • sql中小表写在前面,小表驱动大表,因为要把前面表的数据先缓存

mysql中B+树的优点

B+树的特点

  • 关键字的数量和路数是相同的
  • B+树的根节点和支接点中都不会存储数据,只有叶子节点存储数据
  • B+树的每一个叶子节点增加了指向相邻节点的指针,他的最后一个数据会指向下一个叶子节点的第一个数据,形成一个有序链表结构

优势:

  • 每个节点存储更多关键字,路数更多
  • 扫库扫表能力更强,全表扫描只需遍历所有叶子节点
  • B+树磁盘读写能力更强,因为根节点和枝节点不存储数据,所以一个节点可以保存更多关键字,一次磁盘加载的关键字更多
  • 排序能力强
  • 效率更加稳定,叶子结点拿数据

请你说下对InnoDB索引数据结构的理解?

高手回答:
InnoDB索弓|的数据结构用的是B+树。为什么要用B+?
InnoDB索引肯定会有1个聚集索引,聚集索引默认是主键,、然后是非空的唯一索引, 最后是隐藏列rowid。
聚集索引的存储方式为叶子节点有完整的数据,而非叶子节点,只存有索引值。
那么每页存的数据也就更多,内存跟磁盘交互的单位为页。每页的数据越多,那么就能减少跟磁盘的交互次数,整体上提升速度。
同时,因为真实数据都在叶子节点,所以sq|语句的查询路径都是一样长。 查询稳定。
为什么不用二叉查找树?因为二叉树会有斜树的情况出现,会退化成链表,不够平衡。
为什么不用红黑树?同样的,路数比较少,深度会随着数据量的提升而提升。速度会越来越慢。同时也不够平衡。

为什么不用B tree?

B树跟B+最大的一个区别,B树每个节点都有真实数据,那么每页存的数据就越少,查询数
据跟磁盘的交互也就越多,同时,索弓|树的高度也就越高,查询的链路也会越长,整体查询会慢。还有每个节
点都有真实数据。查询数据就不稳定,有些在索引第-层就能查到,有些要查到
索引最后一层

MylSAM和InnoDB引擎的区别

答:主要区别:
MylSAM是非事务安全 型的,而InnoDB是 事务安全型的。
MylSAM锁的粒度是表级 ,而InnoDB支持行级锁定。
MylSAM支持全文类型索引,而InnoDB不支持全文索弓|。
MylSAM相对简单,所以在效率上要优于InnoDB ,小型应用可以考虑使用MyI5AM。
MylSAM表是保存成文件的形式,在跨平台的数据转移中使用My|5AM存储会省去不少的嘛烦。
0 InnoDB表比MyI5AM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表( alter table tablenametype=innodb )。
●应用场景:
MylSAM管理非事务表。它提供高連存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MylSAM是更好的选择。
InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB ,这样可以提高多用户并发操作的性能。

.数据库索引的优缺点以及什么时候数据库索引失效

答:●索引的特点
(可以加快数据库的检索連度
(降低数据库插入、修改删除等维护的連度
(只能创建在表上,不能创建到视图上
( 既可以直接创建又可以间接创建
(可以在优化隐藏中使用索弓|
(使用查询处理器执行5QL语句 ,在一个表上,- -次只能使用一个索引
●索引的优点
(创建唯一-性索引,保证数据库表中每一-行数据的唯- -性
( 大大加快数据的检索連度,这是创建索引的最主要的原因
(加連数据库表之 间的连接,特别是在实现数据的参考完整性方面特别有意义
(在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
(通过使用索引,可以在查询中使用优化隐藏器, 提高系统的性能
●索引的缺点
(创建索引和维护索引|要耗费时间,这种时间随着数据量的增而增加)
(索引需要占用物理空间,除了数据表占用数据空间之外,每-个索引还要占-定的物理空间,如果建立聚簇索引,那么需要的空间就会更大)
(当对表中的数据进行增加口、 删除和修改的时候,索引也需要维护,降低数据维护的連度)
●索引分类
(直接创建索弓|和间接创建索引)
(昔通索弓|和唯一-性索引)
(单个索引和复合索弓|)
( 聚簇索弓|和非聚簇索引)
●索引失效
(如果条件中有or ,即使其中有条件带索引也不会使用(这就是问什么尽量少使用or的原因)
(对于多列索引,不是使用的第一 部分,则不会使用索引
(like查询是以 %开头)
(如果列类型是字符串 , 那一定要在条件中使用引号弓起来,否则不会使用索弓|)
(如果mysq|估计使用全表扫秒比使用索弓|快,则不适用索引)

3、MyBatis

MyBatis工作原理?

  • 内部封装了JDBC,开发时只关注SQL本身,不需要处理驱动、连接
  • 使用XML和注解来配置和映射原生消息,将POJO映射成数据库中的记录
  • 通过XML或者注解将要执行的各种statement配置起来,并通过java对象和statement中的sql的动态参数映射成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象返回

#{}和${}的区别?

  • #{}预编译处理,${}字符串替换
  • MyBatis在处理#{}时,会将sql中的#{}替换为?号,调用PrepareStatement中的set方法来赋值
  • MyBatis在处理 时 , 将 {}时,将 {}替换成变量的值
  • #{} 可以有效的防治sql注入,提高系统安全性

MyBatis通常一个xml映射文件都会写一个DAO接口与之对应,这个DAO接口的工作原理是什么?

DAO接口即Mapper接口,接口的全限名就是映射文件中的namespace的值,接口的方法就是映射文件中statement的id值,接口中的参数就是传递给sql的参数
Mapper的工作原理:
JDK动态代理,MyBatis的运行时使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,将sql结果返回

MyBatis的一级二级缓存?

  • 一级缓存,基于PerpetualCache的HashMap本地缓存,其存储作用域为session,当session flush或close后,该session中所有cache就得清空,默认打开一级缓存
  • 二级缓存与一级缓存存储机制相同,默认也是采用PerpetualCache HashMap存储,不同在于其存储作用域为 Mapper(NameSpace),并且可以自定义存储源,如Ehcache,默认不打开二级缓存,要开启二级缓存,需要二级缓存属性类实现Serializable序列化接口,在映射文件中配置 cache
  • 对于缓存数据更新机制,当某一个作用域进行了CUD操作后,默认该作用域下所有select中的缓存将被clear

MyBatis插件原理。使用mybtais插件如何数据脱敏?

在mybtis中对外提供了4大对象供开发者拦截,分别是
Excutor:查看mybatis的sql执行过程;
ParameterHandler修改他的参数;
ResultSetHandler拦截返回值;
StatementHandler进行分页
开发者自己定义一个类,实现Interceptor接口,实现intercept方法,在类上通过@Intercepts注解和@Signature选择拦截哪个类中的哪个方法,比如:@Intercepts(@Signature(type = ResultSetHandler.class, method = “handleResultSets”, args = Statement.class)),然后那自定义拦截器的类配置到mybatis的配置文件中。在加载mybatis的时候会去在执行到ResultSetHandler的时候生成拦截器器的链,后面去调用会走拦截器
数据脱敏我们就需要去拦截ResultSetHandler对象修改返回值,在这个里面可以去做数据脱敏处理,可以通过反射获取对象的属性,判断哪些字段需要进行脱敏处理,如果需要脱敏处理,直接通过filed.setAccessibe(true)暴力反问,修改属性值

MyBatis只需要声明接口即可调用SQL是怎么做到的?JDK动态代理和CGLib代理区别?

参考思路:mybatis是通过对dao接口进行代理,在启动mybatis的时候,会去加载在mybtais配置文件中配置的mapper文件,mapper文件中的每个sql语句都封装成一个statement,然后通过mapper文件中的namespace去得到到接口然后给他生成代理的工厂类放入到map中,注入mapper接口类时从该map中拿出对接口代理的对象,在代理对象中的invoke方法中除了object的方法,其他的所有方法都直接去找到相应的statment,然后去执行sql语句;
Jdk动态代理的类必须要实现接口,并且不能是final修饰的类,方法不能是非public的方法,在生成代理类时快,只生成一个代理文件,生成的代理类回去实现目标类实现的接口,运行时通过反射调用目标类的方法,调用时慢,cglib是使用asm框架来生成代理类,目标类无须实现接口,生成的代理类会继承目标类,不能是final修饰的类,方法不能是非public,由于生成了几个类文件,所以生成时慢,之所以有几个类文件,因为cglib在生成代理类的同时,会为目标类的每个方法都生成一个相应的index,通过index直接定位到方法,直接调用,所以调用时快,在spring中会判断如果有接口就使用jdk代理,如果没有接口就使用cglib代理

4、redis

redis一共有几种数据类型

  1. String 二进制安全的,可以存储 int整型、float单精度浮点型、string字符串
    SDS: 字符数组 预分配内存
    存储原理:key=>SDS value=>redisObject=>SDS

三种编码方式?

  • int 存储8个字节的长整型
  • embstr:SDS 存储小于44字节的字符串
  • raw:SD 存储大于44字节的字符串

key 可以分层

  1. hash: String能做的hash都能做
  2. list 列表
  3. set 集合
  4. zset 有序集合

什么是缓存穿透?如何避免?

缓存穿透:一般的缓存系统都是按照key去查询,如果不存在对应的value,就会去数据库查找,一些恶意的请求会故意查询不存在的key,请求量很大,就会造成数据库压力

  • 对查询结果为空的情况也进行缓存,川村时间设置短一些
  • 对一定不存在的key进行过滤,可以把所有可能存在的key放在一个bitMap中,查询时通过bitMap过滤

什么是缓存雪崩雪崩?如何避免?

缓存雪崩:当缓存服务器重启,或者大量缓存在某一个时间段同时失效,会给后端数据库带来压力,导致崩溃

  • 缓存失效后,通过加锁或者队列来控制读取数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据写缓存,其他线程等待
  • 做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效后可以访问A2,A1过期时间短,A2过期时间长
  • 不同的key设置不同的过期时间,缓存失效尽量均匀

redis缓存架构如何实现?

  • 只放热数据
  • 所有缓存都应放置过期时间,过期时间分散
  • 缓存key的可读性,key缩写
  • 数据结构的选择
  • 一个key对应的数据不应过长
  • 避免缓存穿透和雪崩
  • 缓存预热
  • 缓存与数据库的读写顺序
  • 数据一致性问题,是主动更新还是被动更新

缓存和数据库不一致咋办?

一般是出现在主从分离、读写分离的数据库
在从库有数据更新之后,将缓存中的数据也同步更新,当主库发生了数据更新之后,向缓存发出删除、淘汰这段时间写入的旧数据

如何实现分布式锁?(可以引申到zk锁)

  • 线程A setNx(上锁的对象,超时时的时间戳t1),如果返回true,获得锁
  • 线程B 用get获取t1,与当前时间戳比较,判断是否超时,没有超时则false,若超时执行第三步
  • 计算新的超时时间t2,使用getSet命令返回t3(该值可能被其他线程修改过,如果t1==t3获得锁,如果t1!=t3 说明已经被其他线程获得锁)
  • 获得锁后,处理完业务逻辑,再去判断是否超时,如果没超时删除锁,已超时不用处理,可能被其他线程获得锁

redis是单线程的为什么这么快?

  • 单线程是请求单线程,减少了线程不断创建带来的性能消耗,不会出现线程的频繁上下文切换,不会出现线程竞争问题,无锁
  • 处理请求是多线程的
  • 同步非阻塞的,多路复用,多个TCP连接复用一个或多个线程
  • 影响redis速度的不是 单线程只能使用单CPU,他的瓶颈不是CPU,而是内存
  • 纯内存,所有数据结构都在内存中

空间的惰性释放?或者过期策略

  • 定时检查删除-立即过期
  • 惰性检查删除
  • 定期检查删除

内存淘汰策略?

  • 不处理 noeviction 默认配置
  • 所有key中挑选淘汰
    allkeys-random 随机
    allkeys-lru 所有key中最近最少
    allkeys-lfu 所有key中最少使用
  • 过期key中淘汰
    valatitle-random
    volatile-lru
    volatile-lfu
    volatile-ttl 可存活时间最短,快过期里面挑选

Jedis 与 Redisson 对比有什么优缺点?

答:Jedis 是 Redis 的 Java 实现的客户端, 其 API 提供了比较全面的 Redis 命令的支 持;Redisson 实现了分布式和可扩展的 Java 数据结构,和 Jedis 相比,功能较为简单, 不支持字符串操作, 不支持排序、事务、管道、分区等 Redis 特性。Redisson 的宗旨是 促进使用者对 Redis 的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑 上

5、Dubbo

dubbo一般使用什么注册中心?还有别的选择吗?

推荐使用Zookeeper作为注册中心,还可以使用redis等,但不推荐

服务提供者实现失效踢出是什么原理?

基于zookeeper的临时节点原理

默认使用什么通信框架,还有别的选择吗?

默认也是推荐使用netty框架,还有mina

默认使用序列化框架,还有哪些?

推荐使用Hessian序列化,还有Dubbo、FastJson、java自带序列化

dubbo推荐使用什么协议?

  • dubbo 推荐
  • rmi
  • hessian
  • http
  • webservice
  • thrift
  • memcached
  • redis
  • rest

dubbo集群 容错方案有几种?

  • Failover 失败自动切换,自动重试其他服务器 默认
  • Failfast 快速失败,立即报错,只发起一次
  • Failsafe 失败安全,出现异常时,直接忽略
  • Failback 失败自动恢复,记录失败请求,定时重发
  • Forking 并行调用多个服务器,一个成功即返回
  • Broadcast 广播逐个调用所有提供者,任意一个报错则报错

dubbo服务降级,失败重试怎么做?

可以通过 dubbo:reference 中设置 mock=“return null”。mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “ 接口名称 +Mock” 后缀。然后在 Mock 类里实现自己的降级逻辑

dubbo框架设计服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提 供方和服务消费方的业务设计对应的接口和实现。

 配置层(Config):对外配置接口,以ServiceConfig和 ReferenceConfig 为中心。
 服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub 和服务器端 Skeleton。
 服务注册层(Registry):封装服务地址的注册与发现,以服务URL 为中心。
 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册 中心,以 Invoker 为中心。
 监控层(Monitor):RPC调用次数和调用时间监控。
 远程调用层(Protocol):封将RPC调用,以Invocation和Result
为中心,扩展接口为 Protocol、Invoker 和 Exporter。
 信息交换层(Exchange):封装请求响应模式,同步转异步,以
Request 和 Response 为中心。
 网络传输层(Transport):抽象mina和netty为统一接口,以
Message 为中心。

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

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

Dubbo 的核心功能?

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

6、ZooKeeper

ZAB协议?

ZAB协议是分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议
ZAB协议包含两种基本的模式:崩溃恢复和消息广播
当整个zookeeper集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与Leader服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式,首先选举产生新的Leader服务器进行数据同步,当集群中超过半数机器与该Leader服务器完成数据同步后,退出恢复模式进入消息广播模式,Leader服务器开始接收客户端的事务请求生成事务提案来进行事务请求处理

四种类型的数据节点?

  • 持久节点:除非手动删除否则节点一直在 PERSISTENT
  • 临时节点:临时节点的生命周期与客户端会话绑定,一旦客户端会话失效,这个客户端创建的所有临时节点都会被移除 EPHEMERAL
  • 持久顺序节点:增加了顺序属性 PERSISTENT_SEQUENTIAL
  • 临时顺序节点:节点名后边追加一个由父节点维护的自增整型数字

你可能感兴趣的:(笔记,分布式与高并发,数据库,mysql,面试)