目录
一、java基础
二、多线程相关
三、JVM
四、Mysql面试题
五、Redis 面试题
面向对象
封装:
明确标识 允许外部使用的所有成员函数和数据项
继承:
继承父类方法,并作出子类的该表和扩展
多态:
基于对象所属不通的类,外部调用同一个方法,实现逻辑不同
重载和重写
重载:
发生在同一个类中,方法名相同,参数类型不同、个数不同、顺序不同、方法返回值和访问修饰符可以不同,发生在编译时
重写:
发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类、抛出异常范围小于等于父类,访问修饰符范围大于等于父类,如果父类修饰符类型为 private则子类不能重写方法
java数据类型
基本数据类型:(4类8种)
整数类型:
byte 1字节存储范围 -128 ~127
short 2字节存储范围 -32768 ~ 32768
int 4字节存储范围 -2 31次方 ~ 2 31次方-1 正负21亿
long 8字节存储范围 -2 63次方 ~2 63次方-1
浮点类型:
float 4字节存储范围 -3.403E38次方~ 3.403E38次方 单精度
double 8字节存储范围 -1.798E308次方~1.798E308次方 双精度
字符类型:
char
布尔类型:
boolean 1位 只有 true false
引用数据类型:
类
接口
数组
&& 和 || 区别
&& 短路与 两边表达式 只有有一个是false 整体结果是false 两边表达式 从左向右比 如果左边的是false 右边的就不需要判断
|| 短路或 两边表达式两边表达式 有一个是true 整体结果是true
数组:
特点:
数组存储的必须是同一类型的元素,可以是基本数据类型,也可以是引用数据类型
数组在定义的时候,必须给定初始化大小,大小不可变
数组是通过下标来获取数据的,下标是从0开始的
数组是有序集合,不是指代码大小,是插入顺序
使用:
创建声明数组
分配空间
赋值
数组操作
1、HashMap 原理
1:HashMap的数据结构是 数组 +(链表或红黑树)
数组特点: 查询效率高、插入删除效率低
链表特点: 查询效率低、插入删除效率高
在HashMap底层使用 数组 +(链表或红黑树) 解决了数组+链表的问题,使查询、插入、删除效率都很高
2:HashMap的存储过程
根据key的hashcode 来获取元素在数组中的位置、判断当前位置是否有其他元素,如果有其他元素,使用equals比较两个元素是否相等
如果相等则直接覆盖,如果不相等,使用链表的结构存储元素 当链表长度到8 数组长度大于64的时候会讲链表转成红黑树 否则会调用 rehash进行扩容
3:为什么会由链表转成红黑树
当链表元素过多导致查询效率降低
4:HashMap重要的参数:
初始化容量: 默认 16
负载因子 : 默认 0.75
使用 初始化容量 乘以负载 因子 会得到一个值,当数组中的元素数量超过这个值的时候 会进行rehash 讲数组中的容量增加到原来的2倍
在做扩容的时候会重新生成一个新的数组,把原来所有数据要重新计算哈希码值重新分配到新的数组,所以扩容非常好性能
为什么负载因子是0.75:
如果负载因子 是 1的话,在扩容的时候 就避免不了 hash碰撞
如果负载因子是 0.5的话 空间利用率会降低
0,75 的话 提升了空间利用率
2、HashMap 和 Hashtable 的区别
1:hashMap 方法没有 synchronized 修饰 线程是不安全的 hashtable 是线程安全的
2:hashMap 允许 key和value 为null hashtable 不允许
3:hashMap 底层是 组数 +链表+红黑树
4:度到8 数组长度超过64的适合 会转换成红黑树,元素已内部类node节点存在
5:key的hash值 是两次hash 对数组长度取模,对应到数组下标
6:产生hash冲突,直接创建node存入
7:生了hash冲突,使用equal 比较,相同则替代元素,不同判断链表高度 插入链表 链表高度到8 数组长度超过64时 转成红黑树,长度低于64 将红黑树转会 链表
8:null 存在下标0的位子
3、String 和 StringBuffer、StringBuilder 的区别是什么
1:String 底层是final 修饰 不可改变
2:StringBuffer和StringBuilder 可以对字符串进行改变,但是 StringBuffer 是线程安全的 底层使用synchronized 修饰了方法, StringBuilder是线程不安全的
4、Integer和 int 区别
1:Integer 是包装类型,使用Integer的时候必须赋初始值, new Integer 的时候实际上是生成了一个指针指向对象 默认是 null
当定义个 Integer i =127 的时候 实际上是调用了Integer 的valueof方法,判断 值是否在 -128 到127之间,如果在的话,就直接从缓存里 拿,如果不在,new一个新的对象
2:int 是基本数据类型, 不需要实例化 默认是0
答: int 是基本数据类型 Integer 包装类型 int 在使用的时候默认是0 Integer 实际是生成了一个指针指向对象 默认是null 当定义Integer =127的时候 实际是调用了 Integer的valueof 判断 是否在 -128 到127之间,如果在 直接使用,不在就new一个新对象
5、==和equals 区别
1:==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址
2:equals object 也是用==比较 通常会重写
6、final 关键字
1:用来修饰一个引用,如果引用为数据类型,表示这个引用为常量,不能修改
2:如果作用在 对象或数组上那这个对象或数组的本身可以修改,但指向这个对象或数组的地址的引用不能修改
3:如果是成员变量、必须赋值,否则编译报错
7、Object类的常见方法
clone()
equals()
getClass()
hashCode()
notify()
notifyAll()
wait()
8、Java 中的异常处理
9、接口和抽象类的区别是什么
接口和抽象类都不能被实例化,如果想实例化,抽象类必须指向所有抽象方法的子类对象,接口必须指向实现所有接口方法的子类对象
抽象类被继承,接口被实现
接口只能做方法的声明,抽象类可以做方法的声明,也可以做方法的实现
接口里定义的变量是公共常量,抽象类中的变量是普通变量
接口的目的:
是对类进行约束,只约束了行为的有无,不对实现行为进行限制
抽象类的设计目的:
代码复用,当不同类有了相同的方法,可以派生出一个父类,抽象类 可以有实现方法,也可以有未实现的方法,,抽象类不允许实例化
10、Arraylist 与 LinkedList 区别
1、ArrayList于 数组实现,在内存中是连续存储的,适合下标查询, 数组长度是固定的,每次扩容的适合会重新生成一个新的数组,将旧的数据拷贝到新 的数组中,
2、LinkedList 是链表存储,内存是分散的,适合做数据的插入 和删除,
11、HashMap 的长度为什么是2的幂次方
1:为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀
12、ConcurrentHashMap线程安全的具体实现方式/底层具体实现
1: jdk1.7之前 底层是Segment分段所加数组结构组成的,就是讲数据分成一段一段的存储,然后每一段都上一把锁
当一个线程用锁访问一段数据的时候,其他线程也能访问到其他段的数据
查询元素,二次hash 第一次hash 定位segment位置,第二次hash定位院所所在的链表的头部
get 方法,不需要加锁,有volatile保证
2: jdk1.8 取消了Segment分段所,采用CAS 和synchronized 来保证线程安全
数据结构跟HashMap1.8一样,使用 数组,链表红黑树
synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率提升N倍。
put过程:
对于ConcurrentHashMap的数据插入,这里要进行两次Hash去定位数据的存储位置。Segment实现了ReentrantLock,也就带有锁的功能,当执行 put操作时,会进行第一次key的hash来定位Segment的位置,如果该Segment还没有初始化,即通过CAS操作进行赋值,然后进行第二次hash操作, 找到相应的HashEntry的位置,这里会利用继承过来的锁的特性,在将数据插入指定的HashEntry位置时(链表的尾端),会通过继承 ReentrantLock的tryLock()方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋 的方式去继续的调用tryLock()方法去获取锁,超过指定次数就挂起,等待唤醒
13、
14、如何实现数组和 List 之间的转换?
数组转集合 : Arrays.asList(strs)
集合转数组 : String[].toArray()
15、在一个静态方法内调用一个非静态成员为什么是非法的?
静态成员变量或方法,在类加载的时候就会分配内存空间,可以直接通过类名访问
非静态成员变量或方法,只有在类产生实例化的时候才会分配内存空间,然后通过类的对象去访问
16、List 基本实现有哪些
ArrayList
数组实现 根据索引查询数据,速度笔记快
LinkedList
双向链表实现,增删比较快
vactor
动态数组实现
21、Java程序初始化顺序
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类非静态变量
父类非静态代码块
父类构造方法
子类非静态变量
子类非静态代码块
子类构造方法
21、java 中有多少种方法可以创建对象
22、想要拿到一个类的class对象 有几种方式
this.getClass()
Class.forName()
new ClassName().getClass()
23、hashCode 和equals 区别
hashCode 作用是获取哈希码,也称作散列码,实际返回的是int整数
equals 作用 如果没有被重写的情况下 是判断两个对象的地址是否相等,如果被重写过,比较的是两个对象的值是否相等
equals 相同 hashCode 一定相等, hashCode相等 equals不一定相等
24、list 和set 区别
相同点
list 和set 都继承了 collection
list 有序的 按对象的进入顺序保存,允许有多个null值,可以使用iterator去除所有元素,在逐个遍历,还可以使用get方法获取指定下标的元素
set无序,不能重复,允许有一个null值,取元素使用iterator接口取所有元素,在数个遍历
25、ArrayList和LinkedList怎样实现线程安全
ArrayList:
Collections.synchorizedList 和 CopyOnWriteArrayList
synchorizedList 使用了同步锁 比CopyOnWriteArrayList 要快
CopyOnWriteArrayList 使用了锁加复制数组 读取速度更快,并发性能好
LinkedList:
使用 CurrentLinkedQueue来代替LinkedList
ArrayList:
基于动态数组,连续内存存储,适合下标访问,
扩容机制:
数组长度是固定的,超出长度存储数据时,需要新建数组,然后将老数组的数据,拷贝到新数组中,如果不是尾部插入数据,还会涉及到元素移动,
使用尾部插入并且指定初始容量可以极大提升性能,
LinkiedList:
基于链表实现,可以存储在分散的内存中,适合做插入及删除,不适合查询,查询时需要逐个遍历,LinkedList必须使用iterator不能使用for循环,每次get获取元素时,都需要对list重新遍历,性能消耗极大
26、常用的数据结构
1:数组
2:列表
3:链表
4:树
5:
27、什么时候需要重写父类的方法
1、实现接口,要重写
2、继承抽象类 要重写抽象类中的方法
3、当父类方法 满足不了子类实现时 需要重写
28、面向对象的设计原则
1、单一职责
2、开闭原则
3、依赖倒置原则
4、接口隔离
5、里氏替换
6、迪米特原则
29、jdk1.8中对 hash算法 和寻址算法如何优化
hash算法优化
先拿到 key的hashcode 然后根据key的hashcode 右移16位 (把高16位移到了低16位,高16位 用0补全) 做一个 亦或运算
30、java中的异常体系
父类Throwable
Throwable 下两个子类 Exeption 和Error
Error 是程序无法处理的报错 程序被迫停止
Exeption 不会导致程序停止 又分为RunTimeExepotion运行时异常 和CheckedExeption 检查时异常
RunTimeExepotion是运行时发生的会导致当前线程执行失败
CheckedExeption 通常发生在编译期间
31、error 和exception 区别
error 是 ,Java虚拟机运行错误
exception 一般是由程序逻辑错误引起的
32、遇到过的设计模式
1:代理模式,mybatis中用到jdk动态代理生成 mapper的代理对象,在执行代理对象的方法是会执行sql、spring中aop、@Configuration注解的底层代码用到了代理
2:责任链模式:Tomcat中的pipeline 以及Dubbo中的Filter机制都使用了责任链模式
3:工厂模式:spring中的BeanFactory 就是一种工厂模式的实现
4:适配器模式:spring中的Bean销毁的生命周期中用到了适配器模式,用来适配各种Bean的销毁
33、深拷贝 和浅拷贝的区别
1:浅拷贝是指,只会拷贝基本数据类型的值,以及示例对象的引用地址,不会复制引用地址所指向的对象
2:深拷贝是指,既拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行拷贝
34、谈谈ConcurrentHashMap的扩容机制
1.7版本
1:1.7版本的ConcurrentHashMap基于Segment分段实现
2:每个Segment相对于一个小型的HashMap
3:每个Segment内部进行扩容,和HashMap的扩容逻辑类似
4:先生成新的数组,然后转移原数组到新数组中
5:扩容的判断也是每个Segment内部单独判断的
1.8版本
1:1.8版本ConcurrentHashMap 不在基于Segment分段锁来实现,基于数组 链表 红黑树实现
2:当某个线程put时,如果发现ConcurrentHashMap正在进行扩容 会一起扩容
3:当某个线程put时,发现没有正在扩容,则将key-value添加到ConcurrentHashMap中,然后判断是否超过了阈值,超过了进行扩容
4:ConcurrentHashMap是支持多个线程同时扩容的
5:
35、泛型中 extends 和supper 的区别
1: extends T> 表示 包括T在内的任何T的子类
2: supper T> 表示 包括T在内的任何T的父类
1、线程与进程的区别
2、创建线程的3种方式
1:new Thread()
2:实现Runnable接口
3:实现Callable接口
3、线程的状态:
New 新建一个线程对象
Runnable 可运行状态
Running 运行状态
BLOCKED 阻塞状态
DEAD 死亡状态
3、线程池的创建
newSingleThreadExecutor() 创建单个线程池
newFixedThreadPool(3) 固定数量的线程池
newCachedThreadPool() 带缓存的线程池
newScheduledThreadPool(2) 定时线程池
newThreadPoolExecutor() 自定义线程
3、cas的原理
1:cas 实现
4、synchronized 锁升级。过程
要锁定某一个对象,在对象头上的某两位会记录这把锁,还会记录当前线程的id
偏向锁概念是:
线程第一次进入的时候,先不尝试对对象加锁,只记录当前线程的id,第二进入的时候,判断线程线程id是否一样,如果一样直接使用,
如果不一样,进行锁升级,偏向锁升级成为轻量级锁,假如线程获取锁失败,进入自选状态,默认是10次尝试获取锁,如果获取锁失败,将轻量级锁升级成为重量级锁经过os进入等待队列
cas主要占CUP 重量级锁会产生线程切换, 所以cas比重量级锁快
5、synchronized 实现原理
首先被synchronized修饰的方法或代码块 在编译前后会生成monitorenter 和 monitorexit 两条指令
当虚拟机执行到monitorenter 首先尝试获取锁,如果这个对象没有锁,或者当前对象已拥有锁对,把锁的计数器+1
当执行道monitorexit的时候,会将锁计数器-1,当锁计数器为0时,就被释放了
当获取锁对象失败的时候,会进行阻塞直到对象锁被另一个线程释放为止
5、volatile相关
1:保证线程可见性,不保证原子性
保证了,一个线程对变量的改变,另一个线程就能马上看到
MESI 使用了Cup的缓存一直性协议
2:禁止指令重排序
6、synchronized 和valatile 区别
1:synchronized 保证原子性。可见性
synchronized 还会创建一个内存屏障,保证了所有CPU操作后的结果直接会刷新的主内存
2: volatile 保证了可见性 不保证原子性 禁止指令重排序
3: volatile作用在变量上,synchronized 作用在类,方法,变量上
4: volatile 不会造成线程阻塞,synchronized 可能会造成线程阻塞
7、谈谈-synchronized和reentrantlock-的区别
1:synchronized 是java自带的 jvm层面的锁 reentrantlock 是java类
2:synchronized 无法判断获取锁的状态, lock 可以判断锁状态,也可以尝试主动获取锁(不要了)
3:synchronized 会自动枷锁和释放锁,lock需要手动枷锁和释放锁,在finally里释放
4:被sybchronized 修饰的方法,在多线程环境下,一个线程获取到锁,另外一个线程进入等待,lock锁可以直接结束
5:synchronized 可重入、非公平,不可中断的锁,lock是可重入,可判断,可公平的锁
8、核心线程池ThreadPoolExecutor的参数;
1:corePoolSize:核心线程数
2:maximumPoolSize:线程中最大线程数量
3:keepAliveTime:允许的空闲时间
4:unit:时间单位
5:workQueue:任务队列
6:threadFactory:线程工厂
7:handler:拒绝策略
abort 报异常
discard 悄悄扔掉
discardold 最先的任务扔掉
callerRuns 谁提交任务谁执行
9、AQS理论的数据结构
1、调用lock 方法 底层实现是 先调用sync.acquire()方法
2、判断是否上锁 调用 tryAcquire()方法
内部调用nonfairTryAcquire()方法
调用getState() 获取所得状态
如果没有上锁 执行上锁流程
如果上了锁 走重入流程
3、如果没有拿到锁 调用 acquireQueued方法 获取队列 排队 addWaiter(Node.EXCLUSIVE) 添加一个排他的等待着
内部有3个对象:
1:state 用于计数器,类似gc回收的计数器
2:线程标记 记录当前线程是谁加得锁
3:一个阻塞队列
10、java线程同步都有哪几种方式
1:synchronized
2:wait和notify
3:volatile
11、cas 实现原理
12、volatile实现的原理
1:当对volatile变量进行写操作的时候,jvm会像处理器发送一条lock指令,将这个缓存中的变量回显到主内存
2:在多线程环境下,为了保证各处理器缓存是一致的,就会实现缓存一致性协议
3:缓存一致性协议:每个处理器,通过嗅探在总线程上传播的数据来检查自己的缓存是不是过期了,当处理器发现自己的缓存行对应的内存地址被修改了,
就会将当前的缓存行设置为无效,当处理器对这个数据进行修改的时候,会强制充系统内存里把数据读到处理器缓存里
13、wait/await和sleep区别
1:sleep 方法没有释放锁,而 wait 方法释放了锁 。
2:两者都可以暂停线程的执行。
3:wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
4:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法 执行完成后,线程会自动苏醒
14、ThreadLocal原理
内部维护了一个ThreadLocalMap 调用 get set 都会操作 这个map ThreadLocalMap 是弱引用, 垃圾回收器执行就会回收, value 是强引用
1、ThreadLocal是一个线程本地存储机制,可以利用这个机制 将数据缓存到某个线程,这个线程可以在任意时刻和任意方法中获取缓存的数据
2、ThreadLocal底层通过ThreadLocalMap来实现,每个Thread都存在一个ThreadLocalMap中,Map的Key 是ThreadLocal对象,Map的值是需要缓存的值
3、如果在线程池中使用ThreadLocal会造成内存泄漏,因为ThreadLocal对象使用完之后,应该要把线程池中的key value 回收,手动调用remove方法
15、说说线程池的底层工作原理?
1:创建线程池后,等待提交过来的任务
2:在调用execute()方法添加一个请求任务时,会先判断
正在运行的线程数 小于核心线程数 创建核心线程 执行任务
正在运行的线程数 大于或等于核心线程数 放入队列
队列满了,正在运行的线程数 小于最大线程数量,创建非核心线程 执行任务
队列满了,正在运行的线程数 大于或等于最大线程数量,执行拒绝策略
3:当一个线程完成任务时,它会从队列中取下一个任务来执行
4:当一个线程无事可做超过一定的时间(keepAliveTime)时,线程池会判断
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小。
16、什么是线程池 ?线程池的作用是什么 ?
19、Java 中默认实现的线程池有哪些?它们的区别 ?
20、如何在 Java 线程池中提交任务 ?它们有什么不同 ?
21、常见的线程拒绝策略有哪些 ?
1、报异常
2、扔掉最先的任务
3、谁的任务谁执行
4、悄悄扔掉
22、常见的阻塞队列有哪些 ?
23、为什么建议手动创建线程池 ? Java 提供的线程池有什么缺点 ?
24、JUC了解哪些类
25、AtomicInteger 底层原理
cas 原理 底层调用 compareAndSet()方法
26、对守护线程的理解
27、java 中 如何避免死锁
死锁的四个条件
资源互斥
使用ThreadLocal 打破资源互斥,让资源独立
资源不可抢占
持有资源并不去释放
互相等待
打破任意一个条件 都行
28、如果你提交任务时,线程池队列已满,这时会发生什么
1:如果使用无界队列,那么可以继续提交任务
2:如果使用有界队列,提交任务时队列满了,如果没有达到上限,那么增加线程数,如果线程数已达到最大值,使用拒绝策略
29、并发编程的三要素
1:原子性 不可分割的操作,多个步骤要保证同时成功 或同时失败
2:有序性 程序执行的顺序 和代码执行的顺序要一致
3:可用性 一个线程对共享变量的修改 另一个线程要知道
30、简述线程池,FixedThreadPool用的阻塞队列是什么
底层使用了LinkBlockingQueue 无界的阻塞队列
31、volatile 如何保证 可见性 和有序性的
对 volatile 修饰的变量进行 修改的时候 会CPU 会直接写入主内存,当读取的时候 会直接从主内存读取
volatile 通过内存屏障 来禁止指令重排 所以保证了有序性
32、sleep wait join yield 区别
1:sleep 是Thread类的方法 wait 是object 的本地方法
2:sleep 不会释放lock wait 会释放lock 而且会加入到等待池
3:sleep 不依赖synchronized wait 依赖synchronized
4:sleep 不需要被唤醒 wait需要被唤醒
5:sleep 一般用于线程休眠 或轮询停止, wait 用于线程间通讯
33、Thread 和Runable 区别
1:Thread 是个类 Runable 是个接口
2:Thread 实现了 Runable
34、并发 并行 串行的区别
串行;就是等着前一个任务 执行完,在执行后边的任务
并行:两个任务在同一个时间 一起执行,互不干扰
并发:允许两个任务彼此干扰,同一个时间点交替执行
35、并发的三大特性:
1:原子性 多线程的情况下 要么全部成功 要么全部失败
2:可见性
3:顺序性
36、线程池中阻塞队列的作用 是什么
1:一般的队列只能保证 一个长度的缓冲区,如果超出了这个长度,就无法保证当前任务,阻塞队列可以通过阻塞爆保留住当前想要执行的这个任务
37、为什么是先添加到队列中,而不是先创建最大线程
1:应为创建线程 是要获取全局锁 影响了整体的效率
38、线程池中 线程的复用原理
1:线程池将线程 和任务进行解耦,线程是线程,任务是任务,摆脱了之前的new Thread创建线程时,线程必须对应一个任务的限制
1、java类加载过程
1:加载
2:验证,验证文件是否符合jvm规范
3:准备,将静态变量 分配内存空间 赋初始值
4:解析,解析是将常量池内的符号引用转为直接引用
5:初始化,执行构造方法的过程
6:使用
7:卸载
2、描述一下 JVM 加载 Class 文件的原理机制
java类加载器是基于3个机制
1:委托机制,加载一个类的请求,首先先交给父加载器去加载,不能够找到这个类,在由子加载器完成
2:可见性,就是子加载器可以看见所有父加载器所加载的类,父加载器看不到子加载器锁加载的类
3:单一性,就是保证所有的类只被加载一次
3、Java类加载器有哪几种?
1:BootStrap Loader 引导类加载器,负责加载系统类
2:ExtClassLoader 扩展类加载器,负责加载扩展类
3:AppClassLoader 应用类加载器,负责加载应用类
4、类加载器之间如何协调工作的?
java采用委托模型机制来区分由哪个类加载器去加载,类加载器有加载类的需求时,会先调用parent方法去父类找,如果找不到,自己再去加载
5、ClassLoader中一些 重要的方法
1:getParent() 返回该加载器的父类加载器
2:loadClass(Stirng name) 加载名称为name的类,返回结果是java.lang.Class类的实例 实现双亲委派
3:findClass(String name) 查找名称为name的类,返回结果是java.lang.Class类的实例 用来复写加载
4:findLoadedClass(String name) 查找名称为name 已被加载的类,返回结果是java.lang.Class类的实例
5:defineClass(String,byte[] b,int off,int len) 把字节数组b中的内容转换成java类,返回结果是java.lang.Class类的实 例这个方法被final声明 本地方法,最终加载类只能通过defineClass
6:resolveClass(Class> c) 连接指定的java类
6、 Java 内存分配
1:堆 存放基本类型变量,和程序引用变量
2:栈 存放通过new生成的对象
3:方法区 线程共享区域
4:常量池 存放用final修饰的基本数据类型的常量值
5:静态域 存放类中以static声明的静态成员变量
6:程序计数器
7、如何判断一个对象是否存活?
1:引用计数法,给每个对象设置一个计数器,当计数器为0时表示对象死了
2:可达性算法,从一个GC Root的对象开始找,如果一个对象到GC Roots没有任何引用链相连接时,表示这个对象已死
3:GC ROOT有哪些
1:虚拟机栈中引用的对象
2:方法区中类静态属性引用的对象
3:方法区中常量引用的对象
4:本地方法栈中引用的对象
1:JVM stack
2:native
3:method
4:time
5:constant
6:poll
7:static
8:references
8、深拷贝和浅拷贝
浅拷贝:就是对象中的数据进行简单的赋值
深拷贝:就是对对象中存在的动态成员或指针可重新开辟内存空间
9、Java 中垃圾收集的方法有哪些
标记清除算法: 标记一些可以清除的对象,统一回收
缺点:
效率比较低
会产生大量不连续的内存碎片
复制清除算法: 将内存容量划分为相等的两个部分,然后将活着的对象复制到第二块内存上,然后整体清除第一块内存,在将第二块内存的对象复制带第一块儿内存中
有点:
提升效率
缺点:
每次都会浪费一块内存
标记整理算法:将可回收的对象,移动到一端,然后清除边界以外的对象,不会有内存碎片的产生
只要是为了解决标记清除产生大量内存碎片问题,也解决的复制算法的效率问题
分代收集算法:分为新生代和老年代,新生代对象生命周期短,每次回收都会有很多对象被回收,采用复制清除算法,
10、类加载器双亲委派模型机制
当一个类接收到类加载器的请求时,先向上委派一直委派到父类加载器查询缓存中 如果不存在,向下查找,查找加载路径是否存在,不存在会报错
好处:
主要是为了安全,避免程序员自己编写的类,动态替换java的一些核心类
避免了类的重复加载
11、 Minor GC 与 Full GC 分别在什么时候发生
新生代内存不够用时候发生 MGC 也叫 YGC,JVM 内存不够的时候发生 FGC
12、几种常用的内存调试工具
Jmap、
jstack、
jconsole、
jhat
jstack 可以看当前栈的情况,
jmap 查看内存,
jhat 进行 dump 堆的信息
mat(eclipse 的也要了解一下)
13、JVM有哪些垃圾回收器
新生代回收器:serila、 parNew、 parallel scavenge
老年代回收器: CMS、 serila old、 paralel old
整堆回收: G1
14、G1的内存模型讲一下
15、G1和CMS收集器的区别?以及G1收集器对的改进
16、JVM四种引入类型
1:强引用 只要强引用在,垃圾回收器就不会回收引用对象
2:软引用 在内存足够的时候 不会回收
3:弱引用 只要回收器执行,不管内存够不够 都会执行回收
4:虚引用 跟踪对象被垃圾回收器回收的活动。
17、jvm内存模型
堆
线程共享 存放的是对象实例,回收器主要回收的就是堆内存, 分为新生代,
新生代:
1:分三个区,eden区 survivor from 和survivor to区,
2:新new 出来的对象 会放到eden区, 当eden 区 和survivor from 区 进行过一次gc回收
会将存活的对象放入 survivor to 区 然后 对eden 区 和survivor from 区进行回收
3: 如果 survivor to 区 无法存储某个对象,会将这个对象存入 老年代
老年代:
存放的是在新生代 回收15次依然存活的对象
栈
私有线程
方法区
线程共享 存放的是被虚拟机加载后的 类信息,常量,静态变量 等数据
本地方法栈
私有线程 主要用来是调用native服务
程序计数器
私有线程 改变程序计数器的值,来确定程序的下一条指令
18、ClassLoader的原理
jvm启动时,运行bootstrap classloader 加载java 核心api (appclassloader 和 extclassloader) extclassloader 加载扩展包
appclassloader 加载CLASSPATH目录下定义的class文件
19、GC中root节点有哪些
20、CMS执行过程
1:初始化标记
需要停顿正在执行的任务,从跟对象开始扫描可以和跟对象关联的对象,进行标记
2:并发标记
在初始化的基础上继续往下追溯,应用程序的线程和并发标记的线程同时执行,用户无感知
3:并发预清理
虚拟机查找正在执行并发标记阶段新进入老年代的对象,减少下一个阶段 重新标记的工作
4:重新标记
暂停虚拟机,收集器线程扫描在CMS堆中剩下的对象
5:并发清理
清理垃圾对象,收集器线程和引用线程并发执行
6:并发重置:
重置CMS收集器的数据结构,等待下一次回收
缺点:
会产生大量的堆空间碎片,
需要更多的cpu资源
21、jvm场景问题,标记清除多次后老年代产生内存碎片,引起full gc,接下来可能发生什么问题?
22、Thread 相关方法
start()
getName()
currentThread()
sleep()
setName()
23、死锁
1:4个必要条件:资源互斥、占有且等待、不可抢占、循环等待
2:死锁避免:锁顺序、锁时限、死锁检测与恢复
24、jvm 为什么需要双亲委派
主要为了 类的安全性,避免了重复加载,即使是同一个类被不同的加载器加载,也是两个类
为了保证相同的class文件,在使用的时候,时相同的对象,采用了双亲委派的方式来加载类
25、为什么要打破双亲委派
以为某些时候,父类无法找到要加载的文件,这个时候需要启动类加载器,委托子类实现
25、如何打破双亲委派
重写findClass()
26、G1 和CMS区别
CMS:
以获取最短回收停顿时间为目标的垃圾收集器,基于并发标记实现
过程:
初始化并发
并发标记
并发整理
重新标记
并发清除
并发重置
优点:
并发、低停顿
缺点:
对cup非常依赖
无法回收浮动垃圾
CMS出现fullGC的原因
年轻代晋升老年代 没有足够的连续空间,有可能是内存碎片导致的,所以触发了fullGC
CMS失败后采用SerialOld收集器
G1:
面向服务端的应用收集器
过程:
初始化标记
并发标记
最终标记
筛选回收
只有并发标记阶段能做到用户线程 和回收线程并发执行
G1可以不需要其他收集器配合,能独立管理整个GC堆
目标是替换cms收集器
27、常量池在JVM内存模型中的位置
方法区
28、JVM中哪些是共享区,哪些可以作为GC ROOT
29、什么是字节码,采用字节码好处是什么
30、项目如何排查jvm问题
对于正在运行的环境:
1:使用jmap查看各个区域使用情况
2:通过jstack查看线程运行的状况,比如哪些线程阻塞,出现了死锁
3:通过jstat查看垃圾回收情况,特别是fullgc 如果发现比较频繁,那么就进行优化
4:还可以找到占用cpu 最多多线程,定位到方法 优化这个方法
已经发生oom的系统
1:生成当时的dump文件,(-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)
2:利用jsisualvm等工具来分析dump
3:根据dump 找到异常的示例对象和异常的线程占用cpu高的,定位具体代码
4:然后在进行详细的分析和调试
31、如何查看线程死锁
32、线程之间如何进行通讯
1:可以通过共享内存,或者网络进行通讯
2:如果通过共享内存进行通讯,需要考虑 什么时候阻塞,什么时候唤醒
3:java中 wait()、notify()就是进行线程阻塞和唤醒的方法
4:通过网络连接将通讯数据发送给对方,需要考虑到并发问题,处理方式就是上锁
1、mysql索引的用途
提高查询效率,
索引和实际的数据 都是存在磁盘的,只不过在进行数据读取的时候会先把数据索引加载到内存
2、mysql b+树 分几层
一般情况下 3到4层的b+树,足够支持 千万级别的数据存储
1、索引基本原理
1:把创建了索引的内容进行排序
2:对排序结果生成倒排表
3:在倒排表内容上拼接上数据地址链
4:在查询时候,先拿倒排表内容,在取出数据地址链,拿到真正数据
2、mysql 聚簇索引和非聚簇索引的区别?
myisam:(非聚集索引)
存储文件
.frm 存储表结构
.myd 存储数据
.myi 存储索引
数据 和索引是分开存储的索引在myi文件 数据在myd 文件
innodb:(聚集索引)
存储文件
.frm 存储表结构
.ibd 存储数据和索引
叶节点包含了 完整的数据记录
聚簇索引 和非聚簇索引都是B+树 存储
聚簇索引:就是将数据和索引放到一起,并按照一定顺序组织,找到索引 就相当于找到了数据
非聚簇索引:叶子节点不存储数据,存储的是数据行的地址,根据索引找到数据行的位置,在去磁盘查找数据
3、mysql的 数据结构 优略势
1:hash 索引
采用了一定的hash算法,将键值换算成新的哈希值,检索不需要从跟节点 到叶子节点逐级查询,只需要一次hash 就可以找到数据的位置
2:b+树
是一个平衡的多叉树,从根节点到叶子节点的高度相差不会超过1,从根节点到叶子节点搜索效率相当,不会出现大幅度波动,基于链表的顺序扫描,也可以利用双向 链表左右移动效率非常高
4、索引的设计原则
使查询更快,占用内存空间更小
1:适合索引的列 是出现在where 子句中的列,或者链接子句中的指定列
2:基数较小的类,索引效果差,没有必要在此列中创建索引
3:使用短索引,如果使用长字符串进行索引,应该指定一个前缀长度,这样能够节省索引空间
4:不要过度的使用索引,索引需要占用额外的磁盘空间
5:定义有外键的数据列,一定要建索引
6:更新频繁字段不适合建索引
7:尽量的扩展索引,不要新建索引
8:对于查询时很少涉及到的 重复值比较少的列不要建立索引
9:对于定义 text image bit 数据类型,不用建立索引
5、mysql 锁的类型
共享锁
当一个事务对这个数据上了读锁之后,其他事务只能对这条数据上读锁
排他锁
又称为写锁,当一个事务为一个数据加上写锁,其他事务就不能上任何锁
表锁
上锁的时候锁住整张表,当下一个事务要方位这个表的时候,必须等上一个事务释放锁,才可以加锁访问
锁粒度大,上锁简单,冲突率高
行锁
上锁的时候 锁住的时整张表的某一行数据,其他事务访问同一张表的时候,只有被锁住的记录不能访问,其他都可以访问
1、mysql引擎区别
myisam:
非事物安全的
锁粒度表级
支持全文检索
效率优于innodb
innodb:
支持事物
支持外键
锁粒度行级
不支持全文检索
2、mysql 索引,要有足够的深度
普通索引
唯一索引
主键索引:
MyISAM:
索引文件和数据文件是分离的,索引文件仅保存数据记录的地址
INNODB:
表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文 件本身就是主索引
全文索引
组合索引
3、 索引如何优化,如何优化查询
explan 查询sql 是否用上的索引,
关键信息:
id:
id相同 执行顺序从上往下
id不同 如果是子语句查询,id顺序也会递增,id越大优先级越高
id相同和不同 同时存在 相同的是一组,从上往下执行,id越大优先级越高
1:type 约靠上 越好 基本要达到ref 最少要达到rang
system
const
ref
rang
index
all
2:possible_key 可能用到的索引
3:key sql执行的时候有没有用到索引
4:key_len 使用索引长度
5:Extra 可选的信息
using index 使用了索引的覆盖
using index condition 使用了索引的下推
using filesort 使用了临时空间
4、mysql 慢查询怎么解决的
explain 查询查询慢的原因
表建索引
5、explain怎么使用
6、数据库连接池怎么设计的
7、mysql回表
简单来说就是数据库根据索引(非主键)找到了指定的记录所在行后,还需要根据主键再次到数据块里获取数据
解决办法
查询的时候要把主键id也查出来
8、innodb特性
1:插入缓冲
插入缓冲,change buffer 和insert buffer insert buffer 只对insert有效
change buffer 对 (insert update delete)都有效
每次插入时候,先判断插入的非聚集索引是否不是在缓存池中,如果在,直接插入,如果不在 放入 insert 缓存之中
1:辅助索引页读取到缓存池中。正常select先检查insert buffer 是否有该非聚集索引页存在,若有则插入
2:辅助索引页没有可用空间,空间小于1/32页的大小,则会强行合并操作
3:master thread 每秒和每10秒合并操作
2:二次写
崩溃重启过程中 为加快写入速度用的
3:自适应哈希
如果最近连续3次都访问到的数据,会生成hash索引,效率比原来的高
4:预读
提高io性能用的,分为线性预读和随机预读,线性预读是提前将下一个读取到 buffer pool中
随机预读 是将当前剩余的读取到buffer pool中
9、查询在什么时候不走(预期中的)索引?
1:模糊查询 %like
2:索引列 使用了函数
3:非最左前缀顺序
4:where 对null判断
5:or 至少有一个参数没有索引
6:需要回表查询的结果集过大
10、order by 工作原理
11、MySQL的事务隔离级别,分别解决什么问题?
12、数据库优化 sql优化
13、myisam和innodb的区别,什么时候选择myisam
14、B-树和B+树区别
1:B-树和B树是一个概念,是多路搜索树
特性:
关键字集合分布在整颗树中
任何一个关键字出现只出现在一个结点中
搜索有可能在非叶子结点结束
索性能等价于在关键字全集内做一次二分查找
最底搜索性能为O(logN)
2:B+树是B-树的变体,也是一种多路搜索树
特性:
所有关键字都出现在叶子结点的链表中
不可能在非叶子结点命中
非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层
更适合文件索引系统
优势:
单一节点存储更多的元素,使得查询的IO次数更少
所有查询都要查找到叶子节点,查询性能稳定
所有叶子节点形成有序链表,便于范围查询
15、组合索引怎么使用?
1:从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用
16、最左匹配的原理
1:以最左边的为起点任何连续的索引都能匹配上
17、聚簇索引是什么
18、mysql的默认隔离级别
读已提交
读未提交
可重复读
串行化
19、如何分库分表,分页查询,查询非拆分字段方案
20、聚集索引和非聚集索引的区别?
1:聚簇索引一个表只能有一个,非聚簇索引可以有多个
2:聚簇索引查询速度快,插入慢,非聚簇索引插入快,查询慢
3:聚簇索引的数据行的物理顺序与列值的逻辑顺序一致,非聚簇索引的逻辑顺序与磁盘上行的物理存储顺序不同
21、innoDB的B+树索引叶子节点的Data域存储的是什么?MylSAM的B+树索引叶子节点的Data域存储的是主键还是物理地址?
22、MySQL innodb的b+树索引,主键索引,聚簇索引有什么区别。
23、数据库四大特性
1:原子性
事物包含的所有操作,要么全部执行成功,要么全部失败
2:一致性
从事物执行前后都必须保持一致性的状态
3:隔离性
并发访问数据库的时候,数据库每一个用户开启的事物,不能被其他事物所作的操作干扰
4:持久性
一旦提交,就是持久性的,就算是故障恢复了也不能改变
24、数据库事物隔离级别
1:读未提交
在写数据库的时候会加一个排他锁,不允许其他事务进行操作,读不受限制,读不上锁,会引发脏读
脏读: 读取了别人未提交的数据
2:读已提交
同一个事务对数据的多次读取的结果不一致,引发不可重复度问题
3:可重复读
解决不可重复读问题,出现幻读
4:串行化
事务只能一件一件的进行,不能并发进行。
25、Mysql怎么保证原子性的
利用innodb的undolog 实现原子性,当事物进行回滚时能够撤销所有已执行的sql语句,他记录需要回滚的相应数据
26、Mysql怎么保证持久性的
当数据进行修改的时候,会在内存 和redo log 中记录这次操作,当事物提交时,会将redo log日志进行一次刷盘,当数据库宕机重启的时候,会将redo log的内容恢复到数据库中,再根据undo log和binlog 内容决定回滚数据还是提交数据
27、UNION和UNION ALL 区别
两者都是将两条数据集合 合并成为一个
UNION 会进行排序去掉重复的数据 UNIONALL 不会
所有UNIONALL 比UNION 效率高
28、mysql隔离性的实现
29、B树和B+树了解吗
b树 叶子节点和非叶子节点都存储数据,b+树 非叶子节点存储的是数据的索引,叶子节点存储的是数据
30、mysql 锁竞争了解吗
31、MySQL有哪几种索引?
唯一索引
用于快速定位
普通索引
用于快速定位,列值唯一 可以为空
主键索引
用于快速定位,列值唯一,不可用有空,表中只能有一个
组合索引
多列组成的索引,用来组合搜索
全文索引
对文本进行分词,进行搜索
32、mysql数据结构
二叉树:如果是单边增长的情况下,就会出现链表一样的数据结构,树高度大
红黑树:在二叉树的基础上,多了平衡树,又叫平衡二叉树,不会像二叉树一样往一个方向发展, 同样找6 二叉树需要6个节点,红黑树只需要3个节点
b树: 在红黑树的基础上,每个节点可以存放多个数据 这时查找6 只需要2个节点
b+树: 是b树的变种,非叶子节点是重复的数据,mysql索引的数据结构是b+树
叶子节点存放数据,非叶子节点存放的是索引列的数据
mysql 默认的一个节点是16K的大小,
创建索引的时候可以选择索引类型,一个是btree 一个是hash ,hash查找也很快,当遇到范围性查找的时候就比较尴尬,根据业务需求还判定是创建btree还是hash
33、数据库主从复制原理
主库 有一个binlog日志,记录所有变更记录
通过 log dump 线程 同步给从库
从节点io线程接收binlog内容,写入relay log 文件中
从节点sql线程 读取relaylog 中的内容 从而保证最终一致
默认是异步进行同步,这样 会有一个现象,就是 主库挂了,从库同步失败 就会丢失数据
全同步复制:
主库写入binlog日志 全量同步给从库,等从库都执行完了,返回给客户端 性能会受到一定的损失
半同步复制
就是从库写入日志,成功后返回ack给主库,主库收到至少有一个从库成功了,就认为成功了
34、数据库主从读写延迟解决方案
35、mysql的in查询是可以用到索引吗
首先要遵循最左匹配原则,假如从左向右匹配,如果遇到范围条件的话 从范围条件那开始 是不会用到索引的
36、mysql如何实现的可重复读
innodb 通过版本号实现,读取小于等于当前版本号的数据
37、mysql 锁
读写锁
一个事物获取了这行的排他锁, 其他时候就不能获取这一行的 共享锁和排他锁
共享锁
一个事务获取了共享锁之后 其他事物还可以获取共享锁
意向锁
为了解决 行锁和表锁冲突。 事物在获取锁之前,必须先获取相应的意向锁
按粒度分:
行锁:锁某行数据,锁粒度小 并发高
表锁:锁整张表数据,锁粒度大 并发低
间隙锁:锁的是一个区间
还可以分为
共享锁:就是读锁,一个事务 给某行数据加了读锁 其他事务也可以读
排他锁:就是写锁 一个事务给某行数据加了写锁 其他事务就不能读 也不能写
还可以疯呢为
乐观锁:不会真正去锁某行记录,而是通过版本号实现
悲观锁:行锁 表锁 都是悲观锁
38、mysql 调优
数据选型:首先是数据选型方面进行优化,选取最适用的字段属性,数据的表越小,查询越快
范式应用:合理使用范式和反范式
存储引擎的选择:如果该数据库读操作较多,存储引擎选择MyISAM,如果是写操作多,选择innodb
主键选择:代理主键
执行计划explain:使用explain+sql测试sql语句执行情况,然后优化sql语句
注意的关键字:type关键字,通常达到range级别,最好是ref,而ref最好是一个常数。
索引优化:
尽量在主键上添加使用索引
利用覆盖索引、索引下推机制,注意组合索引的匹配原则,
尽量使用唯一索引,避免使用普通索引
查询优化:这个应该被包含在执行计划中,但是个人觉得还是拆出来,其实我们在写的时候就该注意sql的效率,explain执行计划只是验证
优化数据访问,避免查询中出现筛选大量数据,可以通过limit限制;
避免select * from table这种全表扫描的语句
如果业务没有特殊规定数据,那么就尽量避免使用UNION,可以考虑UNION-ALL替换,因为后者不会过滤重复数据,效率高于UNION
39、mysql数据库中,什么情况下设置了索引但无法使用
1:没有符合最左前缀规则
2:字段进行了隐士的数据类型转化
3:走索引没有全表扫效率高
40、innodb 如何实现事务
1:Redo Log undoLog 来实现事务
2:
41、为什么建议 innodb必须建主键, 推荐使用整形 自增?
42、聚簇索引的优缺点
优点:
1.数据访问更快,因为聚簇索引将索引和数据保存在同一个B+树中,因此从聚簇索引中获取数据比非聚簇索引更快
聚簇索引对于主键的排序查找和范围查找速度非常快
缺点:
1.插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的 ID列为主键
2.更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB表,我们一般定义主键为不可更新。
1、什么是Redis
Redis本质上是一个Key-value 存储的内存数据库 nosql
2、Redis 支持几种数据类型
String
可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配 字符串长度 512M
List
底层结构 是双向链表,首尾插入性能较强, 随机定位性能比较弱
结合使用rpush/rpop/lpush/lpop四条指令
Set
Sorted
跳跃式链表,每隔几个元素挑选出一个代表,然后再代表元素中 再挑选出二级代表 然后再串联
hash
扩容: 采用渐进式rehash方式,同时保留 新数组和老数组, 然后将老数组的数据逐渐移动到新数组中
bitmap
geo
3、Redis 有几种淘汰策略
allkeys-lru : 尝试回收使用较少的键
allkeys-random : 尝试随机回收键
volatile-lru : 尝试从已设置过期时间的键中回收使用较少的键
volatile-ttl : 尝试回收存活时间较短的键
volatile-random : 尝试随机回收 已设置国企时间的键
4、一个字符串类型的值,能存储最大容量是多少
512M
5、怎么理解Redis事物
事物是单独隔离的,按顺序进行,事物在执行时,不会被打断
6、Redis 单线程 为什么还这么快?
1:基于内存
2:多路复用
3:单线程
7、Redis数据持久化的方式: (需要深入了解)
AOF: 开启后,每执行一个修改命令,都会把这个命令添加道aof文件中
优点:实时持久化
缺点:文件会越来越大,需要定期执行重写操作来降低文件体积
RDB:通过bgsave触发命令,然后父进程fork创建子进程,子进程创建rdb文件,根据父进程内存生成临时快照
优点:rdb做恢复要快与aof
缺点:每次生成rdb文件开销比较大
8、zset底层实现,如何选择上一层节点 (???)
9、redis3.0原生集群和redis读写分离+哨兵机制区别
10、缓存数据最终一致性
双删策略,第一次先删除缓存的数据,更新数据库 间隔一段时间在删一次缓存数据
11、Redis如何实现分布式锁
12、redis缓存穿透、雪崩 解决方法
雪崩: 在某一时间,大量数据同时失效,解决方案,设置不同的缓存时间
穿透:
1:布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个不存在的数据会被bitmap拦截条,减轻数据库查询压力
2:将查询为null的数据,存到redis中,设置过期时间
请求一个缓存和数据库中不存在的数据, 解决方案,空数据缓存 设置失效时间
13、redis脑裂
14、redis问了一个实际问题的解决办法,如果redis一个value特别大,有什么解决方案
15、redis哨兵选举
1:过滤故障节点
2:根据有优先级进行选举,配置文件,slave-priority配置的信息,值最低的为主
3:选择消息id最大的为主
4:先择1runid最小的为主
41001
16、你刚才说redis的所有定时器是用链表存储的,还知不知道别的处理方式(不确定对不对,答了quartz的定时任务是用两个队列保存的)
17、redis定期删除怎么做的?
Redis 每隔一定时间,检查一次key是否失效,如果失效就会删除
18、分布式锁 具体是怎么实现的
19、淘汰策略里面讲到了LRU算法实现原理
lru 基于双向链表实现,如果存储满了,淘汰双向链表尾部数据,每次新增访问数据时通过效率把新的节点增加到对头,或者把已存在的节点移动到队头
20、redis 数据迁移
21、redis 哨兵
主要解决了主从复制 出现故障时需要人为干预问题
redis哨兵功能
1:集群监控
负责监控 redis master 和slave进程是否正常工作
2:消息通知
如果某个reids 挂了,负责通知管理员
3:故障转移
master node 故障了 会自动转移到slave node上
4:配置中心
如果master 故障了 通知client端 新的master地址
22、哨兵的高可用原理
当主节点故障了,由哨兵自动完成故障发现和转移,并通知对方,实现高可用
1:哨兵机制建立了多个哨兵节点,来共同监控数据节点的运行状况
2:同时哨兵之间也互相通讯,交换主节点的监控状况
3:每个一秒,每个哨兵会向集群的master slave 其他哨兵进程 发送一次ping命令,做一次心跳检测
哨兵用来判断节点是否正常的重要依据
设计到的概念
客观下线:
1:一个哨兵判定主节点挂掉了,是主观下线
主观下线
1:只有半数哨兵判定主节点挂掉了,此时多个哨兵交换主观判定结果,才会判定主节点客观下线
23、redis 复制
1:从数据库向主数据库发送sync命令
2:主数据库接收到命令后,会保存快照,生成rdb
3:主数据库生成完快照,发送给从数据库,从数据库载入该文件
4:主数据库将所有缓冲区的写命令,给到从服务器去执行
5:以上执行完之后,主数据库每执行一个写命令,都会向从数据库发送
22、redis 主从 集群 哨兵模式,这三个概念了解吗?
1:主从模式:一个master 多个slave 读写分离 备份
2:哨兵模式:监控,故障转移,哨兵发现主服务器挂了,会从slave中选举一个新的主服务器
3:集群模式:为了解决单机redis 容量有限问题,将数据按一定规则分配到多台服务器,内存/qps不受限于单机,易于高扩展性
23、redis 各数据类型的底层实现
1:String类型底层实际
动态字符串(SDS)和直接存储, 编码方式可以是 int、war 和embstr 区别在于内存结构的不同
int:
字符出保存的是整形,可以用long类型来表示,会直接保存在redisObject 的ptr属性里 并将编码设为int
raw:
保存的是大于32字节的字符串值,内存分配2次,创建redisObject对象和sdshdr结构
embstr:
保存大于等于32字节的字符串内存分配1次,分配一个连续的内存空间
24、redis rehash 扩容机制
reids使用链地址法,解决hash冲突
扩容时,使用分摊转移的方式进行扩容,当插入一个x元素出发扩容时,先转移第一个不为空的桶,到新的哈希表,然后将x插入,直到旧的哈希表为空
25、ZSET 是怎么实现的
zset操作
zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。
zrem(key, member) :删除名称为key的zset中的元素member
zincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其 score的值为increment
zrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回 “nil”
zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回 “nil”
zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素
zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素
zrangebyscore(key, min, max):返回名称为key的zset中score >= min且score <= max的所有元素 zcard(key):返回名称为key的zset的基数
zscore(key, element):返回名称为key的zset中元素element的score zremrangebyrank(key, min, max):删除名称为key的zset中rank >= min且 rank <= max的所有元素 zremrangebyscore(key, min, max) :删除名称为key的zset中score >= min且score <= max的所有元素
26、Redis 实现RDB 过程
27、热key 大key 怎么处理
隔离 分治 每个节点分摊不同的key
28、redis bitmap 解决12306方案
1: 二进制 安慰操作 与或非 操作
setbit k1 1 1
setbit k1 7 1
setbit k2 1 1
setbit k2 6 1
与: bitop and andkey(结果名字 key) k1 k2 (全1 为1 有0 则0 )
或: bitop or andkey(结果名字 key) k1 k2 (有1 为1 全0 则0 )
2:需求 场景
1:统计 用户按任意时间窗 统计用户的登录天数
数据库设计
userid date 一年365天 会有很多数据 sql 执行会很慢 (成本高,服务器很多)
bitmap (位图思维方式 ( 压缩,布隆过滤器 列式存储索引判定))
setbit sean 0 1
setbit sean 1 1
setbit sean 2 1
bitcount sean 0 -1
2: 活跃用户数判定 618 2E注册用户,准备礼物 大库准备多少商品
setbit 20200101 0 1
setbit 20200101 7 1
setbit 20200102 7 1
bitop or res 20200101 20200102
bitcount 0 -1
3: 12306 (包头不包尾 假如 1从A-C 那直接把 A B 改成1 后续 用或 就可以了)
29、Redis数据结构 以及 使用场景
1:字符串 String 可以用来存储简单的数据缓存
2:哈希表 hash 用来存储一些 key-value 适合存储 对象
3:列表 list 可以当作栈
4:集合,和列表类似,存储多个元素 不可用有重复的
5:有序集合, 可以用来设置顺序,也可以用来实现
30、Redis 集群策略
1:主从模式,主库可以读写,并且会和从库进行数据同步, 缺点,主库宕机后,需要手动改ip 扩容比较难
2:哨兵模式,这种模式在主从的基础上增加了哨兵节点,当主库宕机 哨兵会发现主库节点宕机,然后会从库中选择一个库作为主库,哨兵也可以做集群,保证了当有一个 哨兵宕机后其他哨兵还可以继续工作
3:cluster 模式 支持多主 多从,会按照key进行槽位的分配 可以让不同的key 分散到不同的主节点上, 可以让集群支持更大的数据容量
31、redis 主从复制的核心原理
全量复制:
主节点通过 bgsave命令,将rdb 发送到从节点,比较消耗 cpu 内存 磁盘io
主节点通过网络 将rdb发送给从节点,对主从节点的网络造成很大的消耗
从节点 需要清空老数据库 载入rdb文件的过程是阻塞的,无法响应客户端
部分复制
六、Dubbo面试题
1、dubbo分层
Config 配置层
Proxy服务代理层
Registry注册中心层
Cluster 路由层
Monitor监控层
Protocol远程调用层
Exchange信息交换层
Transport 网络传输层
Serialize数据序列化
1、dubbo有哪些模块
1、Consumer
2、provider
3、monitor
4、registry
2、dubbo底层通信的原理
2、如何从0到1设计一个类似Dubbo的RPC框架
3、duboo 原理
3、Dubbo 的 SPI 机制
4、基于 Dubbo 能做什么扩展
5、Dubbo 负载均衡算法
6、dubbo调用模式
7、dubbo的spi
8、dubbo协议有什么?具体有哪些不同
9、服务注册与发现;
10、SPI;
11、服务暴露过程;
12、服务引用;
13、降级;
14、负载均衡;
15、dubbo 如何做系统交互
首先 dubbo 是rpc框架
dubbo 底层是通过rpc 来实现服务与服务之间调用的,同时dubbo 也支持很多协议,比如 dubbo协议,http协议 rest 等
他们的底层实现也不太一样, dubbo协议是通过netty 实现 也可以用mina。http协议底层是通过tomcat 或jetty实现
在服务消费者调用某个服务的时候,会将当前方法信息和入参 等信息 封装成invocation对象,在根据不通协议的组织方式将对象发送给服务提供者
16、dubbo 负载均衡策略
轮询算法
随机算法
一致性哈希算法
最小活跃算法
17、dubbo 通讯协议
1:默认 dubbo 协议 单一长连接,NIO异步通讯 基于hessian作为序列化
使用场景
输出数据量小 并发量高
2:rmi 协议
使用场景
java 二进制序列化,多个短链接,适合消费者和提供者数量差不多,适用于文件传输
3:hessian 协议
使用场景
hessian序列化协议,多个短链接,适用于提供者数量比消费者数量多,适用于传输文件
4:
18、dubbo 序列化协议
七、MQ面试题
1、如果让你实现一个mq,怎么样保证消息不丢失
2、RabbitMq怎么保证消息不丢失
3、abbitMQ一致性
4、mq消息丢失怎么办,如何防止重复消费
5、消息队列怎么处理消息丢失问题
rabbitmq 生产者:
1:开启tabbitmq事务,如果消息没有被rabbitmq接收到就回滚事务,继续重试,否则就提交事务 效率慢
2:开启confirm模式,这是每个消息都会被分配一个id,如果消息被rabbitmq成功接收,会返回ack消息,如果消息没有被rabbitmq处理,会回调一个nack接口告诉 你处理失败
rabbitmq 丢失数据:
1:开通mq的数据持久化,将数据写入磁盘
开通方法:
1:将queue 设置持久化 可以保证 rabbitmq持久化queue的元数据 不会持久queue里面的数据
2:发送消息的时候 将消息的deliveryMode 设置为2 将消息设置为持久化
rabbitmq 消费者:
1:把默认的ack机制调为手动提交
kafka丢失数据:
生产者:
设置ack=all leader接收到消息 所有的follower同步到消息后才认为本次写成功
kafka:
1:topic 设置分片数 大于1
2:设置min.insync.replicas 大于1 leader感知至少有一个folower 与自己通着
3:producer 设置 acks=all
4:在producer 端 设置retries =MAX
消费者:
将offer 自动 改为手动
6、如何保证队列消息可靠性
1、消费者 通过幂等性 保证消息不被重复消费 关闭自动提交 改为手动提交
2、生产者 使用消息确认机制, 队列持久化 mq丢列 设置成持久化的
7、Exchange 类型
direct
直接交换机 精确匹配
fanout
topic
模式匹配
#匹配 0或一个单次 *匹配 一个单次
headers