索引

  1. 数据结构 提升mysql的查询效率
  2. mysql: B+Tree hash

B+Tree

BTree : 自平衡多叉查找树

度:   度越大  树越矮胖   IO次数越少

B+Tree : 优化 --》 非叶子节点不存储data  
          增大度

                  所有data都存储叶子节点,有顺序访问指针
                  适合范围查询

优缺点


占空间

增删改变慢

适合加索引

where

id 

排序分组

关联字段

不适合

增删改比较多

不用来查询的字段

数据重复特别多

字段比较大  text

分类

普通索引 

唯一索引

联合索引

全文索引

最左原则

比如:  create index idxxx on 表(a,b,c)

a,b,c

存储引擎

myisam              表   非聚簇索引 (索引)(索引)  (数据)

innodb  事务  外键   行   聚簇索引 (索引+数据)

SQL优化


怎么找的:  慢查询日志   skywalking 


explain: 执行计划 
        id: 执行顺序
        type: 访问类型  all (全表扫描)

                      range ref const            
                      index(覆盖索引) 
         key: 哪个索引
         extra:  using temporay  分组没用到索引
                 using filesort  排序没用到索引

优化:  
     有很多情况会让我们索引失效 : 
             最左
             like  %
             or
             索引字段做操作
     小表驱动大表

多线程快速回顾

线程创建方式

集成Thread 重写run 

实现Runnable 实现run

实现Callable 实现call   带返回值

通过线程池管理线程

如何停止一个正在运行的线程


线程.interupt() // 将线程的中断状态 改为true

线程.isInterupted()  // 判断线程的中断状态   false   true 中断

如何保证多个线程有序执行

join

A {
   B.join() // A会等待B先执行完毕
}

线程分类: 用户线程(默认) 守护线程 setDaemon(true)
线程的优先级 : 抢占式线程调度机制 优先级 影响争夺时间片的概率 取值: 1~10
线程的生命周期

new Thread()    ==>   新建 NEW
线程.start()    ==>  启动线程 ==>  就绪  (Runnable) ==>  争夺时间片
               ==>  运行run方法 ==> 时间片耗尽 ==> 就绪 

锁,wait,sleep  ==>  阻塞的状态

run方法执行完毕  ==> 线程任务完毕  ==> 销毁

线程安全

多个线程  操作 共享变量  ,可能导致共享变量数据错误

JMM

java 内存模型   
                          主内存

线程1 [工作内存  副本]

线程2 [工作内存  副本]       ticket

线程3 [工作内存  副本]

并发编程三大特性

原子性 

可见性 

有序性

volatile


可见性

有序性

synchronized 同步锁

修饰代码块  、 成员方法 、 静态方法

必须使用对象作为锁:   
    对象头: 锁的信息
    对象监视器:  monitor

要想保证多个线程起到同步的效果,必须使用统一个对象作为锁

优点:保证通知

缺点: 性能

1.6  
无锁  偏向锁  轻量锁   重量级

JUC Lock

加锁:  
   Lock 锁对象 

   锁对象.lock()
   try{
      // 同步代码块
   } finally {
      锁对象.unlock();
   }

Lock 和 Synchronized

内置      api

功能:  lock功能更多些 ,  tryLock(时间)==boolean    读写锁

性能:   并发量不大, 效率差不多  
       并发量大  , lock性能好

ThreadLocal 线程局部变量

  共享变量

  线程[副本]
  线程[副本]
  线程[副本]

  项目: 存储用户登录信息
  Thread[ThreadLocalMap<ThreadLocal,value>


  ThreadLocal.remove() 删除掉用户信息

CAS compare and swap 比较并替换 java中乐观锁的实现


内存  V
期望值  A
新值   B 

    V == A    true :  没有被修改   将V改成B  
              false: 说明被其它线程修改了  不做处理 

        UnSafe 不能直接使用

        JDK提供了一些原子类供我们直接使用,底层采用CAS

        AtomicInteger  int值

线程池

通过这套api可以让我们 用固定数量的线程,来执行大量的任务

线程可复用,节省了线程的创建和销毁的时间
线程线程的统一管理

线程池API

Executor   执行任务

ExecutorService  项目中使用的线程池接口

ThreadPoolExecutor 线程池的核心实现类  

Executors  线程池的工具类

ThreadPoolExecutor
核心参数

核心线程数
最大线程数
存活时间
存活时间单位
任务阻塞队列
线程工厂
线程拒绝策略

Executors 工具类

newCached  可缓存的线程池      0    int最大值   同步队列[]

newFixed(核心线程数量)   定长的线程池       

newSingle  单例的线程池     只有一个线程工作

schedule   带调度的定长线程池    定长线程池 +  调度(延时执行任务,周期性的执行任务)

项目中


搜索  异步搜索历史

点赞  异步计算热点评论
批量导入   数据分段 使用线程池执行批量插入


基于spring封装后的API

@EnableAsync  // 开启异步处理功能
@Bean  spring封装过后的线程池bean (ThreadPoolExecutor 线程池)

@Async(线程池的名称)  // 该方法使用线程池 异步处理

spring相关面试点

IOC : 控制反转
重要的类

BeanFactory 基础接口 定义了容器最基本的方法
XmlBeanFactory 实现了基本容器功能

ApplicationContext 容器的高级接口 

通过xml来定义容器中的bean   ClassPathXmlApplicationContext

通过注解来定义容器中的bean   AnnotationConfigApplicationContext


IOC容器 初始化的一个流程 

IOC容器 .getBean方法的流程

初始化流程

1. IOC容器bean的配置,  定义xml配置文件  通过bean标签定义对象

2. 加载配置文件,基于dom解析技术 将配置文件内容解析成document文档

3. 容器会将所有的bean标签,每一个bean标签都会封装为一个BeanDefinition对象(bean的定义)

4. 将所有beanDefinition对象注册到容器
     是将这些对象  以bean 的id作为value , beanDefinition本身作为value, 存入到一个map集合中  (DefaultListableBeanFactory 中的beanDefinitionMap ,ConcurrentHashMap)

5. 最后IOC容器会将注册列表中,所有为单例模式的bean,非抽象的  非延迟加载的bean 进行初始化, 初始化后得到的单例对象
   会存储到  单例的map集合中 
   (DefaultListableBeanFactory 中的singletonObjects ,ConcurrentHashMap)

getBean流程

1. IOC容器会在单例的map集合中,根据查询的beanName查询是否有该对象,有直接返回

2. IOC容器会查看是否有父容器,父容器中是否包含该对象,如何包含直接调用父容器的getBean方法获取对象

3. 先查询bean定义的map集合 , 查看是否有关于该bean的定义,如果没有直接报 NoSuchBeanDefinition

4. 查看bean定义的scope属性, 是 singleton 还是prototype
    如果 singleton
        在检查一次单例集合
        创建这个对象
        将这个对象存入单例map中
    如果 prototype
        创建这个对象

5. 创建对象的方法:
    IOC容器会根据你的配置得到 该对象Class
          会根据你的配置选择正确的构造方法
               如果配置了:  factory-method  ==> 使用反射调用工厂方法进行对象的构建
                          有参构造参数      ==> 使用反射调用有参构造器进行对象的构建
                          使用无参构造器  
6. 根据配置完成DI依赖注入

Bean的循环依赖

A -->  B    B --> A  产生了闭环,这种情况成为循环依赖

1. 如果bean的scope都是多例,  会报循环依赖的错误

2. 如果bean他们依赖关系是通过构造器依赖的, 会报循环依赖的错误

3. 如果bean都是单例的,并且关系是通过属性注入的, 默认 不会报错 (可以通过配置关闭允许循环依赖)


spring如何解决了循环依赖:  
    三级缓存: 
       singletonObject : 一级缓存   存放的是完整状态的bean , 对象实例后,所有属性都完成的依赖注入
       earlysingletonObject: 二级缓存  存放原始的bean ,还未完成依赖注入, 解决循环依赖
       singletonFactorys : 三级缓存,  存放 bean工厂对象, 解决动态代理产生的bean的循环依赖问题

**Bean的覆盖问题

在spring的配置中,出现了相同名称的bean 

spring默认允许bean的覆盖,后加载的bean的定义 会把先加载的bean的定义 覆盖掉

如果不想被覆盖,可以通过配置 关闭调用允许bean的覆盖

spring 事务的配置


手动编程事务:   TransactionTemplate 管理事务


声明式事务:   
    AOP配置事务

    注解配置事务:   @Transactional

什么情况会事务失效

1. 数据库本身支持事务  
2. 方法用的不是public
3. 自己将异常try catch
4. 抛出的异常,不属于RuntimeException, 可以在注解后面加上  rollbackFor(异常的class)
5. 分布式事务
6. 事务传播行为 
7. 自身调用

事务的传播行为

一个事务方法,调用另一个包含事务的方法,产生了事务的嵌套

spring定义了7种传播行为:
    Required :  表示该方法必须运行在事务中,如果调用我们的方法包含事务,那么采用该事务即可,如果调用我们的方法没有使用事务,则新建一个事务

    Requires_new:  表示该方法必须运行在事务中,不过不管调用调用的方法是否包含事务,我们的方法都会启动一个新的事务

项目中AOP的使用

AOP的介绍:  面向切面编程

AOP的使用:  1. 引入aop的依赖   2. 定义切面类  3. 定义切入点  4. 定义处理增强的  通知方法 (5种通知方法)

AOP项目中的使用:  统一异常处理 , 统一的事务处理      ,     行为日志记录  , 主从数据源的切换

AOP底层原理: 
    AOP的底层是基于动态代理:
        注

        JDK  jdk自带的   要求被代理对象必须要实现接口
        CGLIB  开源的,需要引依赖   要求被代理的的对象必须能被继承

mybatis相关面试点

mybatis的核心组件

SqlSessionFactoryBuilder 构建器, 配置文件解析,session工厂的创建

Configuration:  核心配置,解析所有配置内容,都会存入到这个配置对象中

MappedStatement: 封装sql描述的对象,mybatis.xml中每一个 select|insert|update|delete 都会封装成这样的一个对象

SqlSessionFactory session工厂对象,用于创建 SqlSession会话对象

SqlSession:  核心的用于和数据库做交互的API,会话对象

Executor: sqlSession中核心的用于和数据库交互的对象,执行器

mybatis运行原理

构建阶段: 
SqlSessionBuilder 的build方法解析配置文件,将解析的全部内容封装到Configuration对象中
在Configuration中  重点  有一个map集合  mappedStatements ==> 存储了所有的xxxx.xml中的
SELECT|INSERT|UPDATE|DELETE的应sql描述   每个描述都封装成 MappedStatement  ms对象
mappedStatements map的格式    key: namespace + "." + sql标签的id    value: ms对象 该sql描述
解析完毕后,builder会构建出 SqlSessionFactory对象

执行CRUD阶段: 
每一次,当我们要操作数据库时,都会通过sqlSessionFactory 获得一个SqlSession 会话对象
代理对象 = session.getMapper(mapper接口);
会基于jdk的动态代理,生成一个代理对象
代理对象.findAll();
代理对象 会拼接 当前 调用接口的 包名+"." +类名+ "." +调用方法名
如: com.itcast.dao.UserMapper.findAll
去Configuration对象的mappedStatement这个map集合中查找对应sql描述 , 如果未查询到会报绑定异常
如果找到了: 
      调用执行器Executor 根据 sql描述执行查询
       Executor 调用JDBC完成数据库的操作( ParameterHandler  ResultHandler TypeHandler)
关闭会话SqlSession

mybatis 动态SQL

if   <where>   
<foreach>  // 批量处理  
resultMap   
<assoiation> <collection>  // 关联查询 
<cache /> // 二级缓存

mybatis批量处理

java   for { mapper.方法  }     效率特别差

Executor 类型设置为 BATCH(批处理)       SIMPLE(默认)    REUSE(可复用)
java   for { mapper.方法  }   效率更高一些

<foreach>  效率最高
拼接成一条sql语句, 批量插入可以使用  insert into (字段)  values (1条),(2条),........

mybatis关联查询

对1的关联查询
A {
  B b;
}

resultMap
    association ==>b  select==>sql fetch=lazy(延迟加载)  eager(饥饿加载)

对多的关联查询
A {
  List<B>  list;
}
resultMap
    collection ==> list select==>sql

mybatis中的缓存

mybatis支持 一级缓存  二级缓存

一级缓存:  无需配置,直接就可以使用   ,称为SqlSession级别的缓存,在sqlSession会有对应的缓存对象
          在同一个sqlSession中,进行相同操作时,无需查询数据库 使用缓存即可

          但是如果在多个会话SqlSession中,无法实现缓存的共享
二级缓存:  需要手动开启 开启方法: 1. 配置EnableCache = true   2. 开启指定mapper缓存 <cache />  3. 对应sql useCache
          二级缓存作用域 :  同一个mapper一个缓存对象


        加载顺序:   二级缓存 -->  一级缓存  -->   数据库

mybatis中的分页实现

基于PageHelper实现的

基于mybatis的插件机制, PageHelper实现了 mybatis 的 Intecepter 拦截器接口,并指明要拦截 Executor.query方法

我们在配置中引入了 PageHelper插件 , 并且配置了数据库方言 

在需要使用分页的时候,只需要你提前调用 PageHelper.startPage(1,10)

对应接下来要执行的sql, 分页拦截器会进行拦截, 拦截后 生成两条sql语句,  根据参数生成一个 查询count的语句 得到的结果设置到page的total中, 在根据语句 及 使用的数据库,拼接一个分页语句  (mysql  拼接  limit (?,?)) 进行分页查询,得到结果也设置到page中

我们就得到分页查询结果

mybatis的mapper接口是否支持方法重载 mybatis,xml中是否可以定义id相同sql

不可以

mybatis.xml需要传递多个参数

指定多个 @Param

封装成一个实体类

map

#取值 和 $取值

# 预编译, 防止sql注入

$    表名  select * from ${}

JVM面试热点

JVM的基本组成

类加载器:  将.class字节码文件加载到内存

运行时数据区:  JVM在运行时所存储数据内存区域

执行引擎:  将java字节码文件 转化计算机指令 交给CPU执行

本地方法接口:(JNI) 用于执行native修饰的方法

JVM内存的划分

线程共享
堆: 内存最大的一块区域,存储java中几乎所有的对象实例 
    OOM

    -Xms 堆最小值   -Xmx

    堆是GC垃圾回收的主要工作场所

方法区: 类加载会将类的信息加载到方法区中,  如: static变量  final变量 Class对象  运行常量池
    OOM

线程私有
JAVA虚拟机栈: 描述方法的内存模型,java中每一个方法的调用, 就是创建栈帧, 栈帧入栈  栈帧出栈 这样的过程
    OOM
    StackOverflowE :
本地方法栈 :  和java虚拟机栈一致,不过描述native修饰的方法
程序计数器:  记录当前线程执行到哪个字节码

GC垃圾回收 - 如何判断对象是可回收对象

引用计数法   可达性分析

可达性分析:  从 一系列的成为 GC ROOTS的对象,开始向下遍历, 所引用的对象 都是可达。
            如果对象到GC ROOT 没有任何引用, 说明是不可达的 可以被回收

GC ROOT:  虚拟机规定:  正在运行的方法 变量所引用的对象
                     已经加载的类,类中静态变量所引用的对象
                     已经加载的类,类中常量所引用的对象

引用类型:  强引用
          软引用
          弱引用
          虚引用

GC垃圾回收 -收集算法

标记 清除
复制
标记 整理

分代收集

GC垃圾回收 分代收集

将堆根据对象存活时间的不同,分为了新生代和老年代

新生代因为对象存活比较短,而且回收比较频繁,采用的算法是复制算法
    复制算法: 将新生代 Eden区    Survivor0  Survior1     默认比例:  8:1:1
             所有新对象都会被分配到Eden区,Eden区满了触发新生代的垃圾回收(minorGC) ,会将Eden区存活的对象,复制到幸存者区

    对象进入老年代条件:  1. 每次垃圾回收 对象的GC AGE + 1 ,默认到达15岁 晋升到老年代
                      2. 如果复制时,幸存者不够大时,对象直接进入老年代
                      3. 可以配置指定的阈值, 超过这个值大小的对象,直接进入到老年代
老年代因为对象存活比较长,且对象都是一些较大对象,采用算法 是 标记 清除  或  标记 整理

    老年代如果满了,触发老年代垃圾回收   majorGC  , 也成为fullGC

GC垃圾回收 垃圾收集器

串行垃圾收集器   STW时间较长

并行垃圾收集器   STW时间短,提高吞吐量

并发垃圾收集器(CMS )    STW停顿时间最短,可以实现低延迟    

G1  java最新的收集器,也是强调低延迟

类加载机制 - 加载的时机

new   putstatic  getstatic  invokestatic

main方法 

子类 对应的父类

使用反射API时,对应的类 会触发类加载

类加载机制 - 过程

加载  
    读取.class文件字节码的二进制流

验证
    检查字节码流是否能够正确的被当前虚拟机加载
       魔数:  0xcafebabe 
       虚拟机版本:
       内容是否正确
准备
    在方法区中给类静态变量分配内存, 将类中所有的静态变量设置一个 零值
        int a = 0
        String str = null

解析
    将所有的符号引用转为直接引用的过程

初始化
    将类中所有的静态变量进行真正的赋值,所有的静态代码块会被执行一次


初始化完毕后,这类就可以使用

类加载机制 - 类加载器

启动类加载器  bootstrapClassLoader   C++  /jre/lib

扩展类加载器  ExtClassLoader          /jre/lib/ext  

应用类加载器  AppClassLoader          classpath 

自定义的类加载器

类加载机制 - 双亲委派模型

当一个类收到加载请求时, 先查看自己有没有父类,如果有父类会交给父类进行加载, 如果父类无法加载 才会自己加载

作用:
   让同一个类 都交给同一个类加载器去加载

JVM故障监控

jconsole :  用于监控jvm的性能运行指标,   内存使用情况   线程的情况  类加载的情况  CPU的使用率

jmap :   可以将指定JVM虚拟机的堆内存进行快照,用于分析一些堆内存的使用情况,   如: 解决内存溢出

jProfiler : 用于分析 堆内存快照,以可视化的形式去分析堆的使用情况

jstack :   可以分析JVM虚拟机中 线程的使用情况,用于排查和线程有关的问题      如: CPU使用率100%      检查死锁

JVM性能调优

堆内存的设置
    -Xms  -Xmx 设置一样大 将总内存百分之70 都分配给堆内存

垃圾收集器的选择
    互联网项目,关注低延迟 :   以CMS为主的  并发垃圾收集器
    CMS: 缺点 1 标记清除 会产生内存碎片    2. CMS比较消耗内存,如果内存不足,会切换到串行收集器进行垃圾回收

        解决:  1. 开启内存压缩功能, 每次fullGC 进行一次内存压缩

              2. 设置老年代达到百分之80 就开始fullGC
调试的信息的打印
    设置打印 class加载日志
    GC垃圾回收日志
    堆栈日志

分布式锁

什么分布式锁

在分布式系统之间,保证某些系统资源同步访问的一种方式。


白话版: 
在分布式项目中,一些资源多个请求都需要操作, 在操作时可能造成数据错误 ,
要想保证不出错,就需要保证多个请求访问这个数据时,进行同步操作

sychronized能否实现分布式锁

不可以实现分布式锁

每个微服务都可能搭建集群,sychronized是jvm级别的锁,只能保证当前jvm多线程同步访问

分布式锁的解决方案

基于数据锁来解决  (行锁: select .. for update)    数据库的性能下降

基于redis来解决  RedLock   Redission框架来实现   基本远离 加锁设置一个key 解锁将这个key删除

基于zookeeper来解决  文件系统  watch监听
    多个客户端请求加锁, 每个客户端都会创建 临时有序节点
                      节点顺序编号最小的获取到锁   ,没有获取到锁的watch监听它的上一个节点

                      执行完毕后,会删除临时节点,  监听的下一个节点会被执行

项目中是怎么具体使用的

我们采用的是redis的解决方案 (redis速度快   redisson高可用  redis高可用  项目中本身就使用了redis)
实现步骤: 
    1. 引入Redission的依赖
    2. 配置RedissionClient  配置redis集群的连接
    3. 基于RedissionClient获取锁对象   RLock 继承  Lock
            .getLock   非公平锁
            .getFairLock 公平锁
    4. 在需要加锁的地方 使用
        lock.lock()
        try{

            // 同步访问的部分
            头条:  评论 数据是存储在mongoDB  评论: 回复 数量  点赞数量 
                多线程并发访问时  点赞数量不准确 (jmeter 压测)

        }finally{
            lock.unlock();
        }

分布式锁的实现原理

Redisson 底层主要使用lua脚本 对redis进行原子性的操作

    加锁:     判断指定的key,在redis是否存在 
                如果不存在: 
                    使用hash结构设置锁:  key       field                       value
                                    加锁的key    客户端: uuid + 线程ID          加锁次数 1
                    设置失效时间
                如果存在:  
                    判断是否是当前客户端加的锁
                        如果是: 说明向重入加锁 

                               将锁的次数+1

                        如果不是: 直接返回失效时间

    续约:      采用watch dog 利用后台线程, 判断加锁的客户端线程是否存活,是否还持有锁  ,每隔10秒重置失效时间 



    解锁:     判断指定的key 是否存在, 是否当前客户端的加的锁

              将锁的value - 1 = count
              如果count > 0 说明是重入锁,还未完全解锁

              如果count == 0  说明已经解锁完毕

                               删除对应的key  

                               其它客户端可以尝试获取锁

如何保证redis的高可用

高可用方案:   主从复制  (主库 挂掉  不能写数据了)
            2.8哨兵模式  (哨兵 -> 主 -> 从 可以保证高可用,但是不能让我们的redis数据实现分布式存储 )
            3.0集群模式  既保证高可用,又实现分布式存储

我们采用的方案:  3主3从 
    redis集群原理: 1. 准备了16384个hash槽, 集群中所有的master节点会分配这些hash槽
                     如3台主节点:  1: 0~5000   2: 5000~1000  3: 10000~16383

                  2. 我们操作时连接集群中的任意一台即可  如果set name 值 
                       redis会对name进行hash运算得到hash值 使用这个值和16384取模 得到值得范围就是0-16383
                     如: 取模的值是5631 那么久会存储到第二台redis的master中

                  3. redis的集群节点采用二进制 ping pong 机制 通信方式 , 可以监控各节点的状态

                  4. 如果某个master节点挂掉了,如果超过半数的master节点认为某个节点挂掉了,将会将该master节点的slave节点升                        级为新的master节点

分布式事务

什么是分布式事务

在分布式架构中,一个操作可能横跨多个微服务,多个数据库,传统的本地事务无法解决,需要使用分布式事务来解决。

基于XA的二阶段提交
TCC try   confirm  cancel
MQ + 本地消息表

项目中如何解决分布式事务

场景1:  实名认证通过需要在自媒体端 ,开通自媒体账户   user微服务(实名认证) ==>  user数据库
                                 feign       wemedia微服务(自媒体账户开通) ==>  wemedia数据库
场景2:  自动审核文章 如果审核通过且到达发布时间,会远程调用其它微服务发布文章
                                              admin微服务(自动审核) 
                                              article微服务(发布文章三张表) ==> article
我们项目中选择的是Seata框架,是一个alibaba 开源的解决分布式事务的框架,内置多种分布式事务解决方案 (TCC SAGA XA AT),我们用的是里面的AT模型 原子性事务, 这种模式 代码的侵入性非常低,效率也还可以,可以保证事务的一致性

具体使用方式:
1. 搭建SeataServer 事务协调器 (统一管理事务的关键)
        在mysql数据库中准备好事务表:  全局事务表,分支事务表,全局锁表

2. 需要进行分布式事务管理的微服务 引入spring-cloud-alibaba-seata依赖
    配置Seata  数据源  事务协调器的连接,

3. 在需要管理分布式事务的发起方法中 添加@GlobalTransaction全局事务注解,代表开启全局事务

4.  在涉及分布式事务数据库中的创建undo_log  存储需要回滚日志信息

Seata的工作原理

seata AT模式主要基于二阶段提交的方式管理分布式事务

加了@GlobalTransaction注解的方法,代表开启了分布式事务,
当方法执行时 会底层通过netty向SeataServer发送请求,注册全局事务

全局事务通过两个阶段来管理事务:
如: 方法中包含两个服务的调用    服务A    服务B

1. 第一阶段:
    服务A 注册分支事务,
        注册成功后   (执行当前方法sql + 新增undo_log日志) 在一个本地事务执行

    执行成功调用服务B

    服务B 在调用请求中,发现全局事务ID参数,也向服务端注册分支事务
        注册成功后   (执行当前方法sql + 新增undo_log日志) 在一个本地事务执行

    (第一阶段,会真实的将各数据库的事务提交)

2. 第二阶段: 
    如果第一阶段操作成功, 将异步的删除 undo_log日志

    如果第一阶段操作失败, 通过全局事务ID 及 分支事务ID 查询各分支事务的undo_log日志, 根据回滚日志的内容进行事务补偿

undo_log的回滚原理

undo_log是一张表,用于存储待回滚的信息
    回滚信息主要是基于反向sql
        如: 事务方法中 执行的insert sql语句 , 那么通过回滚日志可以生成delete 一条sql的语句



    反向sql的具体实现:
        在rollback_info中记录回滚信息
            当执行业务sql时,会记录操作类型 如: delete  update  insert 
            如果是update操作: 会在执行业务数据前 查询出当前数据状态 作为前置镜像存储
                            执行sql
                            再次 查询出变更后的数据状态 作为后置镜像存储
            当需要回滚时, 先查询当前数据 是否和后置镜像数据一致, 如果一致则按照前置镜像的内容进行回滚

            如果不一致,说明数据被事务以外的操作修改,则回滚事务  回滚操作会进行重试, 重试时间可以进行配置

seata的优缺点及遇到的问题

优点:
代码侵入性低, 整合简单
不同场景的事务需求都可以复用
保证原子性, 要么全部成功,要么全部回滚

缺点:
必须是关系型数据库
默认读未提交,可能有脏读
如果设置读已提交, 性能较差
如果数据被Seata以外操作修改,可能会造成回滚失败



遇到的问题:

tinyint(1) 事务回滚时,该字段没有被修改  修改方案:  tinyint(4)

feign服务降级时,需要在服务降级方法中 手动的回滚全局事务     GlobalTransactionContext.reload(RootContext.getXID()).rollback();

zookeeper 分布式协调服务

zookeeper的介绍

apache旗下的开源的分布式应用协调服务,在分布式的架构中可以为分布式应用提供一致性的存储服务,zookeeper的本质,是它维护了一个文件系统 及 watch监听机制

zookeeper文件节点类型

持久化的文件节点
持久化的有序文件节点
临时的文件节点 
临时的有序文件节点

zookeeper应用场景

可以作为注册中心来使用  如: dubbo+zookeeper
可以使用zookeeper管理集群   如:  kafka + zookeeper
可以使用zookeeper分布式锁   利用临时有序节点+watch监听机制实现分布式锁
zookeeper 也是hadoop大数据架构下的重要组件

zookeeper高可用

搭建zookeeper集群  , leader的投票选举,必须超过半数投票才可以被选举为leader  , zookeeper集群至少也要3台

Leader领导者   处理事务请求
Follower跟随者   执行非事务请求操作, 可以投票  ,有被选举权
ObServer观察者   执行非事务请求操作, 不参与投票,没有被选举权

dubbo RPC框架

dubbo的介绍

alibaba 开源的远程RPC调用框架,现在已经属于apache , 在我们项目中主要基于dubbo+nacos实现SOA面向服务的架构。

dubbo的工作原理

1. dubbo中 主要的角色是 Provider 和 Consumer
    provider负责提供服务
    consumer负责消费服务
    在dubbo中,服务主要指的是接口
2. 在Provider角色中,提供服务接口的实现类,一般Provider都是和容器搭配使用,
   通过容器启动dubbo的提供者对象,提供会将自己的服务地址信息 注册到注册中心中
3. 在Consumer角色中,会订阅注册中心,得到服务的地址信息,将服务的地址信息缓存到本地
    通过服务的地址使用dubbo进行远程的调用
4. 在monitor角色中,会统计服务的调用相关信息

dubbo具体的使用方式

部署注册中心nacos

提供者配置:   提供者应用名称    服务的协议  服务的端口号  注册中心地址
            具体提供的服务 要加上 注解 @DubboService

消费者配置:   消费者应用名称    注册中心地址
            在引用的接口属性上,加 @DubboRefrence

dubbo 拆分了多少个提供者 多少个消费者  
      提供者里,提供了多少个接口

dubbo使用的协议

我们使用的是 默认的dubbo协议:    长链接 使用 netty框架(基于nio) 进行异步通信 , 采用hession序列化机制

http

webService

redis

dubbo支持的注册中心

我们使用的注册中心 是nacos

zookeeper 
redis

simple
multicast

dubbo的负载均衡

默认使用 随机
轮询
最少活跃数
一致性hasn 

@Refrence(loadBanlance="算法")

dubbo的集群容错

默认使用   
重试的方式,   如果调用超时  默认会重试两次, 重试的次数可以修改  (适合读)

快速失败: 只调用一次,出错立刻返回错误 (适合非幂等性)

安全失败: 用于不重要的操作,直接忽略错误, 如:记录日志

定时重试: 不会立刻重试,会间隔一段时间重试

并行调用: 调用所有提供者,哪个先返回结果就使用哪个结果, 一般用于快速读操作

广播调用: 依次调用所有提供者,如果有提供者出错,立刻返回失败, 广播是让所有服务都调用一次

dubbo的超时机制

默认操作timeout 时间 1s

一般我们会在服务端设置超时机制, 大部分的接口超时时间都在2s

dubbo的版本管理

都 是通过3个属性来确认具体的服务提供者

服务名称: 接口的全限定类名  如: com.itcast.dubbo.UserService
分组名称:  @Service (group="分组名称")
版本号:   @Service (version="版本号")

dubbo的服务治理

dubbo提供了服务治理工程,来管理我们线上所有的dubbo服务,这个工程叫做dubbo admin工程,可以管理服务的路由,黑白名单,修改负载均衡规则,修改权重

dubbo的依赖检查

默认 依赖检查为true dubbo启动时,会检查服务接口对应的提供者是否存在,如果不存在就会报错

修改为false 关闭检查

@Refrence(check = "false")

springboot相关面试点

springboot介绍

Spring: 配置文件繁琐, 项目规模变大,依赖越来越多,维护困难

SpringBoot:  SpringBoot 可以让我们更简单,更快速的开发基于Spring的项目,独立的,生产级的项目

springboot优缺点

提供了起步依赖,将某种功能的全部依赖进行了整合
提供了自动配置, (约定大于配置,提前准备了配置,自动生效)
内置了web服务器,(如:内置tomcat,部署启动更方便)
提供了健康安全检查机制, (SpringBootAdmin监控工程)

缺点: 
入门比较简单,可能会让程序员不会深入的了解技术的底层
出现复杂底层问题,可能会难以排查
起步依赖,对大量的依赖进行了整合,但是不同的依赖可能会引用相关连的依赖,在大型项目中要注意彼此的关联依赖,否则会造成依赖关系混乱

spring-boot-starter的作用

starter依赖,  起步依赖.  将某种功能的依赖进行了整合,并提供了自动配置相关文件

举例说明常用的spring-boot-starter

spring-boot-starter-web

spring-boot-starter-aop

spring-boot-starter-data-mongodb

spring-boot-starter-data-redis

spring-boot-starter-jdbc

springboot核心配置文件介绍

约定由于配置,大部分的配置都准备了默认值,如果你想修改默认值,可以在核心配置文件上进行设置

application.yml  和 application.properties配置

如果同时定义 application.yml 和 application.properties
两个配置文件都生效,取配置的并集
如果key相同的话,properties配置的优先级更高


另外: 
bootstrap.yml .properties:  启动配置,优先级大于application.yml .properties



配置文件可以存在下列4个文件夹中:
项目根目录/
项目根目录/config目录/
resouces/
resouces/config目录/

springboot多环境配置

准备不同环境的配置文件 如:
application.yml  主配置文件
application-dev.yml  开发
application-prod.yml  生产
application-test.yml  测试


- **配置文件: 再配置文件中配置:spring.profiles.active=dev  # 激活开发环境的配置


- 虚拟机参数:在VM options 指定:-Dspring.profiles.active=dev
- **命令行参数:java -jar XXX.jar --spring.profiles.active=dev**

springboot常用注解

@SpringBootApplication
    @SpringBootConfiguration 配置注解

    @EnableAutoConfiguration 自动配置

    @ComponentScan 组件扫描

@ConfigurationProperties(prefix="redis") 属性配置


@SpringBootTest 单元测试的注解

springboot自动配置原理

@SpringBootApplication
    @SpringBootConfiguration 引导类就是配置类
    @EnableAutoConfiguration   开启自动配置 * 
    @ComponentScan  组件扫描

@EnableAutoConfiguration
    @Import(XXXXImportSelector.class) // 导入 配置选择器返回的配置列表 

AutoConfigurationImportSelector // 这个类可以查询出 需要自动配置的类列表
    selectImports // 查询要导入的类
        1. 查询当前springboot所有候选配置
              加载地址: 所有依赖jar下的META-INF/spring.factories 配置文件
              加载该配置下,所有key为EnableAutoConfiguration的配置类
        2. 得到所有的候选配置后,需要对候选配置进行过滤
              2.1 先将候选的配置进行去重
              2.2 查看是否有配置排除的配置类 (@SpringBootApplication=exclude)
              2.3 对剩余的配置进行条件过滤
                      在配置类上会有一些条件注解  @ConditionalXXXXX
                      必须满足这些条件 该配置才会生效
                       @ConditionalOnClass (指定的类存在才会生效)

                       @ConditionalOnMissingBean ( 指定的bean不存在,才会生效)
        3. 过滤后剩下的配置,就是要自动导入的配置类.通过@Import注解进行导入
            导入后,提前准备好的配置属性 和 一些配置对象就会生效
            如: spring-boot-start-redis时候

            可以直接使用 RedisTemplate 对象,它会自动连接 localhost:6379的redis

项目中是否自定义过starter依赖

在我们项目中,大量使用了自定义starter
    如:  分布式调度xxljob-starter依赖
        分布式事务seata-starter依赖
        阿里云OSS  阿里云内容安全starter依赖

    在该starter起步依赖中,引入对应所需要的注解,然后写好自动装配的配置类
    在resouces下创建 META-INF/spring.factories配置文件,按照springboot的格式要求
    key: EnableAutoConfiguration  value: 我们写好的配置文件

    这样当其它微服务工程引入这个依赖后,既可以自动加载里面的bean

springboot项目健康监控


在springboot的工程中 , spring-boot-starter-actuator 健康监控依赖,可以对外暴露一些web接口,通过这些接口可以检查当前springboot项目的健康情况

又提供了 SpringBootAdmin 健康检测中心,可以基于actuator提供web接口 为我们展示友好的UI页面,来监控你的SpringBoot工程健康情况

能监控的内容非常多:  
    实例是否在线
    注册bean的情况
    线程的情况
    内存的情况
    ....

springboot打包的jar 和 普通的jar区别

springboot打包的jar    (指的是 spring-boot-maven-plugin 插件打包的jar包)

普通的jar包被依赖后,里面的类可以直接使用

springboot打包的jar里面的类不可以被依赖工程引用,原因: 生成的class文件在这\BOOT-INF\classes 目录中,普通的jar class文件直接在包名中

springcloud相关面试点

微服务架构介绍

微服务是一种架构风格,一个大型复杂的软件应用,由一个或多个微服务组成,每个微服务都负责业务功能,每个微服务都可以被独立部署,服务与服务之前可以通过轻量级的协议进行通信

springcloud介绍

微服务是一种架构风格,SpringCloud是具体实现微服务的架构技术,SpringCloud实际上是依托Spring强大的整合能力,将其它的解决微服务中各类问题的框架进行整合,换句话说我们可以成为SpringCloud解决微服务的工具集 包含了多个组件

我们项目中使用的Cloud 是基于springboot2.3.2 , springcloud.Hoxton.SR8

springcloud常用组件

Spring  Cloud   Feign 服务调用
                Ribbon 负载均衡
                Hystrix 服务熔断
                Gateway 服务网关
                Eureka  注册中心
                Config  配置中心

                Alibaba Nacos 注册中心 、配置中心
                Alibaba Seata 分布式事务
                Alibaba Oss  对象存储

注册中心 Netflix Eureka 或 Nacos
注册中心介绍

提供了  服务的注册、服务的发现及服务状态监控

工作流程介绍

统一的搭建注册中心服务端,  
如果是eureka 需要自己搭建SpringBoot项目 引入服务依赖
如果是nacos 则直接在nacos官网下载对应版本的服务端即可 我们使用的nacos版本:  1.3.2

在微服务中引入注册中心客户端的依赖   spring-cloud-starter-alibaba-nacos-discovery

在微服务的配置文件中,配置nacos服务端的地址, 也可以使用namespace 为不同的环境进行隔离

当微服务启动时,会将自己的服务信息注册到nacos, 并且会定时的发送心跳来告诉服务端自己还存活

作为消费者可以通过注册中心拉取注册的服务信息进行调用

注册中心高可用

Eureka 和 nacos都提供了 高可用集群方案

可以运行3台nacos搭建高可用集群,需要已mysql作为数据持久化方案

Netflix Ribbon负载均衡
负载均衡组件介绍

基于负载均衡算法,从注册的服务列表中,选择指定服务去调用的组件

支持负载均衡算法

默认的算法: 轮询
随机
重试
可用性过滤
响应时间权重

工作原理

开启了负载均衡客户端后,原本的请求会被负载均衡拦截器拦截,拦截后 会获取请求路径中的host部分,
比如:  http://user-service/user/2   获取到服务名称: user-service服务,得到服务名称会从注册信息中得到服务名称所对应的 服务列表 , 获取当前配置的Rule (负载均衡算法规则对象,默认轮询)
规则,使用规则算法在服务列表中选择一个地址进行调用

Hystrix 熔断器
熔断器的介绍

通过隔离访问第三方服务,防止出现级联失败,为微服务提供保护

基本使用

基于Feign一起使用的 , Feign内部集成了Hystrix, 在配置中开启即可 feign.enable.hystrix=true ,
为这个Feign接口创建一个实现类,实现类的实现方法就是Feign的服务降级方法


ps: 如果要想知道远程调用的微服务出现什么异常了
可以让服务降级实现类 实现 FallbackFactory,需要实现create(Throwable 异常)方法,在该方法中能够得到远程调用的异常对象

工作原理

服务降级: 为我们微服务方法,准备一个兜底的策略,当服务调用出现问题时 可以执行这个降级方法

线程隔离:hystrix为我们的服务调用,准备了一个小型的线程池 (默认 10个核心线程),当我们调用其它微服务时,时间是通过线程池的线程进行调用,我们可以设置一个隔离时间 如果超过隔离时间 还没有得到响应结果,直接走服务降级方法。 线程池满了也会直接走线程降级方法。

服务熔断:如果响应时间比较慢的服务经常被调用,那么调用线程都会等待隔离时间,会影响程序的效率,如果一个服务经常出现错误或者延迟,达到一个默认的比例(默认最近20次调用 超过百分之50请求出错)开启熔断状态,熔断状态所有的请求全部直接走服务降级方法,这个过程会持续5S钟, 5S后会尝试放行请求如果成功,则关闭熔断  如果失败再一次的开启熔断状态

Feign 服务调用
服务调用介绍

声明式的Http服务调用客户端,在使用时我们只需要声明接口,在接口上做一些注解配置 如:指定哪个微服务,指定哪些方法即可,在调用时 只需要通过@Autowired注入该接口 实例对象即可

基本使用

1. 引入spring-cloud-starter-openfeign依赖
2. @EnableFeignClients 开启feign的注解扫描
3. 要调用哪个微服务 对应的声明一个接口  @FeignClients(微服务名称)
4. 要调用的该服务的方法,通过SpringMVC注解定义即可  
5. 具体调用时,通过@Autowired 注入接口实例

负载均衡

Feign内置负载均衡功能,内部整合了Ribbon ,而且还可以设置Feign重试次数

熔断器

Feign内置了 Hystrix熔断器,需要通过配置文件开启  feign.enable.hystrix = true 开启熔断功能

工作原理

当我们开启 @EnableFeignClients时,相当于开启了Feign注解扫描

扫描所有标注了@FeignClients的接口, 使用JDK动态代理技术为该接口创建一个代理对象

代理对象在执行对应的接口方法时 会根据配置生成要调用的路径: GET http://user-service/user/2

底层使用Ribbon进行一个负载均衡的http调用

SpringCloudGateway 服务网关
服务网关介绍

API网关是微服务的入口,所有请求都经由网关路由到指定的微服务,而且网关还可以实现统一鉴权,过滤,限流等功能

我们用的网关是SpringCloudGateway    (netflix zuul)

基本使用

1. 搭建网关工程 引入网关依赖 spring-cloud-starter-gateway

2. 进行网关的配置
     路由配置
         routes:
             - id: 路由的id
               uri: 路由的目的地 lb://微服务名称
               predicates: 断言 判断是否满足路由的条件
                   path: /user/**
               filter: 过滤器
     过滤器配置:
         局部过滤器: 去除路径前缀过滤器

         全局过滤器: GlobalFilter接口  全局校验token
     限流的配置
         配置限流过滤器 + redis
         限流算法: 令牌桶算法     1. 桶的容量   2. 每秒生成令牌的数量  3. 令牌key的规则
     跨域的配置
         Cross跨域配置

Alibaba Nacos 配置中心
配置中心介绍

在微服务架构中,每个微服务都需要有自己的配置,如果在生产环境修改配置都到指定的服务中修改,维护起来比较麻烦,配置中心可以实现统一配置,配置管理,配置版本管理,配置隔离 配置的动态刷新

基本使用

1. 搭建nacos服务端
2. 微服务中配置nacos 配置依赖  spring-cloud-starter-alibaba-nacos-config
3. 在微服务配置文件中(bootstrap.yml)指定配置中心地址  dataId = 服务名称+环境名称+配置后缀

动态刷新

配置中心发生配置变更好会动态更新微服务中的配置,无需重启微服务

多环境配置

配置中心可以通过 namespace | group 来隔离不同环境的配置

共享配置

共享配置文件,可以配置多个微服务都需要使用的通用配置

shared-configs: 读取共享配置文件

高可用

可通过多台nacos 实现nacos集群, 数据需要持久化到mysql数据库中

ElasticSearch面试点

elasticsearch的基本介绍
es solr lucene关系
什么是倒排索引
项目中es的使用
es集群的高可用
es的脑裂现象
es和数据库数据一致性问题
几百亿数据ES的优化

XXL-JOB相关面试点

xxljob的介绍
xxljob项目中的使用
xxljob的工作原理
xxljob的高可用

Redis相关面试点

redis的数据结构: https://www.cnblogs.com/ysocean/p/9080940.html
redis的应用场景
redis的持久化
redis缓存淘汰策略 https://www.cnblogs.com/ysocean/p/12422635.html
redis的高可用
集群: https://www.cnblogs.com/ysocean/p/12328088.html
哨兵: https://www.cnblogs.com/ysocean/p/12290364.html
redis基本使用
redis缓存穿透、雪崩 https://www.cnblogs.com/ysocean/p/12452023.html
redis的bitMap 实现 布隆过滤器 https://www.cnblogs.com/ysocean/p/12594982.html

Kafka相关面试点

kafka如何保证消费消息的幂等性

每一条消息都有 offset偏移量 : 可以将处理消息后的偏移量 缓存到redis中

可以通过消息数据中,保证唯一性的字段去数据库中判断是否处理

kafka如何保证消息不丢失



kafka如何保证集群高可用



MongDB相关面试点

SkyWalking相关面试点

ELK相关面试点

CI&CD相关面试点

常见GIT命令

常见MAVEN命令

常见LINUX命令