- 知识梳理
- Java 基础
- Java 的异常有哪几种?Throwble 的子类有哪些?
- 描述一下 Java8 哪些新特性?你是如何使用 Stream 流操作的?知道 Stream 底层原理吗?
- HashMap 和 HashSet 的底层实现原理
- 死锁及打破方式;(互斥、请求与保持、循环等待、阻塞;超时、银行家算法)
- 线程&协程;(blabla…)
- 阻塞 IO、非阻塞 IO、同步 IO、异步 IO、IO 多路复用(区别及内部流程、用户态内核态 blablabla…)
- 请描述一下从浏览器输入一行请求 url 开始,SpringMVC 的整个处理过程
- 请求是先 Filter 还是先经过 Servlet?Servlet 处理完后,还会经过 Filter 吗?
- maven 是 compile、package、install 啥区别
- 2. 并发基础
- 1. 公平锁和非公平锁的区别
- 2. Java 有哪些同步机制?说一下 volatile 关键字
- 3. Java 线程有几种状态
- 4. 说一下 sleep() 和 wait() 方法的区别
- 5. 如何唤醒处于睡眠状态的线程
- 6. 如何释放处于等待中的线程
- 7. Executor 创建的线程池,有哪几种类型?
- 8. Executor 创建线程池的构造方法的参数都有哪些,线程池的关闭,线程池使用 interrupt 怎么样?
- 9. corePoolSize = 5,maximumPoolSize = 10,那么现在有 4 个任务同时进来,这时会创建几条线程?如果还没处理完又进来 2 个任务,此时又会创建几条线程还是不会创建?
- 10. 还有哪些最原生最简单使用多线程的方法?
- 11. blockqueue 原理,condition 和普通 wait 的区别
- 12. new 一个对象都经历了那几个过程?(考反射)
- 13. 锁膨胀过程和锁的优化
- 3. 消息基础
- 搜索基础
- Spring/Mybatis
- 6. RPC 框架
- 7. JVM 基础
- 8. 缓存基础
- 1. 你在公司怎么用的缓存,为什么用缓存?那会出现什么问题(缓存击穿、穿透、雪崩)?
- 2. Redis 为什么这么快,它的过期和淘汰策略是什么以及手写一个 LRU 算法
- 3. Redis 高并发和高可用的集群原理
- 4. Redis 持久化机制、底层原理是什么,有什么优劣?
- 5. 集群中 Redis key 如何寻址,寻址算法?hash 一致性算法
- 6. 缓存、数据库双写不一致问题
- 7. Redis 并发写的问题,即顺序问题
- 8. 公司的 Redis 架构,多少台,多少内存,多少核,CPU,主从架构,QPS?
- 9. 热点 key 的缓存,以及大对象的缓存问题解决。
- 10. Redis pipline 是全双工还是半双工
- 11. jedis 是否使用连接池,如何处理有序数据
- 12. Redis 数据结构类型及源码简述;
- (String、Hash、ZSet、Set、Dict、GEO、HyperLogLog、BloomFilter、Stream、Bitmap 等)
- 13. Redis 并发竞争 Key 问题
- 14. Redis Replication 全量复制过程
- 15. Redis sentinel 机制
- 16. Redis 的数据丢失和脑裂问题
- 17. Redis 实现分布式锁的几种方式,优劣
- 18. Redis 分片
- 19. sentinel 的作用,以及选举 slave 为 master 过程
- 9. 数据库
- 1. MySQL 索引类型有多少种
- 2. MySQL 索引的使用与优化
- 3. MyBatis 是如何执行 SQL 语句的
- 4. JDBC 获取数据库连接的大概过程(从 getConnection 开始)
- 5. PreparedStatement 和 Statement 区别
- 6. MyBatis 的一些 API 接口,比如 SqlSession、sessionFactory 等等
- 7. 线上为什么用 RR 级别
- 8. 请问是不是每次连接数据库都创建一个连接?线程池你都有哪些了解?有接触过哪些线程池?
- 9. 数据库联表查询时,左连接和右连接的区别
- 10. 说说 MySQL 的七种事务隔离级别
- 11. MVCC
- 10. APM
- 分布式理论
- 项目实战
- Java 基础
知识梳理
Java 基础
Java 的异常有哪几种?Throwble 的子类有哪些?
((20200916194439-4h97y6o “异常”))
描述一下 Java8 哪些新特性?你是如何使用 Stream 流操作的?知道 Stream 底层原理吗?
Java8 支持如下特性:
- Lambda 表达式和函数式接口,@FunctionalInterface 修饰的接口只能由一个函数的接口,不包括默认方法和静态方法
- 接口支持默认方法和静态方法
- 方法引用,支持四种 类型引用:构造器引用(Class::new)、静态方法引用(Class::static_method)、类的成员方法引用(Class::method)、实例的成员方法引用(instance::method)。示例如下:
1 | class scratch { |
- 支持重复注解
- 可以获取参数名称,需要编译增加 -parameters 参数,可以在 maven 的 compile 过程增加该参数
1 | private static void compile() throws NoSuchMethodException { |
- Optional、Streams、Date/Time API、并行数组
- ConcurrentHashMap(((20200930172816-sf88c8v “ConcurrentHashMap 的实现原理与使用”)))、concurrentForkJoinPool、StampedLock(((20200925172739-4h7jrms “StampedLock 详解”)))、原子变量(((20200925175233-ur4xqzv “原子变量详解”)))
- 类依赖分析工具 jdeps
- Metaspace 代替 PermGen space(((20200925231449-p5eetg4 “JVM 运行机制”)))
详情参考:((20201005234154-te3yj73 “Java8 的新特性”))
HashMap 和 HashSet 的底层实现原理
- put 过程
- get 过程,其中的效率红黑树优化
- hash 过程
- resize 过程,其中的死锁问题
((20200915004102-t3x94b1 “5.1 HashMap”))
死锁及打破方式;(互斥、请求与保持、循环等待、阻塞;超时、银行家算法)
线程&协程;(blabla…)
阻塞 IO、非阻塞 IO、同步 IO、异步 IO、IO 多路复用(区别及内部流程、用户态内核态 blablabla…)
请描述一下从浏览器输入一行请求 url 开始,SpringMVC 的整个处理过程
DispatcherServlet 调用 handlermapper,根据 url 映射到 controller 处理器。最后通过参数解析,返回包装到 view。
请求是先 Filter 还是先经过 Servlet?Servlet 处理完后,还会经过 Filter 吗?
拦截器是全局生效,包括静态资源也拦截,常用于字符串编码,cros 拦截
maven 是 compile、package、install 啥区别
他们属于同一个生命周期,一共三生命周期:clean、defaul、site。其中 compile 是编译代码、package 是打成包到当前 target 目录,install 是将包上传到本地仓库
2. 并发基础
1. 公平锁和非公平锁的区别
非公平锁进来时先进行 state 的 CAS 公平锁要判断是否有线程在队列中等待
非公平锁 CAS 失败后,执行 tryAcquire 时,没有判断线程队列是否有等待,直接再进行 state 的 CAS,如果失败后,再和公平锁一样,进队列等前节点唤醒
2. Java 有哪些同步机制?说一下 volatile 关键字
ReentrantLock、Synchronized、volatile、final、wait/notify、Semaphore、Condition
同步要求保证:有序性、可见性、原子性。其中 volatile 保证了可见性和原子性。
3. Java 线程有几种状态
- New
- Runnable
- Blocked
- Waiting
- timed_waiting
- terminated
4. 说一下 sleep() 和 wait() 方法的区别
- Thread.sleep()不会释放占有的锁,Object.wait()会释放占有的锁
- Thread.sleep()必须传入时间,Object.wait()可传可不传,不传表示一直阻塞下去
- Thread.sleep()到时间了会自动唤醒,然后继续执行
- Object.wait()不带时间的,需要另一个线程使用 Object.notify()唤醒
- Object.wait()带时间的,假如没有被 notify,到时间了会自动唤醒,这时又分好两种情况,一是立即获取到了锁,线程自然会继续执行;二是没有立即获取锁,线程进入同步队列等待获取锁
5. 如何唤醒处于睡眠状态的线程
通过 interrupt 方式
6. 如何释放处于等待中的线程
notify
7. Executor 创建的线程池,有哪几种类型?
- newCachedThreadPool:0,MAX,60,SynchronousQueue
- newFixedThreadPool:core,core,0,LinkedBlockingQueue
- newScheduledThreadPool:core,MAX,0,DelayedWorkQueue
- newSingleThreadExecutor:1,1,0,LinkedBlockingQueue
- newWorkStealingPool:parallelism,asyncMode ? FIFO_QUEUE : LIFO_QUEUE
((20200919140157-d41qw7t “线程池”))
8. Executor 创建线程池的构造方法的参数都有哪些,线程池的关闭,线程池使用 interrupt 怎么样?
core, max, keepalive, timeunit, blockqueue, threadfactory, rejecthandler
9. corePoolSize = 5,maximumPoolSize = 10,那么现在有 4 个任务同时进来,这时会创建几条线程?如果还没处理完又进来 2 个任务,此时又会创建几条线程还是不会创建?
不会创建。
创建 1 个。
10. 还有哪些最原生最简单使用多线程的方法?
- 继承 Thread
- 实现 Runnable
- tiimer
- future/callable
11. blockqueue 原理,condition 和普通 wait 的区别
12. new 一个对象都经历了那几个过程?(考反射)
new:
- new 关键字创建一个 Java 对象时,JVM 首先会检查这个 new 指令的参数能否在常量池中定位到一个类的符号引用
- 然后检查与这个符号引用相对应的类是否已经成功经历加载、解析和初始化等步骤,里面会对静态变量初始化,重新赋值
- 类完成装载步骤之后,就已经完全确定出创建对象实例时所需的内存空间大小,接下来 JVM 将会对其进行内存分配,以存储所生成的对象实例(分配可能在栈上分配,否则在堆上使用指针碰撞或空闲列表方式创建)
- 调用构造方法生成一个对象
反射:
- class.forName()指定了 ClassLoader 后,就可以在指定的环境中查找某些类,即加载类可以在编译时不存在,运行时存在
- 实例
13. 锁膨胀过程和锁的优化
锁优化:自旋锁、锁粗化、锁消除、偏向锁和轻量级锁
偏向锁:
3. 消息基础
在哪里使用过消息队列?不使用消息队列可以吗?使用过哪些消息队列?有哪些优劣势?(顺序性如何保证?事务消息如何保证?消息重复了怎么办?消息丢失了怎么办-> 消息队列的常见解决方案:)消息队列引入之后的缺点?MQ 挂了怎么办?
参考:
- MQ 的设计概要:https://zhuanlan.zhihu.com/p/21649950,这个地址搞通可以回答 80%。
- 还需要看其它的消息队列,进行优劣势对比。
- rocketmq 的 example:http://rocketmq.apache.org/docs/transaction-example/
答:短信使用过,我在钉钉开发的时候,审批通过回调需要调用短信项目来给审批人发送短信,实际调用的就是消息队列,放在消息队列中,等待短信中台去消费来实际发送短信。消息队列本身是用来解耦、异步和削峰。但是系统可用性就会降低,MQ 中间件挂掉怎么办?复杂性,重复数据和丢失数据的处理,幂等性。一致性,多个系统同时需要处理数据,如果只有某个系统失败,那么数据就会出现不一致。
回答中透露出三个点:
解耦:如果不使用,你单独 A 系统对接 B、C 系统还要分别考虑重试机制。
异步:发送多个消息,快速返回,消息系统异步的执行任务。
削峰:A 业务系统只能接受每秒 5K 条消息,但是中午达到了 1W 条每秒,可以使用 MQ,接受 1W 条消息,然后 A 业务系统以每秒 5K 条消息慢慢进行消费,虽然会有消息堆积,但是不会持续时间很长
我们用的 push 方式、使用过顺序消息、重置位点。
消息重复发送怎么办?(即幂等性)
例如 kafka 的重复消费场景
消息丢失怎么办?
现在你要设计一个广告计费,用户点击一次发送消息进行计费结算,如何保证?(涉及金额)
- 生产者消息发送到 MQ 过程中由于网络问题, MQ 没收到,消息丢失。这需要发送消息的时候需要回调:MQ 收到了消息。
- MQ 突然宕机停电,这需要开启持久化。(有可能还没持久化到磁盘就挂了)。
- 消费者消费到一半自己挂掉,MQ 以为消费已经被消费了。这需要消费者每次消费完之后手动发送消费成功的 ACK。
- 例如 kafka,消费者在写入数据到 broker 中,broker 的 leader 在同步该消息到 follower 时,自己挂掉,重新选举了 leader 就会导致之前的消息丢失。解决方案是每次只有 leader 同步到 follower 时,才会返回生产者消息发送成功!其中 kafka 的 topic 是有首领副本的概念。
消息的顺序性保证
例如 MySQL 主从同步,是通过 binlog 来实现,从数据库通过复制主数据库的 binlog 来同步,对一条记录进行增删改,对应生成了三个 binlog 有三条记录增删改,这时将 binlog 使用 MQ 发送,就需要保证顺序性。
- 生产者生产消息于同一个 queue/partition:将需要保证顺序的消息发送在同一个 queue 中,这样消费者消费 queue 就会按照顺序消费。在 kafka 中就是一个 partition。
- 消费者消费消息:消费者使用多线程消费就会出现消费顺序不一致,因此消费者需要在消费的时候使用内存队列,例如订单的生成创建发送短信等顺序业务,可以在同一个内存队列中落盘。即使用内存队列 + 多线程来即保证了多线程性能也保证了消息的顺序性。
消息的延迟和消息积压的过期恢复?
几百万消息在 MQ 中积压了几小时怎么办?
这时候先让 3 个消费者恢复正常,然后再部署 10 倍的消费者专门消费 topic 为 consu 的消费,将 3 个消费者修改逻辑,让原来的消费者中转到 consu 中进行消费落盘,这样能快速的消费也不会对落盘数据库造成影响。
几百万消息在 MQ 中积压了几个小时,由于过期失效策略导致消息没了怎么办?
晚上凌晨在消息的源头补数据重新导入到 MQ 中。(不要设置过期失效策略)
几百万消息 在 MQ 快满了怎么办?
消费者快速消费,消费完就丢掉不做处理,然后再使用方案 2。或者导入到其它的 MQ 中进行中转消费。
消息位点重置原理
事务消息原理
如何设计一个消息中间件?
- 支持分布式(可用性)
- 落盘顺序写(持久化)
- 消息多个副本,并使用 zk 做 leader follower
- 数据丢失,内部每次写都进行落盘(每秒)或者写入 MQ 就持久化
- 消息顺序性,使用单个线程。
- 消息堆积能力
- 两次 RPC + 消息存储。
- 计算存储分离
- 弹性扩容缩容
- 网络、存储、复制、高可用、刷盘、堆积、顺序、幂等、事务
- 元数据中心同步
- 生产者消费者模型设计(缓冲队列),推拉模式
消息的重复处理
- 依赖 Exactly-once 语义,根据消息消费表 + 事务来做状态的更新,局限在于,如果有非 MySQL 操作或跨库就有问题,而且引入了事务
- 插入消息消费状态表,插入失败再稍后重试。
- 根据消息内容做重试(messageKey)。为了解决 md5 可能重复,messageKey 为 topic+tag+md5(body)做 Redis 锁一段时间,我们是锁两天,方便重置位点和解决问题的时间
- 机器重启过程的消息重复几率很大,因此需要在 bean 初始化后启动 consumer,bean 销毁前关停 consumer。producer 直接在 initmethod 使用 init/destory 即可。
消息可靠性保证
数据幂等、高可用镜像集群、Confirm 机制、事务机制、持久性
搜索基础
Spring/Mybatis
Spring 如何加载注解
Spring 启动流程、bean 实例化
MyBatis 如何防止 SQL 穿透
使用 # 而不是 $,底层使用 PreparedStatement,基于 SQL 预编译的替换
Spring 的 PostProcessor 接口起到什么作用
bean 初始化后回调,包括初始化前和初始化后的回调,例如 Apollo 的 ApolloConfig 注解,就是在初始化前增加监听器
Spring 如何实现事务
- ACID
- 隔离级别:读未提交、读已提交、可重复读、串行读
- 传播机制:require、support、mandatory、requires_new、not_support、never、nested
- 事务行为:事务开启、事务提交、事务回滚
- 事务切面:根据 ThreadLocal 获取是否有事务
如何在 Bean 对象中获取 ApllicationContext 上下文对象?
实现 ApplicationContextAware,原理是读取配置文件(注解扫描)后调用 refresh()
refresh:调用 FactorypostProcessors,初始化事件、注册事件监听器、通知子类刷新容器、初始化单例对象。
动态代理有哪些?JDK 动态代理和 cglib 有什么不同?
- JDK 代理:继承 Proxy,实现目标实现类的接口,将代理的方法都加上 final 修饰。在动态生成的实现类里面去调用
InvocationHandler
的 invoke 方法。 - cglib:继承目标类,实现 Factory,通过回调钩子方法调用
为什么用 Spring data JPA 而不用 MyBatis,MQ 的选型、注册中心的选型、配置中心的选型、分库分表的选型、数据传输的选型(主要针对我的项目来问的)
6. RPC 框架
1. Dubbo序列化,默认的序列化,各个序列化区别
2. Dubbo泛化调用实现的网关优劣点
3. Dubbo 消费者到提供者执行涉及的模块
4. Dubbo SPI
5. Spring Cloud 的 Eureka
内部服务注册表有双缓存:ReadWrite 缓存和 ReadOnly 缓存,会存在短时间内服务上线不可见的情况
7. JVM 基础
jvm 内存结构介绍、代码调用过程中的内存流程
堆 虚拟机栈 本地方法栈 程序计数器 元空间
栈帧的入栈出栈过程,与线程绑定,操作数栈用来记录操作位点
Java 文件被跨平台的虚拟机编译为 .class 二进制文件,接着加载类信息到方法区,验证,准备阶段初始化类变量为默认值,解析初始化卸载
Java 如何分配内存,多线程呢?
分为堆栈(栈上分配),每个线程一个线程栈,里面独立的有本地变量,共同引用了来自堆的对象
JVM 的内存模型
- 程序计数器
- 虚拟机栈
- 虚拟机堆
- 本地方法栈:栈上分配和逃逸分析以及 TLAB
- 元空间:取代了方法区,而且改为了直接内存,其中运行时常量池是其部分
常见异常:OutOfMemoryError: GC Overhead Limit Exceeded/Java heap space/MetaSpace
方法区是接口,永久代是实现。
内存分配:指针碰撞、空闲列表。内存回收:引用计数、可达性分析
((20200925230314-cidbmyl “【11】JVM 基础入门”))
类加载机制
类的加载过程:
- 加载:指 JVM 读取 Class 文件,并且根据 Class 文件描述创建 java.lang.Class 对象的过程
- 验证:验证文件
- 准备:这时候进行内存分配的仅包括类变量(static),而不包括实例变量
- 解析:JVM 会将常量池中的符号引用替换为直接引用,也就是得到类或者字段、方法在内存中的指针或者偏移量
- 初始化:执行类构造器
<clinit> ()
方法的过程 - 卸载:所有实例被回收、加载该类的 ClassLoader 被回收、Class 对象无法通过任何途径访问(包括反射)
加载类的加载器:
- BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C++ 实现,负责加载
%JAVA_HOME%/lib
目录下的 jar 包和类或者或被-Xbootclasspath
参数指定的路径中的所有类。 - ExtensionClassLoader(扩展类加载器) :主要负责加载目录
%JRE_HOME%/lib/ext
目录下的 jar 包和类,或被java.ext.dirs
系统变量所指定的路径下的 jar 包。 - AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。
双亲委派模型
((20200925230314-cidbmyl “【11】JVM 基础入门”))
OOM 定位、解决方式,流程
gc 算法、扫描算法(优劣)、内存分区
JVM 中如何扫描对象;(root、hotspot 算法)、cardTable)
GC 算法(复制、标记清除、标记整理、内存碎片及解决方式)
GC 异常
什么情况下类被初始化,什么情况下类不会被初始化
类被初始化:
- 反射
类不会被初始化:
- 定义对象数组
8. 缓存基础
1. 你在公司怎么用的缓存,为什么用缓存?那会出现什么问题(缓存击穿、穿透、雪崩)?
- 在获取钉钉 token 时,使用的缓存,2 个小时过期,缓存 1 个小时 45 分钟,不用每次都去请求钉钉的 API。
- 在大规模情况下,会出现数据不一致以及缓存穿透、缓存雪崩。
2. Redis 为什么这么快,它的过期和淘汰策略是什么以及手写一个 LRU 算法
IO 多路复用 + 队列 + 事件分发(单线程) + 多线程操作 + 纯内存操作
3. Redis 高并发和高可用的集群原理
- 高并发:一主多从架构。
- 高可用:主从架构 + 哨兵。
4. Redis 持久化机制、底层原理是什么,有什么优劣?
- RDB:还可以做冷备,通过 runid 做 slave 的备份全量复制。按时间间隔全量备份写入磁盘。
- AOF:后者保证数据不丢失(1 秒)。每次操作都会写入 os cache,然后每隔一段时间写入磁盘。当 AOF 大到一定程度会执行 rewrite 操作,基于当前内存数据重新构造一个 aof 文件,将旧的 aof 文件删掉。
当同时开启了 RDB 和 AOF。重启时默认使用 AOF 恢复数据。因为 AOF 比 RDB 数据更完整。
RDB 优点:
- 基于 fork 子进程,对于主进程影响小。
- 基于 RDB 恢复数据时速度更快。
- 而且生成的 RDB 文件可以放在分布式存储中。方便做冷热备份
RDB 缺点:
- 丢数据会更多
- fork 子进程时,当快照文件过大时,会严重影响主进程。
AOF 优点:
- 默认间隔 1s ,丢失数据会更少
- 基于 append-only 写入,写入性能高
AOF 缺点:
- 因为 AOF 记录指令日志。所以AOF 文件比 RDB 文件大
- 影响写 QPS
5. 集群中 Redis key 如何寻址,寻址算法?hash 一致性算法
寻址算法基于 hash slot 寻址。固定 16739 取余。
使用 redis cluster,而不是 replication + sentinel。redis cluster 针对海量数据+高并发+高可用场景。
redis cluster:自动将数据进行分片,每个 master 上放一部分数据。内部支持当 master 不可用时还可以继续工作。
redis cluster 不仅有 6379 端口,还有 16379 端口,用于集群间的通信。
hash 算法演进:hash->一致性hash(自动缓存迁移)+虚拟节点(解决负载均衡问题),当某个机器宕机,会影响部分机器->redis cluster的hash slot,固定有 16739 个 slot,直接基于这个 16739 取余,就算有机器宕机,还是基于 16739 取模,这样对其它机器无影响。
redis cluster 的节点通信:内部使用 gossip 协议进行通信,元数据分布式存储,好处在于每个节点都存储了元数据,缺点在于会通信同步的的延迟。
6. 缓存、数据库双写不一致问题
先修改数据库、再删除缓存失败怎么办?这个时候先删缓存、再写数据。
- 删缓存成功、写数据库失败。这样其它的请求过来虽然请求的数据库是旧数据,但是写入缓存之后,是一致的。
- 线程 1 在删缓存成功、写数据库减少过程中,线程 2 读缓存为空,读数据库为 1000,这时候就设置了缓存为 1000。线程 1 数据库减少 1,变成 999。使用内存队列,同一个商品 id、订单 id 取模进行分发操作缓存和数据库,实现原子化。
- 分布式,多机器下,内存队列单点热化,在 ng 层就实现负载。
7. Redis 并发写的问题,即顺序问题
解决方案在于分布式锁和数据库中的时间戳版本号,不要用旧版本号覆盖新的版本号即可。
8. 公司的 Redis 架构,多少台,多少内存,多少核,CPU,主从架构,QPS?
9. 热点 key 的缓存,以及大对象的缓存问题解决。
10. Redis pipline 是全双工还是半双工
11. jedis 是否使用连接池,如何处理有序数据
12. Redis 数据结构类型及源码简述;
(String、Hash、ZSet、Set、Dict、GEO、HyperLogLog、BloomFilter、Stream、Bitmap 等)
13. Redis 并发竞争 Key 问题
- 分布式锁
- 写入数据库的时候,需要保存一个时间戳。假设时间戳如下
系统 A key 1 {valueA 3:00}
系统 B key 1 {valueB 3:05}
系统 C key 1 {valueC 3:10}
那么,假设这会系统 B 先抢到锁,将 key1 设置为{valueB 3:05}。接下来系统 A 抢到锁,发现自己的 valueA 的时间戳早于缓存中的时间戳,那就不做 set 操作了
14. Redis Replication 全量复制过程
- master 执行 bgsave,本地生成 rdb 快照文件(通过配置可以设置文件是否落盘)
- master -> slave
- master 在生成 rdb 过程中,期间的命令写入内存
- 如果复制过程中,内存写入数据过多(可配置)那么复制失败
- slave 收到 rdb,清空旧数据,然后加载 rdb 到内存,并提供服务(此时是旧数据)
- 如果 slave 开启了 aof,那么会立即执行 bgrewriteaof,重写 aof。
slave 第一次连接 master,会执行全复制。它们都会维护自己的 offset,slave 会上报到 master。
master 维护了 backlog,大约 1M,用来解决复制过程中的断传,后面进行增量。
当 master 回滚数据时,slave 会通过 run id 和 master 不同来发去全量复制。
15. Redis sentinel 机制
类似 zk 的功能,sentinel 自身高可用集群,用来监控 master 和 slave,负责 Redis 整体集群的高可用。
16. Redis 的数据丢失和脑裂问题
问题:由于 master->slave 异步,可能会丢失
解决:min-salves-write 和 min-slaves-max-log 配置,如果 slave 复制和 ack 延迟太长,master 拒绝写请求,保证数据不会丢,此时客户端可以将数据临时写入 MQ,定时任务将 MQ 刷入 Redis
问题:由于网络分区,多个 Redsi 集群互不通信,有多个 Master 接收写入,当脑裂恢复后,其它 Master 的数据会丢失。
问题:和丢失数据解决方案相同,如果 slave 和 master ack 延迟过大,直接拒绝请求,不会导致消息丢失过多。
17. Redis 实现分布式锁的几种方式,优劣
18. Redis 分片
- 客户端分片
- 代理分片
- 一致性hash
- 虚拟节点分片
19. sentinel 的作用,以及选举 slave 为 master 过程
sentinel 三大作用:
- 集群监控:监控 master 和 slave 进程是否正常工作
- 消息通知:某个实例有问题,可以报警给管理员
- 故障转移:master 故障则转移到 slave 中
- sentinel 觉得 master 宕机,主观宕机
- 大多数 sentienl 觉得 master 宕机,客观宕机
- 选举 master 的考虑优先级:
- 跟 master 断开时长
- slave 优先级(配置文件slave priority)
- 复制的 offset(复制进度)
- run id
9. 数据库
1. MySQL 索引类型有多少种
- 主键索引:alter table table_name add primary key_name(
column
) - 唯一索引:alter table table_name add unique(
column
) - 普通索引:alter table table_name add index index_name (
column
) - 全文索引:alter table table_name add fulltext (
column
)
聚簇索引:索引结构和数据一起存放,例如主键索引叶子节点就存了数据,其中 innodb 的 .ibd 文件就包含了该表的索引和数据。
非聚簇索引:索引结构和数据分开存放,例如 MYISAM 的 .MYI 文件包含了表的索引,叶子节点存储索引和索引对应数据的指针,指向 .MYD 文件的数据。
非聚集索引的叶子节点并不一定存放数据的指针, 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
聚簇索引缺点:依赖有序数据,更新代价大(数据被修改索引也被修改)
非聚簇索引缺点:依赖有序数据,回表查(回查 .MYD 数据文件)
其中主键索引叶子节点存 data(聚簇索引,),二级索引叶子节点存主键 key,再回查主键索引树
2. MySQL 索引的使用与优化
覆盖索引、不回表、最左前缀、索引字段选择、数据量大时,优化器可能会使用不好的索引需要使用 force index
3. MyBatis 是如何执行 SQL 语句的
SQLSessionFactory->SqlSession->Executor->StatementHandler->ParameterHandler->TypeHandler->ResultSetHandler
其中二级缓存在解析 mapper 标签时创建,接着再 CachingExecutor 里面的 TransactionalCacheManager
中保存了这个引用,也就是跨 sqlsession(同时也是跨 executor 的,因为创建 sqlsession 时 executor 也会创建新的)。
4. JDBC 获取数据库连接的大概过程(从 getConnection 开始)
- 导包
- 反射注册驱动
- 打开连接获取 connection
- 执行查询,statement
- 获取结果集
- 释放资源
5. PreparedStatement 和 Statement 区别
prepareStatement 继承自 Statement,会先初始化 SQL,先把这个 SQL 提交到数据库中进行预处理,prepareStatement 可以在 SQL 中用?替换变量;
而 Statement 直接 SQL 编译,执行一次编译一次,而且存在 SQL 注入风险
6. MyBatis 的一些 API 接口,比如 SqlSession、sessionFactory 等等
- 参数处理:ParameterHandler
- 结果集处理:ResultSetHandler
- statement 实例:StatementHandler(PreparedStatment/Statement/CallableStatement),例如设置超时时间返回行数
- 执行器:Executor,模板模式,例如 缓存,事务,连接