超全的java后端体系
2025-08-09 12:05:16   15  举报
       
        
         
          
         
         
         
         
         
         
      
          
           
          
          
          
         
         
        
       
      
        AI智能生成
       
      
        Java思维导图,超全
       
       
      
           作者其他创作
          
          
           大纲/内容
          
         
             基础
             
            
              八个基本数据类型
              
               
              
             
             
              关键字
              
             
               访问权限
               
              
                public
                
               
                 所有都可以访问
                
               
                protected
                
               
                 本类和子类,以及同一包的其他类可以访问
                
               
                不写默认
                
               
                 本包内可以访问
                
               
                private
                
              
                 本类可以访问
                
               
               static
               
              
                修饰内部类
               
               
                修饰代码块
               
               
                修饰成员变量
                
               
                 类变量,在内存中只有一个副本,被所有对象共享
                
               
                修饰方法
               
              
               final
               
              
                修饰变量
                
               
                 基本数据类型值无法被修改。
                 
对象、数组等引用无法被修改,但是可以改变其内容
                
               对象、数组等引用无法被修改,但是可以改变其内容
                修饰类
                
               
                 无法被继承,String就是final类
                
               
                修饰方法
                
              
                 可以被子类继承,无法被重写
                
               
               transient
               
              
                被修饰的变量不会被序列化
               
              
               finally块一定会执行吗?
               
             
                不一定,当执行了System.exit(0)语句,导致 JVM 直接退出
               
              
              抽象类 & 接口
              
             
               抽象类
               
                
              
              - 不能被实例化
 - 子类可以是抽象类或者普通类
 - 抽象方法不能有方法体,如果子类是普通类则必须实现抽象方法
 
               接口
               
                
              
              - 接口里所有方法都是抽象方法,必须是public类型,1.8后可以有default默认实现方法,静态方法
 - 接口可以多继承
 - 如果不是抽象类实现接口,则必须要实现接口中所有方法
 - 接口中的变量会被隐式的加上public static final
 
               各自应用场景(设计层面区别)
              
             
              重载 & 覆盖
              
             
               重载
              
              
               覆盖override
              
             
              I/O
              
             
               背景:用户进程无法操作I/O设备(磁盘),必须通过内核来完成完成,然后将内核读取到的数据拷贝到用户进程
              
              
               模式
               
              
                BIO
                
               
                 用户进程发起I/O操作,由用户空间转到内核空间,内核等待数据准备完成,然后拷贝到用户空间,这期间用户线程是
                 
                  阻塞
                 
                 的
                
                
                 同步阻塞。每个连接都会创建一个线程,造成资源极大浪费
                
               
                NIO
                
               
                 所有连接(channel)注册到一个多路复用器(selector)上,通过轮询的方式
                 
,检查就绪的channel,准备好则进行读写操作
                
                ,检查就绪的channel,准备好则进行读写操作
                 同步非阻塞。单个线程处理多个请求,节约系统资源
                
                
                 java层面的NIO封装了IO多路复用
                
               
                I/O多路复用
                
               
                 通过单线程就可以监视多个文件描述符是否就绪(可读、可写、异常)
                
                
                 函数
                 
                
                  select
                  
                 
                   进程可以通过把一个或者多个fd传递给select系统调用,进程会阻塞在select操作上,这
                   
样select可以帮我们检测多个fd是否可读写
                  
                  样select可以帮我们检测多个fd是否可读写
                   优点
                   
                  
                    基本所有系统都支持(保底)
                   
                  
                   缺点
                   
                 
                    随着FD(文件描述符)数量增多导致性能下降,时间复杂度O(n)
                   
                   
                    操作系统对单进程FD有数量限制(默认1024),单机支持的tcp连接少
                   
                  
                  poll
                  
                 
                   将用户传入的数组拷贝到内核空间,然后查询每个fd是否可读写
                   
                  
                  
                   优点
                   
                  
                    通过链表存储FD,所以没有最大FD数量限制
                   
                  
                   缺点
                   
                 
                    随着FD(文件描述符)数量增多导致性能下降,时间复杂度O(n)
                   
                  
                  epoll
                  
                
                   优点
                   
                  
                    将轮询改为回调(事件通知机制),不会随着FD增多而性能下降,事件复杂度O(1)
                   
                   
                    fd是操作系统的最大文件句柄,远大于1024
                   
                  
                   缺点
                   
                 
                    只有linux支持
                   
                  
                 应用
                 
                 
               
                  redis
                  
                 
                 
                  HTTP 2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求
                 
                
                AIO
                
              
                 用户进程发起read后,立即返回。内核收到read后,会等到数据准备完成,然后将数据拷贝到用户内存,完成后通知用户进程
                
                
                 异步非阻塞
                
               
               零拷贝
               
              
             
              深拷贝 & 浅拷贝
              
             
               clone()是浅拷贝
              
              
               如何深拷贝
               
             
                序列化->反序列化
               
               
                转Json,,然后转成对象
               
              
              特殊类
              
             
               Object
               
              
                equals() & hashCode()
                
               
                 equals()
                 
                
                  默认比较两个对象的地址是否相等
                 
                 
                  可以重写,属性一样就可以相等
                 
                
                 hashCode()
                 
                
                  返回一个对象的hash值
                 
                 
                  两个对象相等,hash值一定相等,但是hash值相等,两个对象不一定相等
                 
                
                 ==
                 
               
                  基本类型比较值是否相等,引用类型比较引用地址是否相等
                 
                
                toString()
               
               
                wait(),notify(),notifyAll()
               
               
                getClass()
               
               
                clone()
                
               
                 浅拷贝
                
               
                finalize()
                
              
                 对象被gc的时候会调用此方法
                
               
               String
               
              
                String s = new String("abc")创建了几个对象
               
    
               
                String/StringBuilder/StringBuffer
                
              
                 可变性
                 
                
                  String内部value是final修饰,不可变类。所以每次修改都会产生新的对象
                 
                 
                  StringBuffer 和 StringBuilder 是可变类,字符串的变更不会产生新的对象
                 
                
                 线程安全性
                 
                
                  String因为不可变,所以是安全的
                 
                 
                  StringBuilder不安全
                 
                 
                  StringBuffer通过synchronized保证安全
                 
                
                 性能
                 
                
                  String每次拼接和修改都会产生新的对象,性能最差
                 
                 
                  其次是StringBuffer,因为有锁的开销
                 
                
                 存储位置
                 
               
                  String =》方法区
                 
                 
                  StringBuffer 和 StringBuilder =》堆
                 
                
               SimpleDateFormat
               
              
                线程不安全
                
               
                 因为parse方法里的establish方法里cal.clear,cal.set,retrun cal不是原子操作
                
               
                解决
                
              
                 每个线程new SimpleDateFormat(),缺点造成大量垃圾
                
                
                 线程隔离,放到ThreadLocal里,每次使用时候get
                
                
                 锁
                
                
                 在 Java8 里面引入了一些线程安全的日期 API,比如 LocalDateTimer、DateTimeFormatter 等
                 
                
               
               包装类对象
               
             
                自动装箱
               
               
                自动拆箱
               
               
                缓存池设计
                
               
                 Integer/Long
                 
                
                  
                   缓存
                  
                  、自动装箱拆箱
                 
                 
                  在-128到127间的值,valueOf方法返回的都是缓存池的对象,是同一个对象。
                  
范围之外是new一个新对象
                  
                范围之外是new一个新对象
                   Integer.valueOf(int i)
                  
                 
                 Byte
                 
                
                  都是从缓存池获取,valueOf返回都是同一对象
                 
                
                 作用
                 
               
                  减少空间使用,提升性能
                 
                
                无缓存池设计
                
              
                 Float/Double
                 
                
                  valueOf 每次都是new一个对象,没有缓存设计
                 
                
                 为什么有的有缓存设计有的没有
                 
               
                  小数无法确定缓存个数,整数可以确定
                 
                
              反射
              
             
               通过获取的字节码文件然后将方法、属性、构造方法映射到Method、Field、Constructor等类
              
              
               为什么性能差
               
              
                优化
                
              
                 每次获取Method、Field、Constructor都是要新建对象的,所以如果频繁调用,可以将其缓存起来
                
               
               获取Class对象
               
              
                类.class
               
               
                对象.getClass()
               
               
                Class.forName("全限定名")
               
              
               优点
               
              
                增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
               
               
                提高代码的复用率,比如动态代理,就是用到了反射来实现
               
               
                可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用
               
              
               缺点
               
              
                性能相对直接调用差
               
               
                反射可以绕过一些限制访问的属性或者方法
               
              
               注解
               
             
                反射
               
              
              动态代理
              
             
               在运行时动态地生成代理类,在不修改源代码的情况下,为原有的类提供额外的功能
              
              
               技术
               
             
                jdk
                
                
               
                 被代理类必须实现接口,运行期间生成字节码,实现目标类接口
                
               
                cglib
                
               
                 运行期间使用ASM框架生成字节码,
                 
                  
                   继承目标类
                  
                 
                 ,所以
                 
不能代理final修饰的类和方法
                
               不能代理final修饰的类和方法
                javassist
                
              
                 是一个强大的字节码操作工具,可以运行时
                 
                  修改类的字节码
                 
                 ,从而可以对目标类进行功能增强和修改
                
               
              java8新特性
              
             
               lambda
              
              
               Stream流
               
              
                stream()
                
               
                 有序
                
                
                 线程安全
                
               
                parallelStream()
                
               
                 无序
                
                
                 多线程,非线程安全
                
                
                 forkJoin
                
                
                 并行不一定性能最好,要根据场景测试。比如子任务耗时短,频繁线程切换反而更耗时
                 
                
               
                原理
               
               
                常用操作:foreach、filter、map、flatMap、sorted、distinct、count、min、max、skip、limit、collect、anyMatch、allMatch、reduce、findFirst、findAny
               
              
               Option
              
              
               方法与构造函数引用
              
              
               接口中可以有静态、默认方法
              
              
               元空间
              
              
               @FunctionalInterface
               
             
                一个接口只有一个未实现方法
                
              
                 Function 函数型接口:有一个输入参数,有一个输出
                
                
                 Predicate 断定型接口:有一个输入参数,返回值只能是 布尔值!
                
                
                 Consumer 消费型接口: 只有输入,没有返回值
                 
                
                
                 Supplier 供给型接口: 没有参数,只有返回值
                
               
              异常
              
             
               Throwable
               
              
                Error
                
               
                 系统环境问题引起的异常
                
               
                Exception
                
              
                 CheckedException
                 
                
                  是程序在编译阶段必须要主动捕获的异常,遇到该异常有两种处理
                  
方法通过 try/catch 捕获该异常或者通过 throw 把异常抛出去
                 
                方法通过 try/catch 捕获该异常或者通过 throw 把异常抛出去
                 RuntimeException
                 
               
                  运行时的错误,它可以被捕获并处理
                 
                
               必须try...catch/throw
               
              
                CheckedException
               
               
                Exception及其子类,但不包括RuntimeException及其子类
               
              
               无须try...catch/throw
               
             
                Error以及子类
               
               
                RuntimeException以及子类
               
              
              SPI
              
             
               Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由
               
ServiceLoader读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类,从而给程序提供拓展功能
              
              ServiceLoader读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类,从而给程序提供拓展功能
               缺点:无法按需加载,会加载所有实现类
              
              
               demo
               
             
                定义接口,以及实现接口
               
               
                
                 META-INF/services
                
                文件夹下创建一个文件,文件名就是接口全限定名
               
               
                文件中输入接口实现类的全限定名
               
               
                通过ServiceLoader.load(接口名.class)加载其实现类
               
              
              泛型
              
             
               用于类、接口、方法
              
              
               作用
               
              
                代码复用
               
               
                类型安全
                
               
                 编译时类型检查
                
               
                消除强制类型转换
               
              
               泛型上限
               
              
                <? extends Xxx>
                
              
                 只接收Xxx类型或者Xxx类型的子类
                
               
               泛型下限
               
             
                <? super Xxx>
                
              
                 只接收Xxx类型或者Xxx类型的父类
                
               
              序列化&反序列化
              
             
               序列化 & 反序列化
               
              
                序列化
                
               
                 把对象转换为字节数组的过程
                
                
                 作用
                 
               
                  网络传输对象
                 
                 
                  持久化对象
                 
                
                反序列化
                
              
                 把字节数组恢复为对象的过程
                
               
               Serializable接口
               
              
                为什么要实现该接口
                
               
                 标记一个类可以被序列化和反序列化,jvm
                 
会自动给它提供序列化和反序列化的能力
                
               会自动给它提供序列化和反序列化的能力
                serialVersionUID
                
              
                 用来表示类的版本,用于在序列化和反序列化过程中检查
                 
版本一致性。如果发送方和接收方的类版本不一致,
将引发`java.io.InvalidClassException`异常
                
                版本一致性。如果发送方和接收方的类版本不一致,
将引发`java.io.InvalidClassException`异常
                 多个类定义同一个serialVersionUID有风险吗?
                
               
               序列化的前提是保证通信双方对于对象的可识别性,
               
所以很多时候,我们会把对象先转化为通用的解析格式
               
             所以很多时候,我们会把对象先转化为通用的解析格式
                选型考虑因素
                
               
                 序列化之后的数据大小
                
                
                 序列化的性能(耗时)
                
                
                 是否跨平台、跨语言
                
                
                 技术成熟度
                
               
                常见技术
                
                
              
                 JDK Serializable
                 
                
                  只支持java,性能差
                 
                
                 xml
                
                
                 json
                 
                
                  fastjson、gson、jackson
                 
                
                 protobuf
                 
                
                  跨语言、速度快、体积小
                 
                
                 Kryo
                 
                
                  只支持java、体积小、速度快
                 
                 
                  线程不安全
                  
                
                   线程安全方式
                   
                 
                    ThreadLocal
                   
                   
                    kryo提供的pool
                   
                  
                 FST
                 
                
                  Java,不支持跨语言、体积小、速度快、线程安全
                 
                
                 Hessian
                 
               
                  跨语言、速度较慢
                 
                
              JDBC
             
             
              Maven
              
             
               冲突解决
               
             
                发现
                
               
                 运行异常,一般类找不到,方法找不到等
                
    
               
                依赖原则
                
               
                 路径最近者优先
                
                
                 第一声明者优先
                
               
                排除依赖
                
               
                 maven helper插件,然后使用<exclusion>标签
                
               
                <optional>true</optional>
               
              
              加密算法
              
             
             
              MDC
              
             
            
             数据结构
             
            
              Collection
              
             
               List
               
              
                线程不安全
                
               
                 ArrayList
                 
                
                  动态数组
                 
                 
                  构造方法
                  
                 
                   new ArrayList()
                   
                  
                    {}。第一次add才初始化数组,默认容量是10
                   
                  
                   new ArrayList(n)
                   
                 
                    立即初始化数组
                   
                  
                  扩容
                  
                 
                   默认1.5倍,复制到扩容后的数组并指向原数组
                  
                 
                  删除|新增
                  
                
                   其他元素需要移动,开销大且不固定
                  
                 
                 LinkedList
                 
               
                  双向链表
                  
                 
                   Node通过prev和next指针构建
                  
                  
                   相对于单向链表优点
                   
                 
                    双向遍历,可以在任一节点向前向后遍历
                   
                   
                    任一节点的插入删除都是O(1)复杂度,
                    
而单向链表需要从头开始遍历,复杂度为O(n)
                   
                  而单向链表需要从头开始遍历,复杂度为O(n)
                  删除|新增
                  
                 
                   只需改变node的前后指针,开销小切固定
                  
                 
                  实现了Queue
                 
                
                线程安全
                
              
                 Vector
                 
                
                  synchronized
                 
                
                 Collections#synchronizedList(list)
                 
                
                  对原list做了装饰,实际还是使用synchronized代码块,
                  
可以通过参数传入指定的锁对象,否则默认当前对象作为锁
                 
                可以通过参数传入指定的锁对象,否则默认当前对象作为锁
                 CopyOnWriteArrayList
                 
               
                  写操作会加锁,同时会复制一份原来的数据进行写操作,
                  
然后再把原数组设置为新数组
                 
                 然后再把原数组设置为新数组
                  读操作不会加锁
                 
                 
                  读多写少的场景
                 
                
               Set
               
              
                HashSet
                
               
                 底层是Map,key为set的元素,value为Object对象
                 
                
                
                 默认构造的HashSet的底层map就是HashMap
                
                
                 有一个构造器使用的是LinkedHashMap
                
               
                TreeSet
                
               
                 底层是TreeMap
                
               
                LinkedHashSet
                
              
                 继承了HashSet,用的构造器是HashSet的
                 
LinkedHashMap,用双向链表维护插入顺序
                
               LinkedHashMap,用双向链表维护插入顺序
               Stack
              
             
              Map
              
             
               HashMap
               
              
                java8
                
               
                 源码
                 
                
                  数据结构
                  
                 
                   数组+链表+红黑树
                   
                 
                    为啥是红黑树不是平衡二叉树
                    
                   
                     红黑树插入和删除的性能要比平衡二叉树好
                    
                   
                    key & value 可以为null
                   
                  
                  put
                  
                 
                   过程
                   
                 
                    计算key的hash值
                   
                   
                    如果Node数组没有初始化,则初始化(resize())
                   
                   
                    计算key的数组下标,如果head Node为null,则将KV封装成Node插入
                    
                   
                     这里会有线程不安全问题
                    
                   
                    如果头结点不为空且key和待插入的key一致,则直接替换
                   
                   
                    如果当前是红黑树,则按照红黑树的方式插入
                   
                   
                    如果当前是链表,则遍历链表替换或者插入到尾部,如果链表长度达到8了,
                    
那么看数组长度是否达到64,没达到则扩容,否则进行链表转红黑树
                   
                   那么看数组长度是否达到64,没达到则扩容,否则进行链表转红黑树
                    最后,如果当前++size>threshold,则会进行resize()扩容
                    
                  
                     ++size没有同步,也没有volatile+cas,线程不安全
                    
                   
                  get
                  
                 
                   过程
                   
                 
                    key计算hash值,然后定位到数组下标
                   
                   
                    如果头Node就是要查找的,返回
                   
                   
                    否则树查找或者链表遍历查找,返回
                   
                  
                  resize
                  
                
                   初始化数组(第一次put)
                  
                  
                   扩容
                   
                 
                    链表长度达到8且数组长度小于64
                   
                   
                    size达到负载容量
                   
                  
                 Q & A
                 
               
                  loadFactor = 0.75
                  
                 
                   负载因子越小,扩容越频繁,hash冲突越小,空间换时间
                  
                  
                   负载因子越大,扩容越不频繁,hash冲突越大,链表越长,时间换空间
                  
                  
                   0.75在时间和空间上取得一个平衡
                  
                 
                  为什么链表要达到8才树化
                  
                 
                   红黑树的时间复杂度为O(logn),而链表时间复杂度平均为O(n/2)
                   
                   
                  
                    所以8链表查找长度平均为4,红黑树为3,所以有树化的必要。
                    
6差不多,但是转换为树效率低,而且红黑树有更多的指针如
left、right、parent,color,更加耗内存
                   
                  6差不多,但是转换为树效率低,而且红黑树有更多的指针如
left、right、parent,color,更加耗内存
                   树转链表是6,不是7是为了防止树和链表频繁转换
                  
                 
                  key的hash值如何计算
                 
                 
                  key在数组下标如何计算
                  
                 
                   index = key.hash & (数组长度-1)
                  
                  
                   等价于取模运算,性能高。前提是数组长度必须是2幂次方
                  
                 
                  容量为什么必须是2的幂次方
                  
                 
                   下标计算决定了必须是2幂次方
                  
                  
                   否则会造成key分布不均匀,而且有的index永远不会分配到key
                  
                 
                  容量最大值为2的30次方
                 
                 
                  modCount是干嘛用的
                  
                 
                   记录map修改次数,相当于乐观锁版本号
                  
                  
                   
                    fast-fail
                   
                   : 进行迭代的时候,如果map被需改,抛出ConcurrentModificationException
                  
                 
                  扩容下标怎么移动,,如何判定是否需要移动
                  
                 
                   e.hash & oldTab.length
                   
                 
                    等于0则无需移动
                    
                   
                     原下标
                    
                   
                    否则需要移动
                    
                  
                     原下标+oldTab.length
                    
                   
                  为啥重写hashCode和equals方法
                  
                 
                   因为比较key的值相等,不是比较内存地址,所有重写equals(),
                   
重写equals()则必须重写hashCode(),因为两个对象相等,其hashCode必须相等
                  
                 重写equals()则必须重写hashCode(),因为两个对象相等,其hashCode必须相等
                  遍历方式
                  
                
                   map.keySet()
                  
                  
                   map.values()
                  
                  
                   map.entrySet()
                  
                  
                   通过Iterator遍历
                   
                 
                    Iterator<Entry<String, String>> iterator = map.entrySet().iterator()
                   
                  
                7和8区别
                
              
                 数据结构
                 
                
                  后者比前者多了红黑树
                 
                
                 节点类型
                 
                
                  前者是Entry,后者是Node
                 
                
                 插入方式
                 
                
                  前者头插法,后者尾插
                 
                
                 扩容时机
                 
                
                  前者是先扩容再插入,后者是先插入后扩容
                 
                
                 扩容后存储位置
                 
                
                  前者是重新计算,后者是原位置or者原位置+旧容量
                 
                
                 线程不安全
                 
               
                  前者因为是头插法,在并发扩容的情况下,可能两个Entry的next指针互相指向,导致闭环
                 
                 
                  后者设置头Node和计算size不安全
                 
                
               HashTable
               
              
                synchronized保证线程安全
               
               
                数组+链表
                
               
                 节点是Entry,默认初始容量是11,容量没有2的幂次方限制
                
                
                 O(1)
                
                
                 key和value不能为null
                 
               
                  因为HashTable用于多线程场景,ge(key) = null的话无法确定是不存在key还是存在key,value就是为null
                 
                
                头插法
               
               
                扩容rehash()
                
              
                 先扩容后插入
                
                
                 新数组长度 = 原数组长度 * 2 + 1
                
                
                 遍历所有元素,重新计算hash,使用头插法插入到新数组
                
               
               ConcurrentHashMap
               
              
                java8源码
                
              
                 数据结构
                 
                
                  结构图
                 
                
                 构造方法
                
                
                 参数
                 
                
                  volatile int sizeCtl
                  
                
                   控制table的初始化和扩容
                   
                 
                    0  : 初始默认值;
                    
-1 : 有线程正在进行table的初始化;
>0 : table初始化时使用的容量,或初始化/扩容完成后的threshold;
-(1 + nThreads) : 记录正在执行扩容任务的线程数;
                  -1 : 有线程正在进行table的初始化;
>0 : table初始化时使用的容量,或初始化/扩容完成后的threshold;
-(1 + nThreads) : 记录正在执行扩容任务的线程数;
                 put
                 
                
                  put过程
                  
                 
                   看数组是否初始化了,没有则先初始化
                   
                  
                  
                   根据key定位数组下标,看头Node是否为null,是则cas设置头Node
                   
                  
                    key和value都不能为null
                   
                  
                   头Node不是null且hash值=-1,说明正在扩容,则参与到协助迁移数据流程
                  
                  
                   头Node红黑树,则按照红黑树方式插入
                   
                  
                  
                   否则头Node的hash大于0,则是链表,遍历链表,比较key如果一样则替换旧值,否则进行尾插
                  
                  
                   如果当前是链表,且长度大于等于8并且数组长度小于64,则会进行扩容迁移。如果大于等于64,则会将链表转为红黑树
                  
    
                  
                   最后会根据put结果是插入还是更新旧值计算当前map容量
                  
                 
                  扩容和数据迁移
                 
                 
                  安全性保证
                  
                
                   volatile+cas
                   
                  
                    当table[i]为null,cas设置头Node
                   
                  
                   synchronized
                   
                 
                    当头Node不为null时候,会用头Node对象作为monitor,以桶为粒度,进行同步
                   
                  
                 get
                 
                
                  过程
                  
                 
                   计算key的hash值h
                  
                  
                   如果数组没有初始化或者用h计算的数组下标处没有Node,则返回null
                  
                  
                   如果当前桶的头结点就是要查找的key,则返回
                  
                  
                   头结点hash小于0,说明当前正在扩容或者是红黑树结构,则调用当前Node类型的find方法
                   
                  
                    hash小于0
                    
                  
                     hash= -1则为TreeBin
                     
(TreeNode的代理结点,负责红黑树的操作)
                     
                    (TreeNode的代理结点,负责红黑树的操作)
                      红黑树
                     
                    
                     否则为ForwardingNode
                     
                   
                      正在扩容,则去新的table去查找
                     
                    
                   不是上面情况,则是链表,则遍历链表查找,返回
                  
                 
                  是否加锁
                  
                
                   Node的val和next都是volatile修饰,所以节点修改和新增都具有可见性
                  
                  
                   数组是volatile修饰,所以扩容具有可见性
                  
                  
                   节点是红黑树的时候,如果树正在变色旋转并且要查询的值不是红黑树的头节点,会加一个
                   
                    
                     读写锁
                    
                   
                  
                 
                 addCount和size()
                 
               
                  baseCount + 数组方式计数,分散并发压力
                 
                 
                  size得到的可能不是准确值
                 
                
               TreeMap
               
              
                红黑树
               
               
                构造函数
                
               
                 传入比较器Comparator
                 
                
                  key无需实现Comparable接口
                 
                
                 不传入比较器Comparator
                 
               
                  key必须实现Comparable接口,使用key默认的比较规则
                 
                
                数据插入
                
               
                 根据是否传入比较器,如果是则按照该比较规则,否则按照key默认的比较规则进行树搜索的遍历搜索,
                 
如果存在key,则替换。否则插入,插入完成后,如果破坏红黑树的规则,则还需通过变色旋转的方式来达到平衡
                
               如果存在key,则替换。否则插入,插入完成后,如果破坏红黑树的规则,则还需通过变色旋转的方式来达到平衡
                可以实现一致性哈希
               
              
               LinkedHashMap
               
              
                继承HashMap,通过维护一条
                
                 双向链表
                
                ,实现了散列数据的有序排列,当我们希望顺序存取,可以使用
               
               
                重写了HashMap的三个方法
                
              
                 afterNodeAccess(Node<K,V> p)
                 
                
                  move node to last
                 
                
                 afterNodeInsertion(boolean evict)
                 
                
                  possibly remove eldest
                 
                 
                  removeEldestEntry(first)默认实现是返回false
                  
                
                   继承LinkedHashMap重写该方法可以删除第一个元素实现LRU
                  
                 
                 afterNodeRemoval(Node<K,V> p)
                
               
               WeakHashMap
               
              
                Entry继承WeakReference,每次gc的时候,都会回收它,适合做缓存
               
              
               Properties
               
             
                继承Hashtable
               
              
              Tree
              
             
               二叉树
               
               
               
              
                左子树小于根节点,右子树大于根节点
               
               
                缺点:极端情况下,会造成左右两边不平衡,一边几乎成为链表,查找时间复杂度为O(n)
               
              
               平衡二叉树(AVL)
               
              
                左子树小于根节点,右子树大于根节点,
                
                 
                  且左右子树的高度差<=1
                 
                
                
               
               
                插入和删除
                
               
                 如果改变后左右子树高度差大于1,则破坏了平衡,会通过旋转达到平衡
                
               
                O(log n)
               
              
               红黑树
               
               
               
              
                1.节点分为红色或者黑色;
                
2.根节点必为黑色;
3.叶子节点都为黑色,且为null;
4.不会出现相邻的红色节点;
5.从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点;
6.新加入到红黑树的节点为红色节点
               
               2.根节点必为黑色;
3.叶子节点都为黑色,且为null;
4.不会出现相邻的红色节点;
5.从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点;
6.新加入到红黑树的节点为红色节点
                插入和删除
                
               
                 当插入或者删除破坏了上面的规则后,会进行变色来符合上述规则,
                 
如果变色不行则通过左旋右旋来符合上述规则
                
               如果变色不行则通过左旋右旋来符合上述规则
                O(log n)
               
              
               B-Tree
              
              
               B+Tree
              
             
              SkipList
              
            
               因为链表没办法二分查找,只能遍历比较查找,所以有了跳跃表
              
              
               从最高层开始查找,找到
               
                
                 小于等于目标值的最大值
                
               
               ,然后到下一层查找,然后以此内推,直到最后一层
              
              
               时间复杂度O(log(n)),空间复杂度O(n),空间换时间
              
              
               特征
               
                
              
              - 由很多层结构组成
 - 每一层都是一个” 有序“ 链表(索引)
 - 最底层(Level 1)的链表包含所有元素
 - 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。
 - 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素
 
               索引选取
               
              
                如果用相同的间隔提取索引,插入数据会重建索引,导致性能差
               
               
                随机函数
                
              
                 通过使用一个随机函数,来决定这个结点插入时,是否需要插入到
                 
索引层、以及插入到第几级索引。时间复杂度O(log(n))
                
               索引层、以及插入到第几级索引。时间复杂度O(log(n))
               jdk实现ConcurrentSkipListMap
              
             
             JVM
             
            
              组成
              
             
               运行时数据区
               
(JVM内存模型)
               
              (JVM内存模型)
                堆
                
               
                 对象一定在堆中分配吗?
                 
                
                  不一定。如果对象在方法内创建且
                  
                   没有返回出去(方法逃逸)
                   
或者 没有被其他线程访问(线程逃逸) ,则会分配在栈里
                 
                 或者 没有被其他线程访问(线程逃逸) ,则会分配在栈里
                  逃逸分析
                  
                 
                   对象在方法内创建,没有逃逸到方法之外,则没有发生逃逸
                   
(对象不是入参或者没有return出去)
                  
                  (对象不是入参或者没有return出去)
                   会有性能损耗,要确保收益大于损耗
                   
                 
                    JDK7默认开启
                    
+XX:+DoEscapeAnalysis
                   
                  +XX:+DoEscapeAnalysis
                  栈上分配好处
                  
                 
                   随着方法执行结束销毁
                  
                  
                   提升对象访问速度,不需要访问堆
                   
                  
                 
                  即使逃逸分析通过,大对象还是会分配到堆中
                  
                 
                
                 jdk8开始静态成员变量存放在堆中的Class对象里
                
               
                方法区
                
               
                 常量池、类元信息
                
                
                 实现
                 
               
                  jdk7:永久代
                  
                 
                   使用的是jvm的内存,所以存在上限,容易出现OOM。
                   
通过-XX:PermSize调节大小
                  
                  通过-XX:PermSize调节大小
                   Full GC,导致STW
                  
                 
                  jdk8:元空间MetaSpace
                  
                
                   使用本地内存,默认是无限制使用的,可以通过参数调节。
                   
因此不需要考虑GC的问题
                  
                 因此不需要考虑GC的问题
                栈
                
               
                 栈帧
                 
                
                  局部变量表
                 
                 
                  操作数栈
                  
                 
                   主要用于保存计算过程的中间结果,同时
                   
作为计算过程中变量临时的存储空间
                  
                 作为计算过程中变量临时的存储空间
                  动态链接
                  
                 
                   运行时再确定具体类型
                  
                 
                  方法返回地址
                 
                
                 OOM
                 
                 
               
                  基本上都是创建的了大量的线程导致的
                 
                
                本地方法栈
                
               
                 native方法
                
               
                程序计数器
               
              
               类加载子系统
               
              
                类加载机制
                
               
                 Java虚拟机把Class文件加载到内存,并对数据进行校验、准备、解析和初始化,变成可被使用的java类型
                
                
                 Class文件获取
                 
                
                  java类可以动态加载到内存
                 
                 
                  途径
                  
                
                   zip包
                  
                  
                   网络
                  
                  
                   动态代理技术
                  
                  
                   .....
                  
                 
                 ClassLoader类
                 
               
                  作用根据类全限定名找到Class文件,然后加载它并转换成java.lang.Class对象
                 
                
                生命周期
                
               
                 加载
                 
                
                  通过类的全限定名来获取定义此类的二进制流,然后将其静态存储结构转化为方法区运行时数据结构,
                  
在堆内存中生成代表此类的Class对象(ClassLoader的definClass方法将字节流转成Class对象),
作为方法区这个类的各种数据的访问入口
                 
                在堆内存中生成代表此类的Class对象(ClassLoader的definClass方法将字节流转成Class对象),
作为方法区这个类的各种数据的访问入口
                 验证
                 
                
                  确保Class文件包含的信息符合《Java虚拟机规范》的约束
                 
                
                 准备
                 
                
                  为类的静态变量分配内存空间,设置零值。
                  
非静态变量不会分配内存。
                 
                非静态变量不会分配内存。
                 解析
                 
                
                  将常量池中的符号引用替换为直接引用
                 
                
                 初始化
                 
                
                  开始执行类中定义的java程序代码(字节码)
                 
                 
                  只有当类被直接引用的时候,才会触发类的初始化
                 
                
                 使用
                
                
                 卸载
                
               
                类加载器
                
              
                 同一个Class文件被同一个类加载器加载,这两个类才相等。
                 
equals()、isAssignableFrom()、instanceof
                
                equals()、isAssignableFrom()、instanceof
                 类型
                 
                
                  启动类加载器:c++实现
                  
                 
                   <JAVA_HOME>/lib
                  
    
                  
                   被-Xbootclasspath指定的路径存放的
                  
                 
                  拓展类加载器: ExtClassLoader
                  
                 
                   <JAVA_HOME>/lib/ext目录下的jar
                  
                  
                   被java.ext.dirs指定的路径下类库
                  
                 
                  应用程序类加载器:AppClassLoader
                  
                 
                   classpath路径
                  
                 
                  自定义类加载器
                  
                
                   why
                   
                  
                    类不在classplath路径下,可能来源于数据库,文件,网络等:需要我们自定义加载器去找这些类
                   
                   
                    代码加密:需要自定义类加载器去解密
                    
                   
                   
                    .....
                   
                  
                   如何自定义
                   
                  
                    继承java.lang.ClassLoader,重写
                    
                     
                      findClass
                     
                    
                   
                  
                   拓展
                   
                   
                 
                    tomcat为啥自定义类加载器
                    
                   
                  
                 双亲委派模型
                 
               
                  当一个类加载器收到类加载请求时,不会先自己加载,而是上抛给父加载器,一直到启动类加载器,如果父类加载不了,才会由子加载器去加载
                 
                 
                  好处
                  
                   
                 
                 - 比如加载Object类时候,只有启动类加载器才可以加载,如果没有双亲委派,那么所有加载器都可以加载的话,那么系统中存在不同的Object;
 - 安全性,保证系统的稳定运行
 
                  破坏双亲委派模型
                  
                 
                   java1.2双亲委派模型出现之前
                  
                  
                   SPI,父类加载器去请求子类加载器完成类加载
                   
                  
                    spi的接口由启动类加载器加载,而启动类加载器不能加载子类,也无法委托给应用类加载器,
                    
所以必须打破双亲委派。ServiceLoader通过设置上下文类加载器为应用类加载器,从而可以加载子类
                   
                   所以必须打破双亲委派。ServiceLoader通过设置上下文类加载器为应用类加载器,从而可以加载子类
                    如java.jdbc.Driver
                   
                  
                   热部署
                  
                  
                   Tomcat
                  
                 
                  如何破坏双亲委派模型
                  
                
                   继承ClassLoader,重写loadClass方法
                  
                  
                   使用线程上下文加载器,可以通过 java.lang.Thread 类的
                   
setContextClassLoader()方法来设置当前类使用的类加载器类型,比如SPI
                  
                 setContextClassLoader()方法来设置当前类使用的类加载器类型,比如SPI
               字节码执行引擎
               
             
                执行字节码、修改程序计数器、执行垃圾收集线程
               
              
              GC
              
             
               内存溢出 & 内存泄露
               
              
                内存溢出
                
               
                 当内存不够分配对象时候,就会OOM
                
               
                内存泄露
                
              
                 当资源没有得到释放(比如流没有close)、循环引用等
                 
原因,导致对象一直得不到释放回收,造成溢出
                
               原因,导致对象一直得不到释放回收,造成溢出
               强引用、软引用、弱引用和虚引用
              
              
               垃圾判断
               
              
                引用计数法
                
               
                 循环引用,无法被清除
                
               
                可达性分析
                
               
                 GC Roots
                 
                  
                
                - 虚拟机栈(栈帧中本地变量表)引用的对象
 - 方法区中 静态属性 引用的对象
 - 方法区中 常量 引用的对象
 - 
                    本地方法栈引用的对象
                    
 - 被同步锁持有的对象
 
                 如果对象和gc roots之间没有可达路径,则被判断为垃圾对象
                 
                
                
                 缺点:gc roots确定的过程中STW比较久
                 
                
               
                三色标记法
                
               
                 相比可达性分析好处
                 
                
                  不发生STW,或者极短的STW
                 
                
                 CMS、G1都采用
                
                
                 原理
                 
                 
                
                  三色
                  
                 
                   白:还没有被垃圾回收器扫描的对象
                  
                  
                   灰:已经被垃圾回收器扫描,但对象引用的其他对象没有被扫描
                  
                  
                   黑:已经被垃圾回收器扫描,对象以及引用的其他对象也是存活的
                  
                 
                  流程
                  
                  
                
                   初始阶段:gc roots是黑色,其他都是白色
                   
                  
                  
                   初始标记阶段:跟gc roots直接关联的对象标记为灰色
                   
                  
                  
                   并发标记阶段:当前节点标记为黑色,如果有子节点则标记灰色
                   
                  
                  
                   重发并发标记阶段:直到灰色对象没有其它子节点引用时结束
                   
                  
                  
                   扫描完成:黑色的为存活对象,白色为垃圾对象
                   
                  
                 
                 缺陷
                 
                 
               
                  用户线程与 GC 线程同时
                  
运行,可能产生多标、漏标
                  
                运行,可能产生多标、漏标
                   多标
                   
                   
                  
                    产生浮动垃圾,留到下一次回收即可
                    
                   
                  
                   漏标
                   
                   
                 
                    会回收非垃圾对象,不可接受
                    
                   
                   
                    解决
                    
                    
                     
                    
                   
                  
                     读屏障、写屏障、增量更新、原始快照
                     
                    
                    
                其他
                
              
                 跨代引用如何标记
                
               
               GC算法
               
              
                标记复制
                
               
                 新生代算法
                 
                
                  新生代大部分对象是朝生夕灭,只需复制少量对象,
                  
而老年代恰恰相反所以不适用
                 
                而老年代恰恰相反所以不适用
                 空间浪费
                
               
                标记清除
                
               
                 使用
                 
                  空闲列表
                 
                 记录内存空间和大小,会产生内存碎片,虽然
                 
有很多空间,但是每个空间分配不了大对象,提前触发GC
                
                有很多空间,但是每个空间分配不了大对象,提前触发GC
                 因为产生内存碎片,内存的访问需要通过
                 
                  空闲列表
                 
                 ,
                 
所以更加耗时,吞吐量低
                
               所以更加耗时,吞吐量低
                标记整理
                
               
                 解决标记清除的内存碎片问题,会往一边移动存活对象,
                 
所以gc停顿会持续更长的时间
                
               所以gc停顿会持续更长的时间
                分代算法
                
              
                 为什么要分代
                 
                
                  分代可以将大内存分成多个小内存,提升回收效率
                 
                 
                  对象生命周期不一样,不同代可以选择不同的gc算法,提升速度以及减少gc停顿时间
                 
                
                 新生代
                 
                
                  Eden
                  
                 
                   对象第一次分配到Eden区域,大对象直接到老年代,当gc的时候,存活下来
                   
的对象会被复制到survivor区
                  
                  的对象会被复制到survivor区
                   对象内存分配线程安全
                   
                 
                    TLAB
                   
                  
                  Survivor from
                 
                 
                  Survivor to
                 
                
                 老年代
                 
               
                  对象进入老年代
                  
                
                   熬过一定gc次数
                   
                   
                  
                    -XX:MaxTenuringThreshold,默认15
                   
                   
                    为什么最大是15
                    
                   
                  
                   大对象直接进入老年代
                  
                  
                   空间担保机制
                  
                  
                   动态对象年龄判定
                  
                 
               垃圾收集器
               
                
              
             
                 Serial
                 
                
                  新生代单线程垃圾收集器
                 
                
                 Serial Old
                 
                
                  是Serial的老年代版本,使用标记-整理算法
                 
                
                 ParNew
                 
                
                  Serial的多线程版本,是激活CMS后默认的年轻代垃圾回收器
                 
                
                 Parallel Scavenge
                 
                
                  新生代多线程收集器,复制算法,强调的是高吞吐量,高吞吐量可以最高效率的
                  
利用处理器资源,适合后台运算不需要太多交互的任务
                 
                 利用处理器资源,适合后台运算不需要太多交互的任务
                  最大gc停顿时间:-XX:MaxGCPauseMillis、设置吞吐量:-XX:GCTimeRatio
                 
                
                 Parallel Old
                 
                
                  Parallel Scavenge老年代版本,标记整理算法,吞吐量优先
                 
                
                 CMS(Concurrent Mark Sweep)
                 
                
                  低停顿老年代收集器
                  
                   
                  
                 - 大部分工作可以和用户线程并发执行
 - 使用标记清除算法,使用空闲列表管理内存
 
                   适用于与客户交互的应用,强调低延迟,但是吞吐量相对较低,适合多核的机器
                  
                 
                  优点
                  
                  
                 
                   并发收集、低停顿
                   
                  
                 
                  缺点
                  
                 
                   和用户线程并行期间占用了cpu时间,会降低吞吐量
                   
                  
                    处理器越多,影响越小(默认占用四分之一)
                   
                  
                   并发清除阶段会产生浮动垃圾,留到下一次GC
                  
                  
                   空间碎片
                  
                 
                  阶段
                  
                 
                   初始标记
                   
                   
                  
                    
                     STW
                    
                    ,快速标记GC Roots能直达的对象
                   
                  
                   并发标记
                   
                   
                  
                    和用户线程并发执行,从GC roos进行可达性分析,标记存活对象
                   
                  
                   最终标记
                   
                   
                  
                    
                     STW
                    
                    ,标记并发标记阶段产生对象
                   
                  
                   并发清除
                   
                   
                 
                    和用户线程同时执行,清理垃圾对象
                    
                   
                  
                  concurrent mode failure
                  
                
                   cms特有的错误,当清理线程和用户线程并发执行的时候,老年代不能容纳新的对象,则会抛出该错误
                  
                  
                   影响
                   
                  
                    退化为Serial Old回收器,暂停用户线程,停顿加长
                   
                  
                   原因以及避免
                   
                 
                    空间碎片太多
                    
                   
                     -XX:+UseCMSCompactAtFullCollection
                    
                    
                     -XX:CMSFullGCsBeforeCompaction=n
                    
                   
                    触发Full GC的内存阈值太高
                    
                   
                     -XX:CMSInitiatingOccupancyFraction=N调小
                     
-XX:+UseCMSInitiatingOccupancyOnly
                    
    
                   -XX:+UseCMSInitiatingOccupancyOnly
                    垃圾产生速度超过清理速度
                    
                  
                     晋升阈值过小
                     
Survivor空间过小
Eden区过小,导致晋升速率提高
存在大对象
                   Survivor空间过小
Eden区过小,导致晋升速率提高
存在大对象
                 G1(Garbage First)
                 
                
                  设计目的
                  
                 
                   对gc暂停时间可预测、可配置,对延迟可控的情况下尽可能提高并发量
                  
                 
                  标记整理
                  
                 
                 
                  把堆内存划分多个独立相等的Region,每个Region都可能是eden、survivor和old区域。
                  
可以设置最大停顿时间,默认200ms,根据各个Region的回收价值进行选择性回收
                 
                 可以设置最大停顿时间,默认200ms,根据各个Region的回收价值进行选择性回收
                  回收过程
                  
                 
                   初始标记
                   
                  
                    STW
                   
                  
                   并发标记
                  
                  
                   最终标记
                   
                  
                    STW
                   
                  
                   筛选回收
                   
                 
                    STW,这里不是并发的,因为gc停顿时间可控
                   
                  
                  适用
                  
                
                   较大的堆空间,较短的停顿时间
                  
                 
                 ZGC
                
               
              诊断/监控工具
              
             
               命令行
               
              
                jps
                
               
                 查看虚拟机中进程vmid
                
               
                jstat
                
               
                 查看一些统计信息
                
                
                 jstat -gc vmid
                 
                
                  可以查看各个分区内存占用大小,gc次数、时间
                 
                
                 jstat -gcutil vmid
                 
                
                  可以查看各个分区内存占比,gc次数、时间
                 
                
                 jstat -gccause vmid
                 
               
                  可以查看各个分区内存占比,gc次数、时间,以及上一次gc原因
                 
                
                jinfo
                
               
                 查看虚拟机信息以及启动参数等信息
                
               
                jmap
                
               
                 查看堆信息:jmap -heap vmid
                
                
                 jmap -histo vmid | head -n20
                 
                
                  查看堆内存中的存活对象,并按空间排序
                 
                
                 转储:jmap -dump:live,format=b,file=文件名.dump  vmid
                 
                
               
                jhat
                
               
                 jhat 文件名.dump
                 
               
                  使用浏览器打开 ip:port->port就是jhat命令后输出的port
                 
                
                jstack
                
              
                 获取当前进程的所有线程堆栈信息,以及锁信息
                 
                
                  死锁可以看到具体代码行数
                  
                 
                
                 jstack vmid
                
               
               图形化工具
               
              
                jvisualvm
                
               
                 输入jvisualvm命令即可打开
                
                
                 Visual GC插件
                
                
                 分析dump
                
               
                MAT
               
               
                jconsole
               
              
               arthas
              
             
              常用调优参数
              
             
               Xms:初始堆大小
              
              
               Xmx:最大堆大小
              
              
               Xmn:新生代大小
              
              
               -XX:+DisableExplicitGC:禁止调用Systerm.gc()
               
              
                System.gc()
                
              
                 不会马上gc,也不一定会gc
                
                
                 触发Full GC
                
               
               -XX:-HeapDumpOnOutOfMemoryError
              
             
              生产排查
              
            
               OOM快速定位
               
              
                可能原因
                
               
                 内存分配过小
                
                
                 不断申请资源没有释放(内存泄露)
                
                
                 队列消费速度远远小于生产速度
                
                
                 不断创建线程,网络连接等
                
                
                 ......
                
               
                步骤
                
               
                 生成dump文件
                 
                
                  jps找出进程号,jmap命令生成dump文件
                  
                 
                   缺点如果oom后进程结束了,就拿不到进程号了
                   
                  
                 
                  OOM自动生成dump文件,指定存放目录
                  
-XX:+HeapDumpOnOutOfMemoryError,
-XX:HeapDumpPath = xx
                 
                -XX:+HeapDumpOnOutOfMemoryError,
-XX:HeapDumpPath = xx
                 使用MAT(或jvisualvm)分析dump文件,找到占用空间大的对象,点开,定位代码
                
               
                案例
                
                
              
                 订单导出重复点击,导致产生大量对象发生oom。
                 
通过导出按钮置灰防止等响应之后再点击
                
                通过导出按钮置灰防止等响应之后再点击
                 定时任务定时统计,导致周期性发生oom
                 
                
               
               频繁full gc排查
               
              
                可能原因
                
              
                 手动触发System.gc()
                
                
                 一些资源对象没有close掉
                
                
                 频繁生成长生命周期对象
                
                
                 大对象
                
                
                 jvm参数不合理,如内存大小
                
               
               频繁youg gc排查
              
             
             J.U.C
             
             
            
              并行 & 并发
              
             
               并发:在同一时刻 CPU 能够处理的
               
                任务数量
               
               
              
              
               并行:指在多核 CPU 架构下,同一时刻同时可以执行多个
               
                
                 线程
                
               
               的能力
              
             
              线程
              
             
               分类
               
              
                用户线程(默认)
                
               
                 jvm退出必须等待所有用户线程结束
                
               
                守护线程
                
              
                 jvm退出不用等待守护线程结束
                
                
                 thread.setDaemon(true)
                
                
                 是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程
                
               
               创建方式
               
               
              
                继承Thread重写run、实现Runnable、实现Callable、线程池
                
               
              
               线程状态
               
                
               
              
              
                NEW(新建)
                
               
                 还没有调用start方法
                
               
                RUNNABLE(可运行)
                
               
                 调用start方法后
                
               
                RUNNING
                
               
                 获取时间片
                
               
                BLOCKED(阻塞)
                
               
                 同步块阻塞或IO阻塞
                
               
                WAITING(等待)
                
               
                 wait()、join()、LockSupport#park()
                
               
                TIMED_WAITING(超时等待)
                
               
                 sleep(time)、wait(time)、join(time)、LockSupport#parkNanos
                
               
                TERMINATED(终止)
                
               
                 执行完任务或者异常退出
                
               
               线程交互
               
              
                sleep
                
               
                 Thread.sleep(time)
                
                
                 被interupt中断后会清除中断标识
                 
                
               
                watit/notify/notifyAll
                
               
                 必须在同步代码块里,wait须在notify之前调用
                
               
                LockSupport
                
               
                 使用了permit(许可证)的概念来做到阻塞和唤醒线程的功
                 
能,每个线程都有一个许可(permit),permit只有两个值1和0
,默认是0
                
                能,每个线程都有一个许可(permit),permit只有两个值1和0
,默认是0
                 park()
                 
                
                  阻塞线程
                 
                 
                  消耗许可证
                 
                
                 unpark(thread)
                 
               
                  解除线程阻塞
                 
                 
                  释放许可证(执行多次也只有一个许可证)
                 
                
                join
                
               
                 等待
                 
                  调用该方法的线程
                 
                 结束
                
               
                yield
                
              
                 静态方法:Thread.yield()
                
                
                 暗示调度器当前线程放弃cpu时间片,但是调度器可以忽略该暗示
                
                
                 应用
                 
               
                  如果某一个线程是不太紧急的线程,那么我们可以在编写时调用yield()这样会让其它线程得到更多的执行机会
                 
                 
                  FutrueTask的awaitDone方法里有用到。如果任务还在运行中,将cpu让给其他线程
                 
                
               中断
               
              
                thread.interrupt()
                
               
                 中断线程,只是标记中断,不会真的中断
                
                
                 想要真的中断,目标线程需要有判断中断的逻辑,然后中断
                 
                
               
                thread.isInterrupted()
                
               
                 返回是否被标记为中断
                 
                
               
                Thread.interrupted()
                
              
                 消除当前线程中断状态,并返回之前中断状态
                
               
               锁
               
             
                死锁
                
               
                 多线程情况下,互相等待对方释放锁资源导致死循环
                
                
                 排查
                 
                
                  arthas
                 
                 
                  jstack打印线程状态信息
                 
                
                 解决
                 
               
                  顺序锁
                  
                 
                   使用相同的加锁顺序
                  
                 
                  轮询锁
                  
                 
                   while(true)里使用tryLock,获取失败适度休眠一会
                  
                 
                  超时锁
                  
                
                   锁设置超时时间
                  
                 
                乐观锁&悲观锁
                
               
              
              volatile
              
             
               可见性
               
              
                为什么产生不可见性
                
                
               
                 cpu和内存的性能差距,使得cpu加了一个缓存来平衡这种差距。
                 
而且现在都是多核的,就导致cpu之间共享变量的副本值不一致
                
    
                而且现在都是多核的,就导致cpu之间共享变量的副本值不一致
                 JMM(Java内存模型)
                 
               
                  线程对变量的所有操作(读,写)都必须在工作内存中进行,不能直接操作主内存中的数据
                 
                 
                  不同线程之间 也不能直接访问对方工作内存中的变量,线程间的变量值传递必须通过主内存进行中转传递
                 
                
                解决
                
                
              
                 一个线程对volatile的变量修改后,会写入主存同时立即
                 
通知其他cpu缓存的变量副本失效。其他线程需要从主存
中读取最新的值
                
                通知其他cpu缓存的变量副本失效。其他线程需要从主存
中读取最新的值
                 如何实现?
                 
MESI(缓存一致性协议)
                 
               MESI(缓存一致性协议)
                  嗅探技术
                  
                
                   各个cpu和主存通过总线进行通信。
                   
嗅探总线检查本地缓存是否失效
                  
                  嗅探总线检查本地缓存是否失效
                   产生问题:总线风暴(本地延迟)
                   
                   
                   
                  
                 
                    当有大量线程共享一个对象或者有大量volatile
                    
修饰的变量时候,由于总线通信能力的限制,
会产生一定的延迟
                   
                   修饰的变量时候,由于总线通信能力的限制,
会产生一定的延迟
               有序性
               
             
                禁止重排序
                
               
                 内存屏障
                 
                
                  编译器在适当位置插入指令禁止重排序
                  
                
                   对volatile域写操作
                   
                  
                    前插入StoreStore屏障
                   
                   
                    后插入StoreLoad屏障
                   
                  
                   对volatile域读操作
                   
                 
                    后插入StoreLoad屏障
                   
                   
                    后插入StoreStore屏障
                   
                  
                 单例模式双重检查
                 
               
                  为什么要valatile修饰?对象创建不是原子性的,包括分配内存,实例化,返回地址引用,
                  
修饰后禁止重排序,这样就能保证在对象初始化完了之后才把singleton指向分配的内存空间
                 
                修饰后禁止重排序,这样就能保证在对象初始化完了之后才把singleton指向分配的内存空间
                happens-before
                
              
                 volatile的变量写操作happen-before后面任何对此volatile变量的读操作
                 
                
               
              synchonized
              
             
               用法
               
              
                修饰代码块
                
               
                 反编译后再同步块开始和结束处分别加了
                 
                  monitorenter
                 
                 和
                 
                  monitorexit
                 
                 两个指令,
                 
当线程执行到monitorenter处时候会获取对象的监视器,获取成功才可以执行代码块中的代码
                
               当线程执行到monitorenter处时候会获取对象的监视器,获取成功才可以执行代码块中的代码
                修饰实例方法
                
               
                 monitor即当前对象
                
               
                修饰静态方法
                
              
                 monitor即当前类的Class对象,是该类的全局锁
                
               
               特性
               
              
                原子性
                
               
                 确保线程互斥的访问同步代码,不被其他线程打断
                
               
                可见性
                
               
                 读取共享变量每次都是从主内存获取最新的值,写共享变量每次都会刷新到主内存
                
               
                有序性
                
              
                 有效解决重排序问题,即 “一个unlock操作先行发生(happens-before)于后面对同一个锁的lock操作”
                
               
               锁升级
               
             
                对象内存布局
                
               
                 对象头
                 
                
                  Mark Word
                  
                 
                   存储hashCode,gc分代年龄,锁状态标识,线程持有的锁,偏向线程id等信息
                  
                 
                  类型指针
                  
                 
                   通过它可以找到对象属于哪个类
                  
                 
                  数组长度(对象是数组才有)
                 
                
                 实例数据
                
                
                 对齐填充
                
               
                无锁
                
                 ->
                
                偏向锁
                
                 ->
                
                轻量级锁
                
                 ->
                
                重量级锁
                
(不可逆)
              (不可逆)
                 锁消除(无锁)
                 
                
                  JVM检测到共享数据不存在竞争,则会进行锁消除
                 
                 
                  依据逃逸分析
                  
                
                   比如Vector,StringBuffer是线程安全的类,内部进行了同步操作,
                   
但是如果对象没有逃逸到方法之外,那么则会锁消除
                  
                 但是如果对象没有逃逸到方法之外,那么则会锁消除
                 偏向锁
                 
                
                  默认开启,
                  
                   -XX:-UseBiasedLocking
                  
                  关闭
                 
                 
                  没有并发竞争的情况下,线程获取锁,将线程id写入到锁对象头mark word的偏向线程id上,
                  
下次该线程再次要获取锁时,发现偏向线程id是当前线程话则直接获取到锁
                 
                下次该线程再次要获取锁时,发现偏向线程id是当前线程话则直接获取到锁
                 轻量级锁
                 
                
                  自旋锁
                  
                
                   默认开启,可以通过
                   
                    -XX:+UseSpinning
                   
                   开启,默认自旋10次,可以通过
                   
                    -XX:PreBlockingSpin
                   
                   调整
                  
                  
                   优点
                   
                  
                    发现锁被占用,通过自旋等待锁释放,而不是挂起切换到内核态,从而提升性能
                   
                  
                   缺点
                   
                 
                    如果未获取到锁,造成cpu时间浪费
                    
                   
                     当自旋到限定次数后,如果没有获取到锁,则会膨胀为重量级锁,从用户态
                     
切换到内核态,相比直接使用重量级锁,多了自旋的cpu占用时间
                    
                    切换到内核态,相比直接使用重量级锁,多了自旋的cpu占用时间
                     自适应自旋锁
                     
                   
                      会根据以往经验自动调节自旋次数。比如以往自旋9次获取锁了,那么这次很可能获取到锁,
                      
则增加自旋次数。如果上次没有获取到,则这次很可能获取不到锁,则减少自旋次数甚至
跳过自旋直接碰撞为重量级锁
                     
                    则增加自旋次数。如果上次没有获取到,则这次很可能获取不到锁,则减少自旋次数甚至
跳过自旋直接碰撞为重量级锁
                    总线风暴
                   
                  
                 重量级锁
                 
               
                  依赖于操作系统的Mutex Lock实现,需要从用户态切换到内核态,性能较差
                 
                
              ThreadLocal
              
             
               存储线程本地变量
               
              
              
               应用场景
               
              
                PageHelpler插件存放分页信息
               
               
                Spring的事务
               
               
                存储上下文信息
               
               
                spring判断是否产生循环依赖
               
              
               源码
               
              
                ThreadLocalMap
                
               
                 Thread的属性threadLocals是ThreadLocalMap类
                 
                
                  初始化是在ThreadLocal#set和get方法里发生的
                  
                 
                
                 是ThreadLocal的内部类
                
                
                 key是弱引用,类型是ThreadLocal,value是Object
                
                
                 只有数组,没有链表
                 
               
                  如何解决哈希冲突
                  
                
                   寻找数组下一个空位置,直到为空为止
                  
                 
                set(T value)
               
               
                get()
               
               
                remove()
               
              
               内存泄露
               
              
                原因
                
               
                 当我们new一个ThreadLocal时候对其强引用,当线程执行结束后,不会对其强引用。
                 
但是Entry(threadLocal,object)中key是弱引用,gc会清除,如果线程没有销毁比如线
程池,那么threadLocalMap则被强引用,则threadLocal对象会被清除,但是value
不会被清除,导致内存泄露
                
                但是Entry(threadLocal,object)中key是弱引用,gc会清除,如果线程没有销毁比如线
程池,那么threadLocalMap则被强引用,则threadLocal对象会被清除,但是value
不会被清除,导致内存泄露
                 是否存在threadLocal还没有用完,就被gc回收了
                 
               
                  
                   不会
                  
                  :
                  
因为我们new了一个threadLocal对象,是强引用。也就是说threadLoacl 同时 被强引用和弱引用,所以不会被gc。
只有代码逻辑执行结束,不再对其强引用,才会被gc。
                 
                因为我们new了一个threadLocal对象,是强引用。也就是说threadLoacl 同时 被强引用和弱引用,所以不会被gc。
只有代码逻辑执行结束,不再对其强引用,才会被gc。
                避免
                
              
                 主动:使用完执行remove()
                
                
                 被动:源码自己做了优化,如果get,set,remove方法被调用如果发现key=null的话会把value也置为null
                
               
               InheritableThreadLocal
               
             
                可以将值传给子线程
               
               
                当在父线程
                
                 
                  创建
                 
                
                子线程的时候,会在init方法里将父线程的
                
inheritableThreadLocals 属性传递给子线程
                
              inheritableThreadLocals 属性传递给子线程
                 必须在父线程里面new子线程,才会传递。
                 
如果父子线程使用线程池则不会传递
                
               如果父子线程使用线程池则不会传递
              AQS
              
             
               是什么
               
              
              
               AbstractQueuedSynchronizer
               
              
                volatile int state
                
               
                 竞争资源标识
                 
                
               
                exclusiveOwnerThread
                
               
                 记录持有锁的线程
                
               
                内部类Node
                
               
                 volatile Thread thread
                
                
                 volatile int waitStatus
                 
                
                  0
                 
                 
                  CANCELLED = 1
                 
                 
                  SIGNAL = -1
                  
                 
                   标识后继节点需要被唤醒,-1的值也是后继节点加入到阻塞队列时候改的
                  
                 
                  CONDITION=-2
                  
                 
                   标识当前node在条件队列中
                  
                 
                  PROPAGATE=-3
                 
                
                 volatile Node prev
                
                
                 volatile Node next
                
                
                 Node nextWaiter
                 
               
                  条件队列使用,形成单向链表
                 
                
                volatile Node head
                
               
                 头节点持有锁的线程,不包含在阻塞队列中
                
               
                volatile Node tail
               
               
                内部类ConditionObject
                
               
                 Node firstWaiter
                
                
                 Node lastWaiter
                
                
                 Node通过nextWaiter形成单向链表
                
               
                5个未实现的方法
                
              
                 tryAcquire、tryRelease。
                 
tryAcquireShared、tryReleaseShared。
isHeldExclusively(用于判断是否排它锁)
                
               tryAcquireShared、tryReleaseShared。
isHeldExclusively(用于判断是否排它锁)
               实现类
               
             
                ReentrantLock
                
               
                 抽象类Sync extends AQS
                 
                
                  NonfairSync
                  
                 
                   先通过CAS state值,如果成功则返回获取成功。否则将当前线程封装成Node节点,放入到阻塞队列的队尾,并挂起线程。
                   
当持有锁的线程释放锁时候,则唤醒在阻塞队列里head节点下一个节点的线程,也就是从刚挂起的地方继续执行死循环,
如果CAS state成功,则抢占到锁,并把自己设置为head节点,退出循环
                  
                 当持有锁的线程释放锁时候,则唤醒在阻塞队列里head节点下一个节点的线程,也就是从刚挂起的地方继续执行死循环,
如果CAS state成功,则抢占到锁,并把自己设置为head节点,退出循环
                  FairSync
                  
                 
                   不会先CAS state值,然后会看是否有前继结点(hasQueuedPredecessors()),如果有的话直接加入到队尾
                  
                 
                  公平锁 & 非公平锁优缺点对比
                  
                
                   公平锁
                   
                  
                    所有线程都能获取到锁,不会出现饿死
                   
    
                   
                    吞吐效率低(每个线程都需要唤醒)
                   
                  
                   非公平锁
                   
                 
                    吞吐效率高(抢到锁的线程无需挂起再唤醒)
                   
                   
                    导致等待队列中的线程饿死或者等待太久
                   
                  
                 重写了tryAcquire、tryRelease和isHeldExclusively
                
                
                 ConditionObject
                 
                
                  条件队列
                  
                
                   是一个单向链表,当调用await方法会放入到同步队列,当signal/signalAll则会将同步队列的节点再放入到阻塞队列队尾
                  
                 
                 synchonized & ReentrantLock区别
                 
               
                  前者是jvm层面的关键字,后者是java类
                 
                 
                  前者被动释放锁,后者主动释放
                 
                 
                  前者只能是非公平锁,后者皆可
                 
                 
                  前者是重量级锁,需要内核态和用户态切换,后者不需要
                 
                 
                  后者拥有更丰富的api
                  
                 
                   tryLock()、tryLock(time)、lockInterruptibly()等
                  
                  
                   前者不能知道是否获得了锁,后者可以知道
                  
                 
                  前者不可中断,后者可以
                 
                
                CountdownLatch
                
               
                 内部类Sync继承AQS实现了tryAcquireShared和tryReleaseShared两个方法。
                 
构造方法设置state的值,latch.countDown()则state-1,
当state=0的时候,唤醒阻塞在latch.await()方法的线程
                
               构造方法设置state的值,latch.countDown()则state-1,
当state=0的时候,唤醒阻塞在latch.await()方法的线程
                CyclicBarrier
                
               
                 没有实现AQS,只是利用了Condition条件队列,调用await会调用Condition#awati()并count - 1,
                 
当最后一个线程(count = 0)调用await会singnalAll(),同时会new Generation
                
                当最后一个线程(count = 0)调用await会singnalAll(),同时会new Generation
                 相比CountdownLatch,有一个Generation的概念,对count重置然后重复使用,
                 
而且可以在最后一个到达线程执行一个Runnable方法(构造方法传入)
                
               而且可以在最后一个到达线程执行一个Runnable方法(构造方法传入)
                Semaphore
                
               
                 内部类Sync继承AQS,有NonfairSync和FairSync模式,重写了tryAcquireShared和tryReleaseShared
                
                
                 acquire()和realease()
                
                
                 应用
                 
               
                  拓展容器类,实现阻塞容器
                 
                
                ReentrantReadWriteLock
                
               
                 state的高16位表示读状态,低16位表示写状态
                
                
                 读锁重写tryAcquireShared 和 tryReleaseShared
                 
                
                  每个线程维护一个计数器记录重入次数目的就是实现可重入锁
                 
                
                 写锁重写tryAcquire 和 tryRelease
                
                
                 当线程获取读锁后,该线程不能再获取写锁。
                 
当线程获取写锁后,可以继续获取读锁
               当线程获取写锁后,可以继续获取读锁
                ThreadPoolExecutor内部类Worker
               
              
              Atomic类
              
             
               AtomicInteger/AtomicLong
               
              
                java7
               
               
                java8
               
              
               AtomicBoolean
              
              
               AtomicIntegerArray
              
              
               AtomicReference
               
              
                可以把多个变量放在一个对象里来进行CAS操作
               
              
               AtomicStampedReference
               
             
                解决ABA问题,内部维护了对象和stamp
               
              
              LongAdder
              
             
               通过数组分担并发压力,提升性能(分治)
              
              
               和AtomicLong性能对比测试
              
              
               add(long x)
               
              
                根据cells数组是否初始化,cas操作base是否成功来决定是否要走数组计算的逻辑
                
               
                 数组已经初始化或者casBase失败则走cells数组的逻辑
                
               
                数组初始化或扩容(2幂次方,最大为cpu数)然后对cell元素进行cas累加
               
               
                如果cells正在初始化则cas累加base值
               
              
               sum()
               
             
                base + 数组所有元素和
               
               
                add方法没有返回值,想要获取当前值就得调用sum方法,得到的不是一个精确值
               
              
              ThreadPoolExecutor
              
               
               
             
             
               作用
               
              
                提升响应速度
               
               
                避免频繁的创建和销毁线程
               
               
                防止创建过多线程
               
               
                管理和监控线程
               
              
               源码
               
               
              
                构造参数
                
               
                 corePoolSize
                 
                
                  prestartAllCoreThreads():线程池会提前创建并启动所有核心线程
                 
                 
                  allowsCoreThreadTimeOut() : 允许核心线程空闲回收
                 
                
                 maximumPoolSize
                 
                
                  空闲线程过多会占用内存,空闲销毁
                 
                 
                  为什么要队列满了才创建到最大线程数,而不是直接创建到最大线程数
                  
                 
                
                 BlockingQueue<Runnable> workQueue
                 
                
                  阻塞队列主要成员
                 
                
                 keepAliveTime + timeUnit
                 
                
                  空闲存活时间,当达到设定的时间从队列里获取不到任务后则会销毁线程
                 
                 
                  0表示执行完任务非核心线程立即终止
                 
                
                 ThreadFactory
                 
                
                  可以设置线程名称、守护线程,线程优先级
                 
                
                 RejectedExecutionHandler
                 
               
                  AbortPolicy(default)
                 
                 
                  DiscardPolicy
                 
                 
                  DiscardOldestPolicy
                 
                 
                  CallerRunsPolicy
                  
                 
                   风险
                   
                  
                 
                  自定义
                  
                 
                
                AtomicInteger
                
                 ctl
                
                = new AtomicInteger(
                
                 ctlOf(RUNNING, 0)
                
                )
                
                
                 
                
                
               
                 保证对运行状态和线程数量操作的原子性,无需使用锁同步,提升了性能
                
                
                 获取线程池状态
                 
                  
                 
                
                
                  高3位记录线程池的运行状态
                  
                 
                   c & CAPACITY => c & 00011111111111111111111111111111 => 3个0+c的后29位二进制
                  
                 
                 获取线程数量
                 
               
                  低29位记录线程数量
                  
                
                   c & ~CAPACITY => c & 11100000000000000000000000000000 => c 保留前3位二进制 + 29个0
                  
                 
                内部类Worker
                
                 
                
               
               
                 实现Runnable
                 
                
                  线程池里每个线程就是一个worker
                 
                 
                  职责
                  
                
                   执行firstTask
                  
                  
                   getTask():从阻塞队列拉取任务执行
                  
                 
                 继承AQS
                 
                
                  重写tryAcquire(),实现非重入锁
                  
                
                   在执行runWorker方法里执行task逻辑时候,会先lock,执行完unlock,这个锁不可重入的
                  
                  
                   why
                   
                 
                    因为在比如
                    
                     shutdown
                    
                    、
                    
                     setCorePoolSize
                    
                    ,
                    
                     setMaximumPoolSize
                    
                    等方法也会使用worker实现的不可重入锁,
                    
原因就是当worker正在执行任务的时候,而这些方法会对线程进行中断(调用interruptIdleWorkers()),
那么就会影响到当前正在执行的任务
                   
                   原因就是当worker正在执行任务的时候,而这些方法会对线程进行中断(调用interruptIdleWorkers()),
那么就会影响到当前正在执行的任务
                    shutdownNow()则不会tryLock,而是直接中断线程
                   
                  
                提交任务流程
                
              
                 如果线程池中线程数量小于核心线程数(>0),则新建线程执行任务
                 
                
                  核心线程数=0,则直接丢进队列,并且开启一个线程去队列拉取任务执行
                 
                
                 如果线程池中数量大于等于核心线程数,且队列未满,则将任务放入到队列
                 
                
                
                 如果队列也满了,则看线程池中线程数量是否小于最大线程数,如果没有则新建线程执行任务
                
                
                 如果已经达到最大线程数且队列已满,则执行拒绝策略
                
               
               Executors工具类
               
              
                静态工厂创建几种常用线程池
                
              
                 newFixedThreadPool
                 
                
                  核心线程数=最大线程数,不会回收线程
                 
                 
                  LinkedBlockingQueue
                 
                 
                  因为核心线程不会被回收,所以内部类worker对象会一直持有外部类ThreadPoolExecutor(workQueue.take())引用,导致其不会被gc。
                  
所以需要手动shutDown(),关闭线程池,否则无法被gc造成内存泄漏
                 
                所以需要手动shutDown(),关闭线程池,否则无法被gc造成内存泄漏
                 newSingleThreadExecutor
                 
                
                  核心线程数=最大线程数=1,线程不会空闲关闭
                 
                 
                  LinkedBlockingQueue
                 
                 
                  线程不会被回收,但是也无需shutDown(),因为被FinalizableDelegatedExecutorService包装了,
                  
gc会调用finalize(),从而隐式调用shutDown()
                 
                 gc会调用finalize(),从而隐式调用shutDown()
                  单线程的线程池意义
                  
                
                   保证任务按照提交的顺序执行
                   
                  
                 
                 newCachedThreadPool
                 
                
                  核心线程数=0,最大线程数不限制,60s空闲则回收掉
                 
                 
                  SynchronousQueue
                  
                 
                   不存储元素,每一个put必须等待take操作
                  
                 
                  因为线程会空闲被全部回收,无需手动shutDown()
                 
                 
                  场景
                  
                
                   执行大量短暂的异步任务,提升性能
                  
                  
                   nacos长轮询线程池
                  
                 
                 newScheduledThreadPool
                 
               
                  DelayedWorkQueue
                 
                 
                  ScheduledExecutorService#schedule(commod,delay,unit)
                  
                 
                   只执行一次
                  
                 
                  ScheduledExecutorService#scheduleAtFixedRate(commod,initialDelay,
                  
                   period
                  
                  ,unit)
                  
                 
                   以固定的频率
                  
                 
                  ScheduledExecutorService#scheduleWithFixedDelay(commod,initialDelay,
                  
                   delay
                  
                  ,unit)
                  
                
                   以固定的延时
                  
                  
                   会考虑任务执行时间,进行顺延
                  
                 
               使用
               
              
                线程数设置
                
               
                 I/O密集型
                 
                
                  cpu核数 * 2
                 
                
                 CPU密集型
                 
                
                  cpu核数 +1
                 
                
                 动态化设置
                 
               
                  监控线程池,通过配置中心快速调整参数,减少故障恢复时间
                 
                 
                  监听配置变化
                  
                
                   setCorePoolSize(int)
                  
    
                  
                   setMaximumPoolSize(int)
                  
                 
                线程空闲时间设置
                
               
                 可动态设置setKeepAliveTime()
                
               
                队列设置
                
               
                 类型选择
                
                
                 大小设置
                
                
                 队列的容量
                 
                 
               
                  没有动态设置队列容量的方法
                  
                  
                
                   容量被final修饰
                   
                  
                  
                   解决:自定义队列,可修改容量
                   
                  
                 
                Hippo4j
                
              
                 支持动态设置参数、自定义报警和运行监控
                 
                
               
                线程饥饿死锁
                
               
                 父子任务使用同一个线程池,父任务用完所有线程,子任务获取不到线程,导致等待死锁
                 
                
                
                 分别定义不同的线程池
                
               
                线程执行task发生异常,线程池会怎么处理这个线程
                
              
                 exectute提交会抛出堆栈异常,submit不会,可以通过future.get()获取异常
                
                
                 不影响其他线程执行任务
                
                
                 线程池会移除这个线程,并创建一个新线程放入到线程池
                
               
              BlockingQueue
              
             
               ArrayBlockingQueue
               
              
                添加元素
                
               
                 add(e)
                 
                
                  队列满则抛出IllegalStateException("Queue full")
                  
                 
                
                 offer(e)
                 
                 
                
                  返回true和或false
                 
                
                 offer(e,time,unit)
                 
                
                  如果队列满了,则阻塞,到了设定时间没有成功则返回false
                 
                
                 put(e)
                 
               
                  如果队列满了则阻塞
                 
                
                取出元素
                
               
                 remove(e)
                 
                 
                
                  返回true或false
                 
                
                 remove()
                 
                
                  没有则NoSuchElementException
                 
                
                 poll()
                 
                 
                
                  有元素则返回元素,否则返回null
                 
                
                 poll(time,unit)
                 
                 
                
                  如果不存在元素,则阻塞指定时间,没有则返回null
                 
                
                 take()
                 
               
                  队列为空则阻塞
                 
                
                源码
                
              
                 构造方法
                 
                
                  底层是数组
                 
                
                 添加和取出方法都使用了ReentrantLock进行同步
                
                
                 条件队列:notEmpty = lock.newCondition()
                
                
                 条件队列:notFull = lock.newCondition()
                
                
                 takeIndex
                 
                
                  take获取元素的下标,然后++takeIndex,如果等于队列长度,则赋值为0
                 
                
                 putIndex
                 
               
                  put元素的下标,然后++putIndex,如果等于队列长度,则赋值为0
                 
                
               LinkedBlockingQueue
              
              
               SynchronousQueue
               
              
                不能存储元素, 每个put()都必须等到一个take(),才能解除阻塞, 反之亦然
               
              
               PriorityBlockingQueue
              
             
              其他
              
              
            
               FutureTask
               
              
                通过实现
                
                 RunnableFuture
                
                间接
                
继承了Runnable和Future接口
                
               继承了Runnable和Future接口
                 tips:接口可以多继承
                
               
                构造方法
                
               
                 入参为Callable
                
                
                 入参为Runnalbe
                 
               
                  通过适配器的方式将Runnable转化为Callable
                 
                 
                  runnable返回值为void,传入Result有何意义?
                  
可以通过get方法知道call方法有没有执行完毕
                 
                可以通过get方法知道call方法有没有执行完毕
                task的运行状态
               
               
                get()
                
               
                 首先会判断任务状态,如果是<=completing则包装成waitNode放入到等待队列里,并且LockSupport.park当前线程。
                 
否则根据任务是否是normal或者exceptional等返回任务运行结果或者异常
                
                否则根据任务是否是normal或者exceptional等返回任务运行结果或者异常
                 总结
                 
               
                  根据
                  
                   任务的状态值
                  
                  决定是阻塞、返回结果还是抛出异常
                 
                
                run()
                
              
                 执行run方法,最终会执行callable#call(),不管正常还是异常,都会设置结果,并且会设置
                 
任务状态为Nomal或者Exceptional,最后LockSupport.unpark唤醒之前get里阻塞的线程
                
                任务状态为Nomal或者Exceptional,最后LockSupport.unpark唤醒之前get里阻塞的线程
                 总结
                 
               
                  执行任务,设置执行结果,唤醒阻塞的线程
                 
                
               CompletableFuture
               
              
                Future局限性
                
               
                 get方法获取执行结果,是阻塞的
                
                
                 无法对多个任务进行链式调用
                
                
                 无法组合多个任务
                
                
                 没有异常处理
                
               
                解决Future的局限性的问题
               
              
               ForkJoin框架
               
             
                用于并行执行任务的框架,把一个大任务分割成若干个小任务,
                
最终汇总每个小任务结果的后得到大任务结果的框架
               
               最终汇总每个小任务结果的后得到大任务结果的框架
                适合
                
               
                 计算密集型任务
                
               
                流程
                
              
                 fork-任务分解
                
                
                 任务执行
                
                
                 工作窃取
                 
                
                  当线程完成队列中任务空闲时,去别的队列获取任务执行
                 
                
                 join-结果合并
                
               
             开发框架
             
             
            
              Spring
              
             
               IOC
               
                
                
              
              
                设计思想
                
               
                 控制反转,由使用者创建管理对象变成spring自动创建和管理对象,使用者只需要从容器中获取即可
                
                
                 DI,依赖注入,spring帮助我们自动管理bean的依赖关系
                
               
                定位->加载->注册
                
               
                 
                  AbstarctApplicationContext#
                  
                   refresh()
                  
                  ,
                 
                 会通过解析xml或者扫描注解,然后生成BeanDefinition,
                 
注册到beanDefinitionMap中,后面会进行实例化,属性注入以及初始化后放入到singletonObjects缓存中
                
               注册到beanDefinitionMap中,后面会进行实例化,属性注入以及初始化后放入到singletonObjects缓存中
                 循环依赖
                 
                
                  Spring中循环依赖几种情况
                  
                 
                   构造器的循环依赖
                   
                  
                    Spring解决不了,因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
                   
                  
                   setter方式原型
                   
                  
                    Spring解决不了,检测到则会抛出异常BeanCurrentlyInCreationException
                   
                  
                   setter方式单例
                   
                 
                    三级缓存
                    
                   
                     一级:singletonObjects
                     
                    
                      存放实例化好的单例对象(成品)
                     
                    
                     二级:earlySingletonObjects
                     
                     
                    
                      提前曝光的单例对象,还没有完全初始化好(半成品)
                     
                    
                     三级:singletonFactories
                     
                   
                      要被实例化对象的对象工厂
                      
                     
                    
                    通过二级缓存提前暴露了不完整的依赖对象的引用,从而解决循环依赖问题
                   
                  
                  如何判定发生循环依赖
                  
                 
                   比如A依赖B,B依赖A,当创建A时候,会将A的beanName进行保存(ThreadLocal),标识A正在创建。
                   
发现依赖B,会去创建B,而B又依赖A,发现A还在创建中,判定A和B循环依赖了
                  
                 发现依赖B,会去创建B,而B又依赖A,发现A还在创建中,判定A和B循环依赖了
                  只要二级缓存可以吗?
                  
为什么必须要有第三级缓存?
                  
                为什么必须要有第三级缓存?
                   不可以,第三级缓存实现aop的。
                   
1、如果没有三级,那么二级只能存被代理的普通bean。
2、而三级缓存存放的是singletonFactory,这是一个函数式接口也就是内部类,当从三级获取的时候,
会调用singletonFactory#getObject方法会判断bean是否需要被代理,如果需要则会对其动态代理返回代理对象,否则返回普通bean。
                  
                 1、如果没有三级,那么二级只能存被代理的普通bean。
2、而三级缓存存放的是singletonFactory,这是一个函数式接口也就是内部类,当从三级获取的时候,
会调用singletonFactory#getObject方法会判断bean是否需要被代理,如果需要则会对其动态代理返回代理对象,否则返回普通bean。
                 Bean生命周期
                 
                  
                  
                 
                
                   实例化-》依赖注入-》初始化-》销毁
                   
                  
                 
                  平时对生命周期做了哪些拓展
                  
                
                   ApplicationContextAware设置ApplicationContext
                   
                  
                  
                   Dubbo
                  
                 
                 BeanFactory & FactoryBean & ApplicationContext
                 
                
                  BeanFactory
                  
                 
                   BeanFactory是IOC顶层接口,负责生产和管理bean
                  
                  
                   ApplicationContext
                   
                 
                    继承了BeanFactory,对其进行功能增强:国际化、事件发布、资源访问等
                   
                  
                  FactoryBean
                  
                
                   spring分为两种bean,一种是普通bean,一种的实现了FactoryBean的bean。
                   
通过getBean(beanName)获取的是getObject()返回的对象,
想要获取FactoryBean本身,则通过getBean( & + beanNmae)获取
                  
                  通过getBean(beanName)获取的是getObject()返回的对象,
想要获取FactoryBean本身,则通过getBean( & + beanNmae)获取
                   通常用来创建一些比较复杂的bean,比如dubbo的ReferenceBean、spring的MapperFactoryBean
                  
                 
                 @Autowired & @Resource
                 
                
                  @Autowired
                  
                 
                   AutowiredAnnotationBeanPostProcessor在容器启动的时注册
                  
                  
                   AutowiredAnnotationBeanPostProcessor#postProcessProperties实现
                   
                  
                  
                   当自动装配时,从容器中如果发现有多个同类型的属性时,@Autowired注解会先根据类型判断,然后根据@Primary、@Priority注解判断,
                   
最后根据属性名与beanName是否相等来判断,如果还是不能决定注入哪一个bean时,就会抛出NoUniqueBeanDefinitionException异常
                  
                 最后根据属性名与beanName是否相等来判断,如果还是不能决定注入哪一个bean时,就会抛出NoUniqueBeanDefinitionException异常
                  @Resource
                  
                 
                   CommonAnnotationBeanPostProcessor在容器启动时注册
                  
                  
                   CommonAnnotationBeanPostProcessor#postProcessProperties
                  
                 
                  区别
                  
                
                   @Autowired 是 Spring 定义的注解,
                   
@Resource 是 JSR 250 规范里面定义的注解,而 Spring 对 JSR 250 规范提供了支持
                  
                  @Resource 是 JSR 250 规范里面定义的注解,而 Spring 对 JSR 250 规范提供了支持
                   @Autowired 是根据 type 来匹配。如果需要支持 name 匹配,就需要配合@Primary 或者@Qualifier
                   
来实现
@Resource 可以根据 name 和 type 来匹配,默认是 name 匹配
                   
                    
                   
    
                  
                 来实现
@Resource 可以根据 name 和 type 来匹配,默认是 name 匹配
                 @Bean & @Component
                 
                
                  前者作用于方法,后者作用于类
                 
                 
                  当需要把第三方库中的类装配到 Spring 容器时,通过 @Bean 来实现
                 
                
                 Spring将配置文件解析成什么后注册到容器?
                 
                
                  
                   BeanDefinition
                  
                 
                
                 BeanFactoryPostProcessor & BeanPostProcessor
                 
                
                  BeanFactoryPostProcessor
                  
                 
                   容器后处理器,spring预留给我们的拓展点,可以在所有bean注册之后(实例化之前),获取beanDefinition并做一些处理
                  
                 
                  BeanPostProcessor
                  
                
                   可在bean初始化过程中,对bean做一些前置和后置处理
                  
                  
                   spring AOP、@Autowired等都是通过BeanPostProcessor实现
                  
                  
                   InstantiationAwareBeanPostProcessor
                   
                   
                 
                    继承BeanPostProcessor
                   
                   
                    作用
                    
                   
                     定义了bean
                     
                      实例化
                     
                     前后的方法,设置属性以及初始化前后方法的方法
                    
                   
                    实现
                    
                  
                     AutowiredAnnotationBeanPostProcessor
                     
                    
                      @Autowire、@Value等
                     
                    
                     AbstractAutoProxyCreator
                     
                   
                      spring AOP
                     
                    
                 scope
                 
                
                  singleton、prototype、request、session、global-session
                 
                 
                  单例A依赖原型B
                  
                
                   解决方案
                   
                 
                    A注入ApplicationContext,每次从其getBean(B.class)重新获取B对象
                   
                   
                    @Lookup
                    
                   
                  
                 bean是线程安全的吗
                 
               
                  spring没有提供线程安全策略
                 
                 
                  分析
                  
                 
                   protoype类型的bean
                   
                  
                    每次都会创建bean,不存在线程共享,所以是安全的
                   
                  
                   singleton类型的bean
                   
                 
                    无状态bean
                    
                   
                     只会查询成员变量,所以安全
                    
                   
                    有状态
                    
                  
                     会修改成员变量,不安全
                    
                   
                  解决
                  
                
                   将成员变量保存在ThreaLocal中
                  
                  
                   对成员变量的修改使用锁或者使用线程安全的数据结构
                   
                  
                  
                   作用域从singleton改为prototype
                  
                 
               AOP
               
              
                面相切面编程,将与
                
                 业务无关的、通用的代码逻辑
                
                (比如日志、事务等等)抽取并封装起来,并通过动
                
态代理的方式作用于目标对象和方法,从而达到了 避免重复代码 ,与目标对象 解耦 的目的
               
               态代理的方式作用于目标对象和方法,从而达到了 避免重复代码 ,与目标对象 解耦 的目的
                概念
                
               
                 通知(Advice)
                 
                
                  需要执行的操作,包括了 before、after、around 等多种类型
                 
                 
                  类型
                  
                
                   前置通知
                   
@Before
                  
                  @Before
                   返回后通知
                   
@AfterReturning
                   
                  @AfterReturning
                    在目标方法正常执行并返回之后执行的通知,目标方法异常则不会执行
                   
                  
                   后置通知
                   
@After
                   
                  @After
                    在目标方法执行完成,目标方法正常&异常都会执行
                   
                  
                   环绕通知
                   
@Around
                  
                  @Around
                   异常通知
                   
@AfterThrowing
                  
                 @AfterThrowing
                 切点(Pointcut)
                 
                
                  一组连接点的集合,它用来定义哪些方法需要被增强
                 
                
                 切面(Aspect)
                 
               
                  一个包含切点和通知的对象,它将切点和通知组合在一起
                  
                 
                
                源码实现
                
               
                 在bean的初始化阶段,执行到一个AbstractAutoProxyCreator的BeanPostProcessor后置处理器的后置方法(postProcessAfterInitialization),
                 
会调用wrapIfNessary方法判断是否需要被代理,需要则对其进行动态代理,织入切面,从而返回代理bean
                
               会调用wrapIfNessary方法判断是否需要被代理,需要则对其进行动态代理,织入切面,从而返回代理bean
                动态代理
                
               
                 jdk(spring默认)
                 
                
                  被代理类实现接口
                 
                
                 cglib(springboot默认)
                 
               
                  被代理类不是接口
                 
                 
                  proxy-target-class = true
                 
                
                AOP
                
              
                 Spring AOP
                 
                
                  动态织入(运行阶段),通过动态代理的方式实现织入
                 
                 
                  只能作用于 Spring 容器中的 bean。通过bean的生命周期拓展点的beanPostProcessor的后置方法对bean进行代理
                 
                 
                  获取被代理对象:Class<?>  clazz = AopUtils.getTargetClass(Object candidate)
                 
                
                 AspectJ
                 
               
                  静态织入(编译阶段、编译后 或 类加载),基于修改字节码。
                  
是通用型代理技术(功能更强大),使用复杂
                 
                是通用型代理技术(功能更强大),使用复杂
               Spring MVC
               
               
              
                请求流程
                
               
               
                过滤器和拦截器区别
                
                
               
                 过滤器属于servlet,拦截器属于spring mvc
                 
                
                
                 过滤器基于函数回调实现,拦截器基于反射实现
                 
                
                
                 过滤器作用于request和response,拦截器作用于controller
                 
                
               
                统一异常处理
                
               
              
               事务
               
              
                7大传播行为
                
(PROPAGATION)
                
               (PROPAGATION)
                 REQUIRED
                
                
                 NESTED
                
                
                 REQUIRES_NEW
                
                
                 SUPPORTS
                
                
                 NOT_SUPPOSRTED
                
                
                 MANDATORY
                
                
                 NEVER
                
               
                实现方式
                
              
                 编程式
                 
                
                  
                   
                    TransactionTemplate#execute
                   
                  
                 
                 
                  可实现更细粒度的事务控制,避免长事务
                  
(比如方法中一些不需要在事务中逻辑,如果用声明式的话,就是长事务了)
                 
                (比如方法中一些不需要在事务中逻辑,如果用声明式的话,就是长事务了)
                 声明式
                 
               
                  @Transactional失效
                  
                 
                   异常被catch并且没有再抛出
                  
                  
                   异常类型
                  
                  
                   
                    被标注的方法不是public修饰(只能是public)
                   
                   
                   
                  
                    jdk动态代理是接口,接口不能定义私有方法
                   
                   
                    cglib的话,继承,私有方法不能被继承
                   
                  
                   (自调用)同一个Service里,
                   
                    普通方法
                   
                   调用被标注的方法,
                   
因为事务是基于动态代理实现的,普通方法不会被代理
                  
                  因为事务是基于动态代理实现的,普通方法不会被代理
                   所用数据源是否加载了事务管理器
                  
                  
                   数据库的存储引擎不支持事务
                  
                  
                   被标注的方法所在的类没有被Spring所管理
                  
                  
                   传播行为是否正确,比如NOT_SUPPORTED就会挂起事务
                  
                 
                  原理
                  
                 
                   因为通过aop实现,所以最终走到
                   
                    
                     TransactionInterceptor
                    
                   
                   拦截器执行invoke方法
                  
                  
                   获取事务的属性、事务管理器,然后根据事务传播行为是加入事务、挂起当前事务创建新事务等。
                   
然后执行目标方法,发生异常根据异常类型判断是回滚还是提交,如果执行成功则提交事务。
                  
                  然后执行目标方法,发生异常根据异常类型判断是回滚还是提交,如果执行成功则提交事务。
                   通过threadLocal保证获取的是同一个连接
                  
                 
                  TransactionSynchronization
                  
                
                   同步器,事务提交前后会从threadLocal中获取注册的同步器执行对应的方法
                  
                  
                   注册
                   
                 
                    
                     
                      TransactionSynchronizationManager
                     
                    
                    #
                    
registerSynchronization(TransactionSynchronization synchronization)
                    
                  registerSynchronization(TransactionSynchronization synchronization)
                     添加到threadLocal中列表中,
                     
在事务节点中(事务完成,事务提交等)遍历列表执行对应方法
                    
                   在事务节点中(事务完成,事务提交等)遍历列表执行对应方法
               异步
               
              
                使用
                
               
                 启动类或配置类+@EnableAsync开启异步
                 
                
                  可以实现AsyncConfigurer来指定executor和可以实现AsyncConfigurer
                  
来指定Executor和AsyncUncaughtExceptionHandler
                 
                来指定Executor和AsyncUncaughtExceptionHandler
                 方法或类+@Async
                 
               
                  不要返回值直接void;需要返回值用AsyncResult或者CompletableFuture
                 
                 
                  可自定义执行器,@Async(”executorBeanName“)
                 
                 
                  动态代理,自调用会失效
                  
                
                   解决
                   
                 
                    将异步方法放入到另一个类中
                   
                   
                    从容器中获取代理对象进行调用
                   
                  
                原理
                
              
                 @Async注解的拦截器是
                 
                  AsyncExecutionInterceptor
                 
                
                
                 拦截器拦截把需要异步执行的方法包装成task丢到线程池异步执行
                 
               
                  如果不指定Executor(@Async)
                  
且没有重写AsyncConfigurer
                  
                且没有重写AsyncConfigurer
                   默认会从spring容器中
                   
                    
                     唯一
                    
                   
                   的TaskExecutor bean,没有唯一的则搜索名为“taskExecutor”的Executor bean。
                   
如果都没有则使用SimpleAsyncTaskExecutor来处理任务,每次都new线程,不会复用线程
                  
                 如果都没有则使用SimpleAsyncTaskExecutor来处理任务,每次都new线程,不会复用线程
               设计模式
               
             
                单例模式
               
               
                原型模式
               
               
                代理模式
                
               
                 Spring aop
                 
                
               
                模板方法
                
               
                 JdbcTemplate
                 
                
    
               
                观察者模式
                
               
                 ContextRefreshEvent
                
               
                工厂方法
                
                
               
                 FactoryBean
                 
                
               
                适配器模式
                
               
                 spring mvc中HandlerAdapter适配各种Controller
                 
                
               
                装饰器模式
                
               
                 Spring的BeanWrapper允许在不修改原始Bean类的情况下添加额外的功能
                
               
                责任链
                
               
                 aop拦截链
                
               
                策略模式
               
              
              SpringBoot
              
             
               @SpringBootApplication复合注解
               
               
              
                @SpringBootConfiguration(用作@Configuration替代品)
                
               
                 说明被SpringBootApplication
                 
所标识的Java类就是一个Java配置类
                
               所标识的Java类就是一个Java配置类
                @ComponentScan(加载
                
                 内部
                
                组件到容器中)
                
               
                 可以从basePackageClasses或basePackages来定义要扫描的包。
                 
如果没有定义,则从声明此注解的类的包中进行扫描
                
               如果没有定义,则从声明此注解的类的包中进行扫描
                @EnableAutoConfiguration(加载
                
                 
                  外部
                 
                
                的组件到容器中)
                
              
                 打开自动装配功能,到META-INF/spring.factories文件中加载需要自
                 
动注入的Java类到容器中 (SPI机制)
                动注入的Java类到容器中 (SPI机制)
                 原理
                 
               
                  @Import(
                  
AutoConfigurationImportSelector.class
)
                  
                AutoConfigurationImportSelector.class
)
                   @import
                   
                  
                    作用:表示要导入的一个或多个组件类
                    
                  
                     常规组件,比如@service、@comment、@controller等
                    
                    
                     标记@Configuration类
                    
                    
                     ImportSelector实现类进行动态注入
                    
                    
                     ImportBeanDefinitionRegistrar实现类进行动态注入
                    
                   
                   AutoConfigurationImportSelector
                   
                 
                    1、扫描META-INF/spring-autoconfigure-metadata.properties
                    
                    
                   
                     ConditonalOnClass、ConditonMissingClass等,根据条件判断是否加载
                    
                   
                    2、再扫描META-INF/
                    
                     spring.factories
                    
                    
                    
                   
                     SpringFactoriesLoader
                     
                   
                      在spring.factories 文件中,根据
                      
org.springframework.boot.autoconfigure.EnableAutoConfiguration = xxxx
来读取需要加载的类的全路径xxxx
                     
                    org.springframework.boot.autoconfigure.EnableAutoConfiguration = xxxx
来读取需要加载的类的全路径xxxx
                    3、用前者过滤后者数据,最后返回需要注入的类的全路径数组
                   
                  
               自动装配流程
               
              
              
               Starter
               
              
                what
                
               
                 starter的作用是在META-INF目录下提供了一个spring.factories文件,
                 
在该文件中我们添加了一个需要注入到Spring容器中的对应的配置类。
                
               在该文件中我们添加了一个需要注入到Spring容器中的对应的配置类。
                自定义Starter
               
               
                实际应用
               
              
               为什么能java -jar xx.jar启动
               
              
                通过
                
                 
                  JarLaucher
                 
                
                的main方法:
                
1、创建 自定义类加载器 (LaunchedURLClassLoader)
2、并设置为上下文的类加载器
3、最后执行真正的启动类的main方法,使用内置tomcat运行应用
               
              1、创建 自定义类加载器 (LaunchedURLClassLoader)
2、并设置为上下文的类加载器
3、最后执行真正的启动类的main方法,使用内置tomcat运行应用
               其他
               
             
                跨域
                
               
                 当一个请求url的 协议、域名、端口 三者之间任意一个与当前页面url不同即为跨域
                
                
                 三种解决方案
                 
               
                  配置CorsFilter
                 
                 
                  重写WebMvcConfigurer
                 
                 
                  @CrossOrigin
                  
                
                   定义在Controller类上
                  
                  
                   定义在方法上
                  
                 
                和spring关系
                
               
               
                过滤器、拦截器、AOP
               
              
              MyBatis
              
            
               源码
               
              
                解析
                
               
                 解析配置文件封装成Confiuration类
                 
                
                  Configuration
                 
                
                 解析mapper.xml文件,放到knownMappers的map容器
                 
               
                  knownMappers<mapper接口.class,MapperProxyFactory>
                 
                 
                  mapper为啥没有实现类
                  
                 
                
                执行流程
                
              
                 SqlSessionFactory开启一个sqlSession,根据mapper接口从knownMappers中拿到代理工厂(mapperProxyFactory),并生成代理对象MapperProxy
                
                
                 调用MapperProxy的invoke方法,最终执行MapperMethod#execute
                
                
                 MapperMethod#execute,根据根据sql的增删改查的类型走相应的逻辑
                 
               
                  查询执行sqlSession的select方法
                  
                 
                   根据是否开启二级和一级缓存执行相应逻辑
                   
                 
                    开启二级缓存
                    
                   
                     命中返回,否则查询数据库
                    
                    
                     二级缓存逻辑在CachingExecutor
                     
                   
                      未命中则继续走BaseExecutor,然后设置二级缓存
                     
                    
                    未开启二级缓存
                    
                  
                     直接走BaseExecutor
                    
                   
                  增删改执行sqlSession的update方法
                  
                
                   会清空所有缓存
                  
                 
               缓存
               
              
                一级缓存
                
               
                 Session级别,维护在BaseExecutor
                
                
                 开启&关闭
                 
                
                  默认开启(无法关闭)。
                  
每次查询其实都会设置一级缓存
                  
                每次查询其实都会设置一级缓存
                   flushCache(默认false)
                   
                  
                    查询前,如果设置为ture则会删除一级缓存
                   
                  
                   localCacheScope(默认SESSION)
                   
                 
                    设置STATEMENT会删除一级缓存
                   
                  
                 hit condition
                 
                  
                
                - flushCache=false
 - localCacheScope=session
 - 同一个sqlSession
 - 相同statementId
 - 相同的sql,参数
 - 没有执行update操作
 
                 与Spring整合
                 
               
                  spring每次都会new一个sqlSession,所以不是同一会话,一级缓存会失效,除非开启了事务,使用同一会话
                 
                
                二级缓存
                
              
                 应用级别,维护在CachingExecutor。因为不是会话级,所以是事务缓存
                
                
                 开启
                 
                
                  mapper.xml配置<cache></cache>即可。在方法里配置useCache为false,即可关闭某个sql的二级缓存
                 
                
                 hit condition
                 
                  
                
                - 相同的statement id
 - 相同的Sql与参数
 - 没有使用ResultHandler来自定义返回数据
 - 没有配置useCache=false 来关闭缓存
 - 没有配置flushCache=true 来清空缓存
 
                 TransactionalCacheManager
                 
                
                  query方法设置二级缓存时候并未提交,而是在会话结束时候,关闭会话里提交或者回滚事务缓存
                 
                
                 集群模式下会出现一致性问题
                 
               
                  mybatis-redis
                 
                
               四大组件
               
              
                Executor
                
               
                 方法分为三类
                 
                
                  执行
                 
                 
                  事务
                 
                 
                  缓存
                 
                
                 BaseExecutor
                 
                
                  SimpleExecutor(默认)
                 
                 
                  ReuseExecutor
                 
                 
                  BatchExecutor
                 
                
                 CachingExecutor
                 
               
                  开启二级缓存则装饰BaseExecutor
                 
                
                StatementHandler
                
               
                 负责处理Mybatis与JDBC之间Statement的交互
                 
                
                  创建statement、sql参数赋值、和数据库交互等
                 
                
                 配置:statementType
                
                
                 实现
                 
               
                  SimpleStatementHandler
                 
                 
                  PreparedStatementHandler(默认)
                 
                 
                  CallableStatementHandler
                 
                 
                  RoutingStatementHandler
                 
                
                ParameterHandler
                
               
               
                ResultSetHandler
               
              
               Plugin
               
              
                通过拦截四大组件进行相应的操作
               
               
                PageHelper、sql监控、分表等
               
              
               TypeHander
               
              
                Java类型和JDBC类型的映射
               
               
                可以自定义
               
              
               实体属性和表字段不一致
               
              
                指定别名
               
               
                resultMap
                
                
              
                 ResultType/ResultMap区别
                 
               
                  ResutType
                  
                 
                   只适用于基本类型以及
                   
                    简单
                   
                   pojo
                  
                 
                  ResultMap
                  
                
                   可以映射表字段和实体属性,支持一对一(association)和一(多)对多(collection)
                  
                 
               如何获取生成的主键
              
              
               动态SQL
               
              
                <if>、<where>、<set>、<foreach>等
               
              
               设计模式
               
              
                建造者
                
               
                 SqlSessionFactoryBuilder
                
               
                工厂
                
               
                 SqlSessionFactory
                
    
               
                模板方法
                
               
                 BaseExecutor子类有SimpleExecutor,BatchExecutor和ReuseExecutor
                
               
                装饰器
                
                
               
                 CachingExecutor装饰BaseExecutor
                
                
                 PerpetualCache被FifoCache、LruCache、WeakCache等装饰
                
               
                代理
                
               
                 MapperProxy
                
               
                单例
                
               
                 ErrorContext,线程级别单列
                
               
                责任链
                
              
                 插件
                
               
               spring整合mybatis原理
               
              
             
             微服务框架
             
             
            
              Dubbo(2.6.x)
              
             
               RPC & 服务治理框架
               
              
                1、解决分布式系统中,服务之间的调用问题。
                
2、远程调用时,要能够像 本地调用一样方便 ,让调用者感知不到远程调用的逻辑。
               
              2、远程调用时,要能够像 本地调用一样方便 ,让调用者感知不到远程调用的逻辑。
               设计
               
              
                设计层级(10层)
                
                
               
               
                 Service:业务层,就是咱们开发的业务逻辑层。
                
                
                 Config配置层:主要围绕 ServiceConfig 和 ReferenceConfig,初始化配置信息。
                
                
                 Proxy 代理层:服务提供者还是消费者都会生成一个代理类,使得服务接口透明化,代理层做远程调用和返回结果。
                
                
                 Register 注册层:封装了服务注册和发现
                
                
                 Cluster 路由和集群容错层:负责路由、负载均衡、容错等
                
                
                 Monitor 监控层:负责监控统计调用时间和次数。
                
                
                 Portocol 远程调用层:主要是封装 RPC 调用,主要负责管理 Invoker,Invoker代表一个抽象封装了的执行体
                
                
                 Exchange 信息交换层:用来封装请求响应模型,同步转异步
                
                
                 Transport 网络传输层:抽象了网络传输的统一接口,可以选择 Netty 或 Mina
                
                
                 Serialize 序列化层:序列化跟反序列化
                
                
                调用链
                
                
               
              
               源码
               
              
                SPI
                
                
               
                 和原生SPI的区别
                 
                
                  通过键值对的方式进行配置,可以
                  
                   
                    按需加载
                   
                  
                  指定的实现类
                 
                 
                  拓展点增强(AOP)
                  
                 
                   自动包装扩展点的 Wrapper 类。ExtensionLoader 在加载扩展点时,
                   
如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类
                  
                  如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类
                   通过装饰器模式对真正的拓展实现类进行增强,类似AOP
                   
                  
                  
                   Wrapper类Demo
                   
                 
                    Wrapper类判断
                   
                  
                  拓展点自动注入(IOC)
                  
                
                   一个拓展点可以自动setter注入其它拓展点
                  
                 
                 约定目录
                
                
                 拓展点的获取
                 
                
                  getExtension(String name)
                 
                 
                  拓展点自适应
                  
@Adaptive
                  
                 @Adaptive
                   类上
                   
                  
                    在加载时候,直接赋值给cachedAdaptiveClass(只允许一个类加注解)
                   
                   
                    getAdaptiveExtension()获取的就是加了注解的拓展点
                   
                  
                   方法上
                   
                 
                    动态代理
                   
                   
                    getAdaptiveExtension()根据URL里配置的选择对应的拓展点,
                    
否则使用默认的(@SPI("xxx")的xxx)
                   
                  否则使用默认的(@SPI("xxx")的xxx)
                  扩展点自动激活
                  
@Activate
                 
                @Activate
                 demo
                 
               
                  定义接口和实现类,接口要加上@SPI注解
                 
                 
                  在META-INF/dubbo路径下创建文件名为接口全限定名
                 
                 
                  在上面文件里输入key=实现类全限定名
                 
                 
                  ExtensionLoader<Robot> extensionLoader =
                  
                   ExtensionLoader
                  
                  .getExtensionLoader(接口.class);
                  
extensionLoader.getExtension(key)拿到某个实现类
                extensionLoader.getExtension(key)拿到某个实现类
                初始化过程
                
               
                 解析服务
                 
                
                  基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler#init()。
                  
所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象
                 
                 所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象
                  
                   ServiceBean & ReferenceBean 在DubboNamespaceHandler里init()
                  
                 
                
                 服务暴露(ServiceBean)
                 
                 
                
                
                  Spring启动结束发布
                  
                   
                    ContextRefreshedEvent
                   
                  
                  
,ServiceBean监听到事件开始导出服务
                  
                 ,ServiceBean监听到事件开始导出服务
                   父类ServiceConfig#export()
                   
                 
                    主要是检查和更新配置,如果缺省则使用默认值。
                    
然后根据delay的值,决定是立即导出还是延迟导出
                   
                   然后根据delay的值,决定是立即导出还是延迟导出
                    获取注册中心地址,然后根据配置组装URL
                   
                   
                    分别开始在本地(injvm)和远程暴露服务,然后创建Invoker(ProxyFactory#getInvoker),
                    
根据协议(默认DubboProtocl#export)转化为Exporter放入到exporterMap。启动服务,监听端口
                   
                   根据协议(默认DubboProtocl#export)转化为Exporter放入到exporterMap。启动服务,监听端口
                    服务注册到注册中心
                   
                  
                 服务引用(ReferenceBean)
                 
                 
                
               
                  ReferenceBean#getObject()
                  
                 
                   父类ReferenceConfig#get()
                   
返回 代理对象
                   
                 返回 代理对象
                    检查和更新配置,组装URL
                   
                   
                    在注册中心consumer目录下注册新节点,同时订阅providers、configurators、routers节点数据(监听)
                   
                   
                    根据协议构建Invoker(默认DubboProtocol#refer),如果是服务有多提供者,那么是一个Invoker集合,通
                    
过Cluster(默认FailoverCluster)进行一个合并成一个虚拟Invoker
                   
                   过Cluster(默认FailoverCluster)进行一个合并成一个虚拟Invoker
                    通过ProxyFactory#getProxy(invoker)(默认javassist)对Invoker进行动态代理,返回代理对象
                   
                  
                服务调用
                
               
                 调用三种方式
                 
                 
                
                  oneway:不关心请求是否发送成功,消耗最小
                  
                 
                 
                  同步调用sync : 在 Dubbo 源码中就调用了 future.get,用户感觉方法被阻塞了,必须等结果后才返回
                 
                 
                  异步调用Async:客户端调用请求后将返回的ResponseFuture存到
                  
上下文中,用户可以随时调用future.get获取结果
                  
                上下文中,用户可以随时调用future.get获取结果
                   异步调用通过唯一ID标识此次请求
                  
                 
                 流程总结
                 
               
                  1. 首先客户端调用接口的某个方法,实际调用的是代理类,代理类会通过 cluster 从 directory 中获取一堆 invokers(如果有一堆的话),然后进行 router 的过滤(其中看配置也会添加 mockInvoker 用于服务降级),然后再通过 SPI 得到 loadBalance 进行一波负载均衡。默认的 cluster 是 FailoverCluster ,会进行容错重试处理
                  
2. 现在我们已经得到要调用的远程服务对应的 invoker 了,此时根据具体的协议构造请求头,然后将参数根据具体的序列化协议序列化之后构造塞入请求体中,再通过 NettyClient 发起远程调用。
3. 服务端 NettyServer 收到请求之后,根据协议得到信息并且反序列化成对象,再按照派发策略派发消息,默认是 All,扔给业务线程池。
4. 业务线程会根据消息类型判断然后得到 serviceKey 从之前服务暴露生成的 exporterMap 中得到对应的 Invoker ,然后调用真实的实现类。
5. 最终将结果返回,因为请求和响应都有一个统一的 ID, 客户端根据响应的 ID 找到存储起来的 Future, 然后塞入响应再唤醒等待 future 的线程,完成一次远程调用全过程。
                 
                2. 现在我们已经得到要调用的远程服务对应的 invoker 了,此时根据具体的协议构造请求头,然后将参数根据具体的序列化协议序列化之后构造塞入请求体中,再通过 NettyClient 发起远程调用。
3. 服务端 NettyServer 收到请求之后,根据协议得到信息并且反序列化成对象,再按照派发策略派发消息,默认是 All,扔给业务线程池。
4. 业务线程会根据消息类型判断然后得到 serviceKey 从之前服务暴露生成的 exporterMap 中得到对应的 Invoker ,然后调用真实的实现类。
5. 最终将结果返回,因为请求和响应都有一个统一的 ID, 客户端根据响应的 ID 找到存储起来的 Future, 然后塞入响应再唤醒等待 future 的线程,完成一次远程调用全过程。
                Cluster
                
               
                 简介
                
                
                 实现
                 
               
                  failover(默认)
                  
                 
                   切换别的服务重试
                  
                 
                  failback
                  
                 
                   异常捕获返回默认结果,然后通过时间轮后台定时重试
                  
                 
                  failsafe
                  
                 
                   异常不抛出,只打印错误日志
                  
                 
                  failfast
                  
                 
                   只会进行一次调用,失败后立即抛出异常
                  
                 
                  forking
                  
                 
                   同时调用多个服务,第一个响应返回则结束,适合读请求
                  
                 
                  broadcast
                  
                
                   会逐个调用每个服务提供者,如果其中一台报错,在循环
                   
调用结束后会抛出异常。通常用于通知所有提供者更新缓存等
                  
                 调用结束后会抛出异常。通常用于通知所有提供者更新缓存等
                Directory(服务目录)
                
                 
                
               
                  类似服务的目录,通过这个目录来查找远程服务,实际是一堆invoker集合。同样的服务可能有多个提供者。
                  
还实现了监听注册中心的功能(指的是RegistryDirectory )
                 
                还实现了监听注册中心的功能(指的是RegistryDirectory )
                 静态:StaticDirectory
                 
                
                  是静态的是因为多注册中心是写在配置里面的,不像服务可以动态变更
                 
                 
                  用在多注册中心的时候,它是一个静态目录,即固定的不会增减的
                 
                
                 动态:RegistryDirectory
                 
               
                  1、获取 invoker 列表
                  
2、监听注册中心的变化,刷新 invoker列表
                 
                2、监听注册中心的变化,刷新 invoker列表
                LoadBalance
                
               
                 配置
                 
                
                  服务端
                  
                 
                   应用级别
                  
                  
                   方法级别
                  
                 
                  消费端
                  
                
                   应用级别
                  
                  
                   方法级别
                  
                 
                 实现
                 
               
                  加权Random(默认)
                 
                 
                  加权RoundRobbin
                 
                 
                  LeastActive
                 
                 
                  ConsistentHash
                  
                
                   TreeMap<Long,Invoker> virtualInvokers
                   
                  
                    tailMap()
                   
                   
                    firstEntry()
                   
                  
                   相同参数的请求总是发到同一提供者
                  
                  
                   当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动
                  
                 
                Registry
                
               
                 有zookeeper、redis、nacos、multicast、simple等
                
                
                 zk注册订阅流程
                 
               
                  生产者在zk上使用临时节点注册服务url,在providers路径下
                 
                 
                  消费者订阅provider下节点,并且将自己url写入到consumers路径下
                 
                
                Protocol
                
               
                 dubbo
                 
                
                  特性
                 
                 
                  采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)
                 
                 
                  在大文件传输时,单一连接会成为瓶颈
                 
                 
                  常见问题
                 
    
                
                 http
                 
                
                  特性
                 
                
                 rest
                
                
                 hessian
                 
                
                  特性
                 
                
                 rmi
                 
                
                  特性
                 
                
                 redis
                
                
                 thrift
                 
                
                  优点:跨语言、使用二进制编码进行数据传输,序列化和反序列
                  
化速度快,并且数据量小,可以降低网络传输负担
                 
                化速度快,并且数据量小,可以降低网络传输负担
                 gRPC
                
                
                 webservice
                
               
                Serialization
                
               
                 hessian2(默认)
                 
                
                  跨语言高效二进制序列化方式
                 
                
                 kryo
                 
                
                  java
                 
                
                 fst
                 
                
                  java
                 
                
                 dubbo
                 
                
                  不成熟,不建议生产使用
                 
                
                 json
                
                
                 java
                 
               
                  JDK自带,性能较差
                 
                
                限流&熔断降级
                
              
                 限流
                 
               
                  预先设定一个固定的限流值,集成Sentinel等
                 
                 
                  自动根据系统或集群负载情况执行限流
                 
                
               设计模式
               
              
                代理模式
                
               
                 ProxyFactory#getProxy,对Invoker进行代理
                
               
                抽象工厂
                
               
                 ProxyFactory实现类JdkProxyFactory和JavassistProxyFactory,
                 
分别用来生产基于jdk代理机制和基于javassist代理机制的Proxy和Invoker
                
               分别用来生产基于jdk代理机制和基于javassist代理机制的Proxy和Invoker
                模板方法
                
               
                 负载均衡LoadBalance,具体实现子类实现
                
               
                策略模式
                
               
                 SPI
                
               
                责任链模式
                
               
                 Filter
                
               
                装饰器模式
                
               
                 MockClusterInvoker装饰Invoker使其拥有mock功能
                
                
                 ProtocolFilterWrapper(ProtocolListenerWrapper(InjvmProtocol))
                
                
                 DelegateProviderMetaDataInvoker装饰Invoker,持有ServiceConfig对象
                
               
                单例模式
                
               
                 容器注册式exporterMap
                
               
                观察者模式
                
              
                 RegistryDirectory,服务上线下线会调用其notify接口
                
                
                 监听Spring的ContextRefreshedEvent后开始服务暴露
                
               
               常见问题
              
             
              Spring cloud
              
            
               Netflix
               
              
                网关
                
                 
                
                
               
                 功能
                 
                
                  通用非业务功能比如认证、监控、日志、限流、校验、黑白名单判断等,然后转发到目标服务。
                  
否则每个服务都要重复写这些功能。
                 否则每个服务都要重复写这些功能。
                  对外唯一入口,用于接入微服务
                 
                
                 Zuul(停更)
                
                
                 Gateway
                 
               
                  三大核心概念
                  
                
                   路由-route
                  
                  
                   断言-predicate
                  
                  
                   过滤器-filter
                  
                 
                注册中心
                
               
                 Eureka
                 
                
                  AP
                 
                 
                  适用于服务实例数量不大的服务注册中心
                 
                 
                  服务上下线感知时间久
                 
                
                 Consul
                
               
                服务调用
                
               
                 REST
                
                
                 Feign/OpenFeign
                 
               
                  为什么Feign第一次调用耗时很长
                 
                
                负载均衡
                
               
                 Ribbon
                 
               
                  消费端(客户端)负载均衡
                 
                 
                  默认轮询
                 
                 
                  使用
                  
                 
                   RestTemplate + Ribbon
                  
                  
                   Feign + Ribbon
                  
                 
                  饥饿加载
                 
                
                断路器
                
               
                 Hystrix
                 
               
                  功能
                  
                
                   熔断:阻止故障的连锁反应导致雪崩
                  
                  
                   降级:快速失败
                  
                  
                   资源隔离
                   
                  
                    线程隔离
                    
                   
                     给每个command分配一个单独线程池
                    
                   
                    信号量隔离
                    
                  
                     获取信号量才可以执行,否则进入fallback。
                     
起到限流和防止雪崩的作用
                    
                   起到限流和防止雪崩的作用
                   监控和告警
                  
                 
                链路追踪
                
               
                 Spring Cloud Sleuth
                 
                
                  作为链路追踪的一种组件,只提供了日志采集,日志打印的功能,并没有可视化的UI界面
                 
                 
                  结合
                  
                
                   zipkin
                   
                 
                    提供了强大的日志追踪分析、可视化、服务依赖分析等相关功能
                   
                   
                    数据传输
                    
                   
                     默认http
                    
                    
                     推荐MQ
                     
                   
                      支持rabbit、kafka
                     
                    
                    持久化
                    
                  
                     数据存储在服务端
                    
                    
                     默认内存,可选择mysql、es(推荐)等
                    
                   
                 Skywalking
                
               
                消息总线
                
               
                 Spring Cloud Bus
                
               
                配置中心
                
              
                 Spring Cloud Config
                
               
               Alibaba
               
                
               
               
             
                包含netflix
               
               
                Sentinel
               
               
                Nacos
               
               
                RocketMQ
               
               
                Dubbo
               
               
                Seata
               
               
                Alibaba Cloud OSS
               
               
                Alibaba Cloud SchedulerX
               
               
                Alibaba Cloud SMS
               
              
             MySQL
             
            
              索引
              
             
               类型
               
              
                B+Tree
                
               
                 特点
                 
                
                  非叶子节点(key),叶子节点(key+value)
                  
                 
                   即使非叶子节点查到也必须继续查到叶子节点才可以结束
                  
                 
                  叶子节点key从左到右有序排放,节点之间通过双向链表连接
                 
                
                 B-Tree
                 
                
                  叶子和非叶子节点都会存放key和value
                  
                
                   查找到目标值则停止查找
                  
                 
                 innodb选择B+Tree
                 
                
                  一个节点16kb,非叶子节点不存放value,所以可以存放更多的key,
                  
那么树的分叉多,树高越低(层数),查询IO次数就越少
                  
                 那么树的分叉多,树高越低(层数),查询IO次数就越少
                   一般树高三到四层,前两层大概率在buffer pool中,所以IO次数更少
                  
                  
                   数据都在叶子节点,查询次数一样,所以查询更加稳定
                  
                 
                  叶子节点间通过指针相连,范围查找时只要找到一个叶子节点后,通过指针就能找到其它数据
                 
                
                 聚集索引
                 
&
辅助索引(非聚集、二级)
                 
                &
辅助索引(非聚集、二级)
                  键值的逻辑顺序 = 表数据行的物理存储顺序
                  
                 
                   聚集索引(主键+行数据),实际就是存储数据的
                  
                 
                  辅助索引(key + 主键)
                  
                
                   回表
                   
                 
                    因为辅助索引只存储主键,所以如果需要其他字段则需要回到聚集索引树去查找
                   
                   
                    覆盖索引
                    
                  
                     从辅助索引就可以得到需要查询的数据,无需回表(using index)
                    
                   
                 分裂
                
               
                自适应哈希索引
                
               
                 O(1),不支持范围查询
                
                
                 innodb对一些常用索引会建立一些key为索引,value为索引记录所在的页的位置,来加速查询,将其放入到adaptive hash index buffer pool中
                
                
                 
                  innodb自己控制的,我们不能建立
                 
                 。可以开启和关闭自适应hash索引:innodb_adaptive_hash_index,默认开启ON
                
               
                全文索引
                
              
                 myisam支持,innodb 1.2.x版本开始支持全文索引
                
                
                 语法
                 
               
                  select * from table where match(content) against ('xx' in natural language mode);
                 
                 
                  对非英文支持不好,可能搜索不到存在的记录,不建议使用
                 
                
               唯一索引 & 普通索引
               
              
                Change Buffer只对普通索引生效
               
              
               索引字段的选取
               
              
                离散度高的字段
               
               
                用于where、order、join的(on)、group by字段上创建索引
               
               
                联合索引
                
               
                 离散度高的字段放在前面
                
                
                 index(a,b,c)
                 
               
                  select * from t where a = xx and b = xx order by c;
                  
select * from t where a = xx order by b;
可以利用叶子结点的有序性
                 select * from t where a = xx order by b;
可以利用叶子结点的有序性
                  select * from t where a = xx order by c;
                  
不能利用有序性,需要filesort排序
                不能利用有序性,需要filesort排序
                过长的字段,建立前缀索引
                
               
                 过长导致树节点存储更少的数据从而树高越大
                 
                
               
                不建议用无序的值作为索引
                
               
                 插入容易导致非叶子节点频繁的分裂
                 
                
               
    
                频繁更新的字段
                
              
                 更新会维护索引,导致性能差,造成页分裂
                 
                
               
               失效
               
              
                违背最左匹配原则
                
               
                 like '%xx'
                
                
                 联合索引a_b,使用b作为查询条件
                
                
                 为什么要设计最左匹配原则
                 
                
               
                索引列有函数计算
               
               
                数字字符类型未使用 '',出现隐式转换(相当于使用CAST函数)
               
               
                反向查询
                
               
                 not like、not in等
                
               
                or 只要有一个字段没有索引就会全表扫描
               
               
                查询优化器选择不走索引
                
              
                 强制使用指定索引
                 
select * from t force index(a) where a = xx and b = xx
                
               select * from t force index(a) where a = xx and b = xx
               查看表索引
               
              
                show index from table
               
               
                关键参数
                
              
                 Non_unique
                 
                
                  非唯一索引,1代表非唯一
                 
                
                 Cardinality
                 
                
                  非常关键的参数,索引中唯一值的估计值,如果
                  
                   Cardinality/count(1)
                   
非常小(结果应该尽可能接近1)则可以考虑删除该索引
                 
                 非常小(结果应该尽可能接近1)则可以考虑删除该索引
                  优化器会根据该值决定使不使用该索引,维护该值需要开销,所以
                  
不是实时的是个大概值
                  
                不是实时的是个大概值
                   可以使用命令
                   
                    analyze table t
                   
                   更新该值
                  
                 
                 Sub_part
                 
                
                  是否列的部分被索引,如果是整个则为null,否则为截取长度值
                 
                
                 Null
                 
               
                  索引列是否包含null,YES表示包含
                 
                
               Multi-Range Read(MRR)
               
              
                适用于range,ref和eq_ref类型的查询,explain的extra信息Using MRR
               
               
                好处
                
               
                 减少磁盘随机io,将随机访问转为较为顺序的的数据访问
                 
                
                  非主键(
                  
                   条件必须有索引
                  
                  )的范围条件,开启MRR的话,会根据非主键索引查询到的主键id放入到
                  
缓存(read_rnd_buffer)中进行排序,然后用排序后的id去聚集索引访问数据
                 
                缓存(read_rnd_buffer)中进行排序,然后用排序后的id去聚集索引访问数据
                 减少缓冲池中页被替换的次数
                 
               
                  因为磁盘预读机制,离散读操作会频繁替换掉缓存池中的页,而主键顺序访问则减少这种情况
                 
                
                set optimizer_switch='
                
                 mrr=on,mrr_cost_based=off
                
                '
               
              
               
                Batched
               
               Key Access(BKA)
               
              
                Index Nested-Loop Join(NLJ)中会拿驱动表的数据一条一条的去被驱动表中查找,
                
BKA则是将数据放入到 join buffer 中然后批量去被驱动表查找,提升了性能
               
               BKA则是将数据放入到 join buffer 中然后批量去被驱动表查找,提升了性能
                BKA 依赖于 MRR
                
              
                 set optimizer_switch='mrr=on,mrr_cost_based=off,
                 
                  batched_key_access=on
                 
                 '
                
               
               Index Conditon Pushdown(ICP)
               
             
                支持range、ref、eq_ref类型的查询,只适用辅助索引
                
               
                 Using index condition
                
               
                将原来server层的过滤下推到存储引擎,减少了回表访问行的数量,从而减少I/O
               
               
                set optimizer_switch = 'index_condition_pushdown=on'
               
              
              事务 & 锁 & MVCC
              
             
               ACID
               
              
                Atomicity
                
               
                 mvcc(undolog)
                
               
                Consistency
               
               
                Isolation
                
               
                 锁 & mvcc
                 
                
               
                Durability
                
              
                 redolog
                
               
               并发带来问题
               
              
                脏读
               
               
                不可重复读
               
               
                幻读
               
              
               隔离级别
               
              
                级别
                
               
                 读未提交
                 
                
                  脏读
                 
                
                 读已提交(Oracle默认)
                 
                
                  解决脏读问题,但是不可重复读
                 
                
                 可重复读(MySQL默认)
                 
                
                  可重复读,但是会有幻读
                 
                 
                  innodb解决幻读
                 
                
                 串行化
                
               
                RC和RR的区别(选取)
                
              
                 RR通过锁区间相对于RC的行锁,增大了锁范围,所以并发度更低。
                 
                
                
                 RC的“半一致性”读可以增加update操作的并发性(对于不满足更新条件的记录,可以提前释放锁)
                
               
               隔离级别解决方案
               
             
                锁
                
                
               
                 锁住的是索引
                 
                
                  避免where条件没有索引,否则导致锁表
                 
                 
                  根据主键更新,则锁住主键
                 
                 
                  根据唯一索引更新,先锁唯一索引,然后锁主键
                 
                 
                  根据普通索引更新,先锁普通索引,然后锁记录对应的主键
                 
                
                 锁粒度
                 
                
                  全局锁(🔐库)
                 
                 
                  表锁
                  
                 
                   lock tables xxx read;
                   
lock tables xxx write;
unlock tables;
                 lock tables xxx write;
unlock tables;
                  行锁
                 
                
                 锁类别
                 
                
                  共享锁(S Lock)
                  
                 
                   select …… lock in share mode
                   
                  
                 
                  排它锁(X Lock)
                  
                 
                   1、增删改会自动加排它锁
                   
2、select ... for update
                  
                 2、select ... for update
                  意向锁
                  
                
                   作用:提升加
                   
                    表锁
                   
                   的效率
                  
                  
                   引擎自己维护,无法手动使用
                  
                  
                   种类
                   
                 
                    意向共享锁(IS)
                   
                   
                    意向排它锁(IX)
                    
                  
                     提升加锁效率
                    
                   
                 锁算法
                 
                
                  record lock
                  
                 
                   RC、RR都有
                  
                  
                   唯一性索引(唯一/主键)等值查询
                  
                 
                  gap lock
                  
                 
                   只在RR中存在,左开右开
                  
                  
                   未命中任何记录,则加间隙锁
                  
                 
                  next-key lock
                  
                  
                
                   只在RR中存在,左开右闭
                  
                  
                   record lock + gap lock
                  
                 
                 死锁
                 
               
                  原因
                  
                 
                   多个事务因为夺取锁资源而造成的一个相互等待。比如事务1获得A资源,接下来要获取B资源。而事务2先获得了B,
                   
再获取A,事务1等待事务2释放B,而事务2等待事务1释放A,就造成了一个互相等待,从而死锁
                  
                 再获取A,事务1等待事务2释放B,而事务2等待事务1释放A,就造成了一个互相等待,从而死锁
                  SHOW ENGINE INNODB STATUS
                 
                 
                  应对策略
                  
                 
                   超时机制
                   
                  
                    被动方式,锁等待达到设定时间,则回滚其中小事务(更新,插入,删除行数少的事务,也就是undo量小的事务)
                   
                   
                    innodb_lock_wait_timeout
                    
                  
                     默认50s
                     
                   
                      设置过大业务不能容忍,过小造成误伤
                     
                    
                   主动死锁检测(默认)
                   
                 
                    主动检测死锁,每次事务请求发生等待时候会判断是否存在
                    
                     回路
                    
                    ,有则回滚小事务
                   
                   
                    innodb_deadlock_detect
                    
                  
                     默认ON
                     
                    
                      并发高的情况,消耗很多cpu资源
                     
                    
                     设置为OFF则关闭主动检测,转为超时机制
                    
                   
                  避免或减少
                  
                
                   操作不同的表或者同表不同行使用相同的顺序
                  
                  
                   隔离级别改为RC
                  
                  
                   将大事务改成小事务,减少事务的持续时间减少锁冲突概率
                  
                  
                   尽快提交事务,减少冲突
                   
                  
                    把耗时操作放在方法前面
                   
                  
                   SHOW ENGINE INNODB STATUS查看死锁原因,从而改进程序
                  
                  
                   打开innodb_print_all_deadLocks开关,会记录所有死锁日志。记录在错误日志里。用完记得关闭
                  
                 
                mvcc
                
                
              
                 Multi Version Concurrent Control,多版本并发控制。
                 
只在RC和RR下生效,用于事务隔离和提升并发访问性能
                
                只在RC和RR下生效,用于事务隔离和提升并发访问性能
                 视频讲解
                 
                
                
                 原理(版本链+视图)
                 
                 
               
                  版本链(unlog链)
                  
                  
                 
                   是一条链表,链接的是每条数据曾经的修改记录
                  
                  
                   行数据隐含字段
                   
                   
                 
                    trx_id:当前事务id
                    
                   
                   
                    rol_pointer:回滚指针,记录上个版本
                    
                   
                   
                    row_id
                    
                   
                   
                    delete_flag
                   
                  
                  视图(ReadView)
                  
                  
                 
                   包含信息
                   
                   
                  
                    creator_trx_id:创建视图时的事务id
                    
                   
                   
                    m_ids:创建该视图时,活跃且未提交的事务id集合
                    
                   
                   
                    min_trx_id:m_ids中最小的事务id
                    
                   
                   
                    max_trx_id:创建视图时应该给下个事务id
                    
                   
                  
                   RC
                   
                   
                  
                    每个select语句执行前都会创建一个新的视图
                    
                    
                  
                     所以会读取到其他事务的更新
                    
                    
                     第二次读会开启新视图,而之前在旧的视图的m_ids里的事务
                     
因为提交了,则不会在新视图的m_ids中,则可以读取到,所以
是不可重复读
                    
                   因为提交了,则不会在新视图的m_ids中,则可以读取到,所以
是不可重复读
                   RR
                   
                   
                 
                    事务开始时创建一个视图,提交前都是用这个视图
                    
                    
                  
                     但是事务中更新语句,或者加锁的select也会读取到最新的数据
                    
    
                    
                     第二次读不会创建新视图,所以已经提交的事务id依然还在m_ids中,
                     
所以认为没有提交,读取不到
                    
                   所以认为没有提交,读取不到
                  查询流程
                  
                  
                   
判
断
条
件
规
则
                  
                 
                
                   根据当前视图中的信息去版本链中遍历比较,符合条件则返回记录
                   
                  
                  判
断
条
件
规
则
              SQL
              
             
               执行流程
               
              
                -client
                
->连接器
->缓存
->分析器
->优化器
->执行器
->存储引擎
->client
               ->连接器
->缓存
->分析器
->优化器
->执行器
->存储引擎
->client
                 连接器
                 
                
                  管理连接、权限验证
                 
                
                 缓存
                 
                
                  命中缓存直接返回,默认关闭
                 
                 
                  8.0版本取消
                 
                
                 解析器
                 
                
                  拆分、检查语法是否正确
                 
                
                 预处理器
                 
                
                  检查字段、表是否存在
                 
                
                 优化器
                 
                
                  执行计划生成,索引选择
                 
                
                 执行器
                 
                
                  操作引擎返回结果
                 
                
                 存储引擎
                 
               
                  存储数据,提供读写接口
                 
                
                更新语句执行流程
                
                 
                
               
              
               慢sql发现
               
               
              
                开启慢查询日志
                
               
               
                mybatis拦截statementHandler,记录sql执行耗时,大于阈值报警或记录
                
               
              
               explain查看执行计划
               
              
                id
                
               
                 
                  id相同,执行顺序从上往下,
                 
                 
                  id不同,执行顺序从下往上
                 
                 
                
               
                select_type
               
               
                talbe
               
               
                type
                
               
                 system
                 
                
                  表中只有一条数据
                 
                
                 const
                 
                
                  通过索引一次找到,一般就是主键查询
                 
                
                 eq_ref
                 
                
                  唯一性索引扫描,表中只有一条数据匹配
                 
                
                 ref
                 
                
                  非唯一性索引扫描,返回匹配某个单独值的所有行
                 
                
                 index_merge
                 
                
                  index_merge是MySQL 5.1后引入的一项索引合并优化技术,它允许对同一个表同时使用多个
                  
索引进行查询,并对多个索引的查询结果进行合并(取交集(intersect)、并集(union)等)后返回
                 
                索引进行查询,并对多个索引的查询结果进行合并(取交集(intersect)、并集(union)等)后返回
                 range
                 
                
                  范围查询
                 
                
                 index
                 
                
                  只遍历树,通常比all快
                 
                
                 all
                 
               
                  全表扫描
                 
                
                possible_keys
               
               
                key
               
               
                key_len
                
               
                 索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好。
                
               
                ref
               
               
                rows
                
               
                 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
                
               
                filtered
                
               
                 表示存储引擎返回的数据在经过过滤后,剩下满足条件的记录数量的比例(%)
                
               
                extra
                
              
                 using index
                
                
                 using index condition
                
                
                 using where
                
                
                 using filesort
                
                
                 using join buffer
                
                
                 using temporary
                
               
               关键字
               
              
                order by
                
               
                 sort_buffer
                 
                
                  一块用来排序的内存,
                  
                   每个查询线程都分配这么一块内存
                  
                  ,会根据sql查询出来的数据放入到内存中进行排序然后返回给客户端
                 
                 
                  当explain的extra信息是using filesort表示使用了内存排序
                 
                 
                  参数
                  
                
                   sort_buffer_size(默认256kb)
                   
                  
                    查询数据 <= sort_buffer:内存中排序
                    
                   
                     快排算法
                    
                   
                    查询数据 > sort_buffer:借助文件排序,导致io,性能变差
                    
                  
                     归并排序算法
                    
                   
                   max_length_for_sort_data
                   
                 
                    默认1024字节,代表select后字段的总长度,当没有大于该值则会使用全字段排序,否则则会自动选择rowid算法
                   
                   
                    全字段排序
                    
                   
                     内存保存select后所有字段,无需回表
                    
                   
                    rowid排序
                    
                  
                     内存只放入主键id和用来排序的字段,排序后还要拿到id进行
                     
                      回表
                     
                     查询
                    
                   
                 优化
                 
               
                  配置优化
                  
                 
                   适当调大sort_buffer的size
                  
                  
                   max_length_for_sort_data调大一点,防止rowid排序,
                   
不过1kb基本已经满足大部分情况了
                  
                 不过1kb基本已经满足大部分情况了
                  sql方面优化
                  
                
                   利用B+TREE叶子节点有序性/联合索引
                   
                  
                    如果有where条件,则筛选字段和排序字段建立联合索引
                   
                   
                    没有筛选的话,排序字段建立索引
                   
                   
                    如果where是in等,还是会filesort。可以分别查询然后内存处理排序
                   
                  
                   尽量避免select *
                   
                 
                    防止字段长度大于max_length_for_sort_data导致rowid排序
                   
                  
                count
                
               
                 MyIsam会维护一个计数值,但是对于带有where条件的count查询就没用了
                
                
                 Innodb每次count查询都要实际计算
                 
                
                  为什么不跟MyIsam一样
                  
                
                   因为Innodb是有事务的,因为事务的隔离性以及mvcc机制,不同事务count查询结果可能不一样
                  
                 
                 count(*)
                 
                
                  不取值,直接累加
                 
                
                 count(1)
                 
                
                  遍历每一行,并把1放进去,不存在null值情况
                 
                
                 count(id)
                
                
                 count(字段)
                 
               
                  如果字段可以为null,且有null值的话,会跳过不计算
                 
                
                in & exists
                
               
                 主结果集大,子结果集小
                 
                
                  IN性能更高
                  
                
                   SELECT * FROM `user` WHERE `user`.id IN (SELECT `order`.user_id FROM`order`)
                  
                  
                   先执行子查询,再执行主查询
                  
                 
                 主结果集小,子结果集大
                 
               
                  EXISTS性能更高
                  
                
                   SELECT `user`.* FROM `user` WHERE EXISTS (SELECT 1 FROM `order` WHERE `user`.id = `order`.user_id)
                  
                  
                   先执行主查询再执行子查询
                   
                 
                    主查询出来的记录一条条的执行子查询里的sql判断结果true|false
                   
                  
                join
                
               
                 type
                 
                
                  join
                  
                 
                   笛卡尔积
                  
                 
                  inner join
                  
                 
                   交集
                  
                 
                  left join
                  
                 
                   返回左表所有行,右表关联不上用null表示
                  
                 
                  right join
                  
                 
                   返回右表所有行,左表关联不上用null表示
                  
                 
                  union
                  
                
                   全连接
                  
                 
                 算法
                 
                
                  Index Nested-Loop Join(NLJ)
                  
                 
                   可以用到被驱动表的索引
                  
                 
                  Block Nested-Loop Join(BNL)
                  
                
                   用不到被驱动表的索引,将驱动表数据放入到join_buffer中,然后将被驱动表的每一行取出来和
                   
join_buffer的数据对比,满足条件的作为结果集返回
                   
                  join_buffer的数据对比,满足条件的作为结果集返回
                    explain的extra信息中using join buffer(Bolck Nested Loop)
                   
                  
                   join_buffer
                   
                 
                    join_buffer_size 默认256k
                   
                   
                    当size不够大不能完全容下驱动表数据时候,则会放部分驱动表数据,然后全表扫描被驱动表,
                    
跟buffer中数据对比。然后清空buffer,放入剩下的驱动表数据,再全表扫描一次被驱动表
                    
                  跟buffer中数据对比。然后清空buffer,放入剩下的驱动表数据,再全表扫描一次被驱动表
                     会造成多次扫描被驱动表,所以可以调大join_buffer_size大小
                    
                   
                 优化
                 
               
                  尽量使用到NLJ
                 
                 
                  小表作为驱动表
                  
                 
                   两个表按照各自条件过滤后,参数join个数据量小的表就是小表
                  
                  
                   straight_join:强制指定驱动表
                  
                 
                  调大join_buffer_size
                 
                
                union
                
               
                 union
                 
                
                  无重复
                 
                 
                  代替or
                 
                
                 union all
                 
                
                  可重复
                 
                
                 tips:内部select后列必须相同
                
               
                where & having
                
               
                 where
                 
                
                  从数据库的字段进行筛选
                 
                
                 having
                 
               
                  从select后面的字段(可以是函数计算,avg,max等)进行筛选
                 
    
                
                on duplicate key update
                
               
                 存在唯一索引情况下,插入或者更新
                
                
                 update会造成自增主键不连续
                
               
                case when ... then
               
              
               字段类型
               
             
                char
                
               
                 长度固定,如果插入长度小于定义长度则用空格填充,检索值的时候删除空格
                
                
                 最多存放255个字符
                
               
                varchar
                
               
                 长度可变,插入小于定义长度时,按实际长度存储
                
                
                 最多存放65532个字符
                
               
                blob
                
               
                 存储二进制数据
                
               
                date
                
               
                 存储日期,不存储时间,如YYYY-MM-DD
                
               
                time
                
               
                 不存储日期,存储时间
                
               
                datetime
                
               
                 YYYY-MM-DD HH:MM:SS
                
                
                 存储范围:
                 
1000-01-01 00:00:00.000000~9999-12-31 23:59:59.999999
                
                1000-01-01 00:00:00.000000~9999-12-31 23:59:59.999999
                 存什么就是什么,与时区无关
                
               
                timestamp
                
              
                 YYYY-MM-DD HH:MM:SS
                
                
                 存储范围:
                 
1970-01-01 00:00:01.000000~ 2038 -01-19 03:14:07.999999
                
                1970-01-01 00:00:01.000000~ 2038 -01-19 03:14:07.999999
                 与时区有关,会随着时区变化而变化
                
               
              主从复制
              
               
               
              
             
                步骤
                
                
              
                 从库通过io线程去主库拉取binlog并写入relaylog
                 
                
                
                 sql线程读取relaylog并执行sql
                 
                 
               
                  单线程,可以通过配置改为多线程执行
                  
                 
                
               读写分离实现
               
               
              
                代理方式
                
                
               
                 Mycat
                 
                
                
                 MySQL Router(官方)
                
                
                 MySQL Proxy
                
               
                组件方式
                
                
              
                 sharding-jdbc
                
               
               主从延迟
               
             
                查看延迟
                
               
                 备库执行show slave status命令,查看seconds_behind_master的值,精度为秒
                
               
                原因&解决
                
               
                 从库配置比主库差,导致数据备份较慢
                 
                 
                
                  提升从库的硬件配置,最好大于等于主库配置
                  
                 
                
                 备库查询压力大,比如查询消耗大量CPU资源,从而影响同步速度
                 
                
                  多接几个从库,分担读的压力
                 
                
                 大事务&慢sql比较耗时,从库执行也耗时,导致延迟
                 
                
                  尽量避免大事务,优化慢sql
                 
                
                 SQL线程单线程写入慢
                 
                 
                
                  MySQL 5.7支持并行复制,也就是sql线程由单线程变为多线程
                 
                
                 异步复制
                 
                 
               
                  5.5后增加半同步复制
                 
                
                写入立马读取场景(强一致性、金融),如何解决
                
              
                 强制主库
                
                
                 适当休眠后再查
                 
                
               
              架构
              
             
               连接池组件
              
              
               管理服务和工具组件
              
              
               SQL接口组件
              
              
               查询分析器组件
              
              
               优化器组件
              
              
               存储引擎
               
              
                innodb
                
               
                 事务、行锁、外键、mvcc、解决幻读、有主键
                
                
                 B+Tree
                 
               
                  叶子节点存放是行记录
                 
                
                myisam
                
               
                 表锁、不支持事务、可以没有主键
                
                
                 B+Tree
                 
               
                  叶子节点存放的是行记录所在磁盘地址,根据地址再去查询数据
                 
                 
                  索引(存放键值和行记录磁盘地址)和数据分开存放,分别是MYI和MYD文件
                 
                
                archive
                
               
                 只支持insert和select。目标是高速插入和压缩功能(压缩比1:10),适合日志等存储归档数据
                
               
                memory
                
               
                 数据存于内存,使用哈希索引
                
               
                csv
               
               
                ......
               
              
               缓存
               
              
                Buffer pool
                
               
                 磁盘读取是按页读取(预读机制),默认16K。
                 
当读取一条数据时,会把其附近的数据也一起预读到缓存池。
下次读取,就直接从缓存读取,无需从磁盘读取,提升查询性能
                
                当读取一条数据时,会把其附近的数据也一起预读到缓存池。
下次读取,就直接从缓存读取,无需从磁盘读取,提升查询性能
                 innodb_buffer_pool_size
                
                
                 LRU
                 
               
                  传统LRU
                  
                 
                   新数据或者访问页中存在的数据,都会移动到队头,如果空间不够,则移除队尾的数据
                  
                  
                   存在问题
                   
                 
                    预读失效
                    
                   
                     可能预读的数据被没有被使用,所以预读失效了
                    
                   
                    缓冲池污染
                    
                  
                     比如一个查询扫描大量树数据,把缓冲池中的大量页都替换出去了,导致大量热数据被移出
                    
                   
                  MySQL基于传统的改造
                  
                
                   划分成新生代&老生代
                   
                  
                    老生代的头连接者新生代的尾部。新页先放入到老生代,只有
                    
                     当读取到了
                    
                    ,才移动到新生代,
                    
这样如果没有被读到,则提前被淘汰了,解决预读失效问题
                   
                   这样如果没有被读到,则提前被淘汰了,解决预读失效问题
                    innodb_old_blocks_pct,老生代占lru链的百分比,默认37
                   
                  
                   设置老生代停留时间
                   
                 
                    数据被访问了,且在老生代停留时间达到设定时间,才会移动到新生代,解决缓存污染问题
                   
                   
                    innodb_old_blocks_time.老生代停留时间,单位ms,默认1000
                   
                  
                Change buffer
                
               
                 如果没有change buffer
                 
                
                  修改的数据在缓存中,直接修改缓存中的数据。否则从磁盘读取,写入缓存再修改
                 
                 
                  有change buffer
                  
                
                   先把变更记录到change buffer中,等未来读取到该数据再并入缓存池中
                   
                 
                    减少了一次磁盘随机操作
                   
                  
                 作用
                 
                
                  如果buffer pool不存在要修改的数据,则在changeBuffer中记录变更内容,
                  
然后写redolog(这时候内存中和磁盘数据不一致,叫脏页),从而提升了速度。在
                  
                  
                然后写redolog(这时候内存中和磁盘数据不一致,叫脏页),从而提升了速度。在
- 数据库空闲
 - 缓存池不够用
 - 数据库正常关闭
 - redo log写满的时候
 
                   第二个和第四个可能会影响到正在执行的sql执行速度,
                   
也就是同一个sql有时很快有时很慢的原因
                  
                 也就是同一个sql有时很快有时很慢的原因
                 适用
                 
                
                  非唯一普通索引
                  
                 
                   如果是唯一索引,必须要到磁盘校验唯一性,所以,缓存就没意义了
                  
                 
                  读多写少或者不是写后立即读取
                  
                
                   立即读取会访问磁盘,change buffer的初衷就是不访问磁盘
                  
                 
                 innodb_change_buffer_max_size
                 
               
                  占用整个缓存池百分比,最大50,默认25
                 
                
                Log buffer
                
               
                 redo log buffer
                
               
                Adaptive hash index buffer
               
              
               InnoDB后台线程
               
              
                多线程模型
               
               
                线程类别
                
              
                 master thread
                 
                
                  负责将缓冲池中的数据异步刷新到磁盘,合并change buffer等
                 
                
                 IO thread
                 
                
                  负责处理IO请求
                  
                 
                   insert buffer thread(1个)
                  
                  
                   log IO thread(4个)
                  
                  
                   read IO thread(4个)
                  
                  
                   write IO thread(4个)
                  
                 
                  show eninge innodb status可以看到上面线程,线程数量可以调整
                 
                
                 purge thread
                 
                
                  负责清理undo log
                 
                
                 page cleaner thread
                 
                
                  负责脏页的刷新
                 
                
                 ......
                
               
               文件
               
             
                参数文件
                
               
                 my.cnf
                
               
                socket文件
                
               
                 使用unix套接字方式进行连接需要的文件
                
               
                pid文件
                
               
                 mysql实例进程PID文件
                
               
                表结构文件
               
               
                存储引擎文件
                
               
                 包括各引擎相关的数据,索引等文件
                
               
                日志文件
                
              
                 binlog
                 
                
                  默认开启,二进制格式的日志,所属server层、逻辑日志,非幂等
                 
                 
                  作用:主从复制 & 数据恢复
                  
                 
                 
                  格式
                  
                 
                   statement(不合理的设置)
                   
                  
                    记录的是sql
                   
                   
                    如果是带有
                    
                     limit,now()等
                    
                    ,从库执行的话可能会造成不一致
                   
                  
                   row
                   
                  
                    记录具体修改的信息
                    
                   
                   
                    缺点:占用更多存储空间,耗费io资源,影响执行速度
                   
                  
                   mixed
                   
                 
                    MySQL 自己会判断这条 SQL 语句是否可能引起主备不一致,
                    
如果有可能,就用 row 格式,否则就用 statement 格式
                   
    
                   如果有可能,就用 row 格式,否则就用 statement 格式
                    8.0 以上版本默认mixed格式
                   
                  
                  刷盘策略
                  
                   
                 
                - 
                     
                      sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync,由系统决定fsync
                     
                     
 - sync_binlog=1 的时候,表示每次提交事务都会执行 fsync
 - sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync
 
                 redolog
                 
                
                  innodb特有、物理日志,幂等的,记录的是对每个页的做了哪些改动。
                  
事务进行中不断写入,是循环写入,空间固定会用完,然后覆盖
                 
                 事务进行中不断写入,是循环写入,空间固定会用完,然后覆盖
                  作用:崩溃恢复
                  
                 
                 
                  流程
                  
                 
                   将修改结果记录到redolog,这里是mysql的日志缓存中
                  
                  
                   刷盘
                   
                 
                    触发时机
                    
                  
                     master thread每秒触发
                    
                    
                     提交事务,根据配置的策略来决定刷盘策略
                     
innodb_flush_log_at_trx_commit
                     
                   innodb_flush_log_at_trx_commit
                      0(性能好)
                      
                     
                       提交不写入磁盘,写入磁盘由mater thread去完成,每1s
                       
执行一次fsync操作,所以mysql宕机会最多丢失一秒的数据
                      
                     执行一次fsync操作,所以mysql宕机会最多丢失一秒的数据
                      1 (默认)
                      
                     
                       每次事务提交,都会fsync,写入磁盘,不会丢失数据
                      
                     
                      2(折中)
                      
                    
                       提交时写入redo log到操作系统的缓存中,由操作系统后台
                       
每秒fsync,mysql宕机不会丢失数据,操作系统宕机则会丢失数据
                      
                      每秒fsync,mysql宕机不会丢失数据,操作系统宕机则会丢失数据
                       阿里云配置的是2,最佳实践
                      
                     
                  undolog
                  
                
                   不是文件,存放在数据库内部一个undo段(undo segment)中
                  
                 
                 慢查询日志
                 
                
                  默认关闭
                 
                 
                  开启
                  
                  
                 
                   非永久
                   
                   
                  
                    SET GLOBAL slow_query_log = 1
                   
                  
                   永久
                   
                   
                  
                    my.cnf文件,设置slow_query_log=ON
                    
                   
                  
                   设置阈值
                   
                   
                 
                    long_query_time,单位s,默认10。
                    
大于该值的sql会被记录
                   
                  大于该值的sql会被记录
                  消耗性能,使用完建议关闭
                  
                 
                
                 查询日志
                 
                
                  记录MySQL的请求信息
                 
                
                 错误日志
                 
               
                  记录错误信息,比如启动失败等
                 
                
              其它
              
            
               三范式
               
              
                1NF
                
               
                 字段都是单一属性,不可再拆分
                
               
                2NF
                
               
                 1NF的基础上,要求表中的每个非主键列完全依赖于主键列,而不是依赖于其他非主键列
                 
                
               
                3NF
                
              
                 2NF基础上,要求表中的每个非主键列之间不应该存在传递依赖关系
                
               
               连接池
               
              
                Druid
                
               
                 支持sql级监控,支持扩展,防止SQL注入等功能
                
               
                HikariCP
                
              
                 SpringBoot2默认
                
                
                 获取和关闭连接性能更好
                
               
               CPU飙升排查
               
              
                show processlist
               
              
               大表添加字段
              
             
             Redis
             
            
              数据类型
              
             
               string
               
              
                bitmap
                
              
                 签到、登录状态等
                
               
               hash
              
              
               list
              
              
               set
              
              
               zset
              
             
              应用场景
              
               
              
              
             
               商品筛选
              
              
               限流
              
              
               验证码
              
             
              底层数据结构
              
             
               dictEntry
               
              
                Redis里每一个键值对都是一个dictEntry,里面指向了key和value
               
               
                key<SDS>
                
               
                 SDS
                 
                  
                 
                 
               
                  Redis使用c写的,c中字符串是用char[]数组表示,redis并没有使用这个,而是自己定义了SDS
                 
                 
                  char[]缺点
                  
                 
                   必须先给目标变量分配足够内存,否则会内存溢出
                  
                  
                   获取字符串长度必须遍历数组,时间复杂度为O(n)
                  
                  
                   长度变更会对字符串数组做内存重分配
                  
                  
                   通过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,
                   
因此不能保 存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全
                  
                 因此不能保 存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全
                  SDS优点
                  
                
                   按需扩容,无需担心内存溢出
                  
                  
                   记录的字符串长度len,无需遍历,复杂度为o(1)
                  
                  
                   空间预分配和惰性空间释放,防止多次重分配内存
                  
                  
                   判断是否结束的标志是 len 属性,二进制安全
                  
                 
                value<RedisObject>
                
              
                 redisObject
                 
               
                  数据类型
                  
                 
                   查看key的数据类型:type key
                  
                 
                  内部编码
                  
                 
                   查看编码命令:object encoding key
                  
                  
                   五大类型中,每种类型可能存在着不同的编码
                  
                  
                   不同编码的目的
                   
                 
                    为了在节约内存和提高性能之间做平衡
                    
                  
                     当数据量小的时候,会采用紧凑(性能偏低)的数据结构
                    
                    
                     当数据量达到一定阈值的时候,会从紧凑型的结构转成高效率的数据结构
                    
                   
                  LRU:最近一次被访问时间
                  
                 
                   查看key的空闲时间:object idletime key
                  
                 
                  引用计数
                  
                 
                   查看key被引用次数:object refcount key
                  
                 
                  实际对象指针
                  
                
                   当值是 string 类型,并且编码是 int 时,保存的就是这个整数值,而不是指针
                   
                  
                 
               string
               
              
                编码
                
              
                 int
                 
                
                  字符串是整数值,用int编码。redisobject的ptr保存的就是实际值,而不是指针
                 
                 
                  好处
                  
                
                   节省了sds和指针的内存空间
                  
                 
                 embstr
                 
                
                  字符串的长度小于等于 44 采用 embstr 编码,真实数据由sds存储
                 
                 
                  好处
                  
                
                   redisObject 跟 sds 的内存是挨在一起,从而分配和释放内存都只要一次。而raw是分开的,需要两次
                  
                 
                 raw
                 
               
                  字符串长度超过 44 用raw编码,真实数据由sds存储
                 
                
               list
               
              
                quicklist
                
              
                 双向链表,链表中每个元素(node)是ziplist
                
                
                 双向链表浪费内存,通过ziplist减少链表节点数量。同时ziplist插入和删除复杂度高,所以通过参数控制ziplist的长度
                
               
               hash
               
              
                ziplist
                
                 
                
                
               
                 节省内存空间,适合少键值对个数,kv体积小的场景
                
                
                 转化为hastable条件
                 
               
                  key或value大于64byte(一个字母为1byte)
                 
                 
                  键值对个数大于512个
                 
                
                hashtable
                
              
                 数组+链表
                
               
               set
               
              
                intset
                
               
                 元素都是整数类型且个数<=512则使用inset存储
                 
                
               
                hashtable
                
              
                 元素有不是整数类型或者个数超过512个
                 
               
                  key是元素的值,value为null
                 
                
               zset
               
             
                ziplist
                
               
                 节省内存空间
                 
                
                
                 元素个数小于128并且每个元素长度小于64字节
                
               
                
                 
                  skiplist
                 
                
                
                 
                 
                
              
                  查询值使用字典
                  
                 
                   字典key存储的是元素,value存储的是score
                  
                 
                  根据score查询使用的skiplist
                 
                
                 为什么不用红黑树
                 
                
                  增删查复杂度跟跳表是一样的,但是范围查询跳表更好
                 
                
                 插入元素是否建立索引判断逻辑
                
               
              为什么快
              
             
               纯内存
              
              
               请求单线程
               
              
                网络请求模块使用了单线程,其他模块是多线程
               
               
                单线程为啥快
                
              
                 Redis的瓶颈不是计算,而是IO能力
                
                
                 避免创建和销毁线程带来的开销
                
                
                 避免了多线程上下文切换
                
                
                 避免线程竞争问题,比如加锁,死锁等
                
               
               I/O多路复用
               
                
               
               
              
                linux: epoll
               
              
               专门设计的数据结构
               
              
                skiplist、SDS.....
               
              
               C语言,不要经过JVM翻译
              
             
              持久化
              
             
               RDB(快照模式-默认)
               
              
                保存当前数据
               
               
                触发
                
               
                 手动
                 
                
                  save
                  
                 
                   整个过程都会阻塞(同步)
                   
                  
    
                 
                  bgsave
                  
                
                   fork一个子进程(阻塞),持久化操作由子进程执行(非阻塞)(异步)
                   
                  
                 
                 自动
                 
               
                  redis.conf配置规则
                  
                 
                   save <seconds> <changes>
                  
                  
                   关闭:save "",并且注释掉其他 save m n
                  
                 
                  主从复制,生成rdb文件发送给从
                 
                 
                  shutdown
                 
                
                lastsave指令
               
               
                数据恢复快,但是容易丢数据
               
              
               AOF
               
              
                记录每次写命令
               
               
                开启aof:appendonly yes
               
               
                流程
                
               
                 1、写命令都会追加到aof_buf(缓冲区)中
                
                
                 2、根据策略将缓存刷盘到磁盘
                 
                
                  策略参数:append
                  
                   
                    fsync
                   
                  
                  
                
                   no
                   
                  
                    不执行fsync(),由操作系统保证数据同步到磁盘,速度最快,最不安全
                   
                  
                   always
                   
                  
                    每次写入都fsync(),速度慢不丢失数据(最多丢失一条命令)
                   
                  
                   everysec(默认)
                   
                 
                    每秒执行fsync(),可能丢失1s(可能大于1s)的数据
                   
                   
                    AOF阻塞
                    
                  
                     每次fsync会记录完成时间T,主线程会对比当前时间和T,如果发现距离T小于等于2s则直接返回,否则阻塞主线程。
                     
fsync执行比较慢的情况下,则会丢失2s的数据
                    
                    fsync执行比较慢的情况下,则会丢失2s的数据
                     定位
                     
                   
                      reids日志
                     
                     
                      info persistence
                      
                     
                       参数:aof_delayed_fsync,fsync延迟超过2s则计数+1
                      
                     
                      监控服务器磁盘io高的进程
                      
                    
                       如工具iotop
                      
                     
                 3、随着aof文件不断增大,会触发重写机制
                 
               
                  当aof文件越来越大,达到阈值则会将文件压缩,只保留可以恢复数据的最小指令集
                 
                 
                  触发
                  
                 
                   手动
                   
                  
                    bgrewriteaof
                   
                  
                   自动
                   
                 
                    auto-aof-rewrite-min-size
                    
                   
                     运行AOF重写时文件最小体积,默认 为64MB
                    
                   
                    auto-aof-rewrite-percentage
                    
                   
                     代表当前AOF文件空间 (aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值
                    
                   
                    触发阈值
                    
                  
                     aof_current_size>
                     
                      auto-aof-rewrite-min- size
                     
                     
&&
aof_current_size-aof_base_size)/aof_base_size>= auto-aof-rewrite- percentage
                    
                   &&
aof_current_size-aof_base_size)/aof_base_size>= auto-aof-rewrite- percentage
                  注意
                  
                
                   也会fork子进程,fork过程中会阻塞
                  
                  
                   不是在原文件上压缩,而是读取服务器现有键值对,生成新的文件
                   
                    替换
                   
                   原文件
                  
                  
                   no-appendfsync-on-rewrite
                   
                 
                    no(默认)
                    
                   
                     重写过程中,(appendfsync为always或者everysec)会阻塞新的写入
                    
                   
                    yes
                    
                  
                     重写过程不会阻塞新的写入,等到重写完成再这些新的写入指令写入到aof文件
                    
                    
                     如果有延迟问题可以设置为yes,但可能会丢失数据
                    
                   
                恢复慢,但是基本不会丢数据
               
               
                同时开启
                
                
              
                 重启选择aof恢复数据
                 
                
                
                 坑
                 
                 
               
                  第一次开启aof,重启。因为aof为空文件,所以会丢失所有数据
                 
                 
                  解决
                  
                  
                
                   先备份rdb文件,同时动态开启aof,再手动触发生成aof文件。
                   
再修改conf文件,开启aof,修改aof文件名(可不修改)
                  
                 再修改conf文件,开启aof,修改aof文件名(可不修改)
               混合持久化
               
             
                Redis 4.0 版本的混合持久化功能默认是关闭。
                
aof-use-rdb-preamble 为 yes 开启此功能
               
               aof-use-rdb-preamble 为 yes 开启此功能
                
                 重写
                
                aof文件时候会同时生成rdb文件,然后将rdb作为aof文件一部分。然后就追加新的写入命令
               
               
                数据恢复时候先加载rdb,然后加载aof的命令。这样结合了两者的优点
               
              
              内存
              
             
               maxmemory
              
              
               info memory
               
              
                关键信息
                
              
                 used_memory
                
                
                 used_memory_rss
                 
                
                
                 mem_fragmentation_ratio
                
                
                 used_memory_peak
                
               
               内存回收
               
             
                过期键删除策略
                
               
                 惰性删除
                 
                
                  key 在过期之后,没有立即删除,而是在读写 key 的时候,才对过期的 key 进行删除
                 
                 
                  缺点:key一直没有被访问的话就一直不会被删除
                 
                
                 定期删除
                 
               
                  expires名字的字典,key保存的key名称,value保存的是过期的时间。定时从字典中
                  
获取若干key,判断是否过期,过期则删除
                 
                获取若干key,判断是否过期,过期则删除
                8个淘汰策略(内存达到maxmemory上限)
                
              
                 noeviction(default)
                 
                
                  拒绝写入,抛异常
                 
                
                 设置了过期时间
                 
                
                  volatile-lru
                 
                 
                  volatile-lfu
                 
                 
                  volatile-random
                 
                 
                  volatile-ttl
                 
                
                 所有的key
                 
               
                  allkeys-lru
                 
                 
                  allkeys-lfu
                 
                 
                  allkeys-random
                 
                
              集群
              
             
               作用
               
              
                性能、扩展、高可用
               
              
               模式
               
             
                主从Replication
                
               
                 配置
                 
                
                  开启
                  
                 
                   
                    从服务redis.conf文件配置:
                   
                   
                    
                     slaveof ip port
                    
                   
                   
                  
                  
                   
                    从服务
                   
                   
                    启动命令
                   
                   
                    加上slaveof ip port
                   
                   
                  
                 
                  关闭:slaveof no one
                 
                
                 数据同步
                 
                
                  全量复制
                  
                 
                   slave连接到master会发送psync命令,maseter收到后执行bgsave生产rdb文件,同时将此
                   
后的写命令写入到缓冲区,将rdb发送给slave,同时将期间新的写命令发送给slave
                  
                  后的写命令写入到缓冲区,将rdb发送给slave,同时将期间新的写命令发送给slave
                   slave收到rdb文件会删除本地的文件,然后进行新rdb文件的内存载入,完成后会执行master发送的写命令
                  
                 
                  增量复制
                  
                
                   slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器
                  
                 
                 缺点
                 
               
                  主宕机,不能自动切换,需要人工介入去修改主节点,同时还要更改客户端配置
                 
                
                哨兵Sentinel
                
                 
                 
                
               
                  
                   故障发现
                  
                  和
                  
                   自动故障转移
                  
                  的高可用方案
                 
                 
                  客户端连接到哨兵节点集合,哨兵负责转发
                 
                
                 工作原理
                 
               
                  哨兵间建立连接
                  
                 
                   利用redis的发布订阅模式
                   
                  
                 
                  哨兵同从库建立连接
                  
                 
                   向主库发送info命令,返回从库列表信息,然后可以同从库建立连接
                  
                 
                  故障发现
                  
                 
                   哨兵会监控主、从和其他哨兵是否正常(ping/s)。
                   
当发现master在超过时间(down-after-milliseconds,默认30s)没有响应,
则标记为 主观下线
                  
                  当发现master在超过时间(down-after-milliseconds,默认30s)没有响应,
则标记为 主观下线
                   当有达到一定数量(配置指定quorum)哨兵标记主观下线,则会标记其为
                   
                    客观下线
                   
                  
                 
                  自动故障转移
                  
                
                   从哨兵集群中选出leader(raft)
                  
                  
                   哨兵leader向某个slave发送slave of no one命令,让其成为master
                   
                  
                    选取策略
                    
                  
                     如果slave和哨兵断开时间比较紧久,超过阈值则失去选举权
                    
                    
                     优先级:数值越小越优先(配置中配置replica-priority 100)
                    
                    
                     复制数量:优先级相同,选取从master中复制的数据多的
                    
                    
                     进城id:如果复制量也相同,则选择进程id小的
                    
                   
                   向其他节点发送replicaof命令,让他们成为选取的master的slave节点,
                   
原master节点变为新master的从节点
                  
                  原master节点变为新master的从节点
                   通知客户端主节点已更换
                  
                 
                Cluster(分片)模式
                
              
                 作用
                 
                
                  可拓展
                  
                 
                   数据分片,解决单节点容量限制问题
                  
                 
                  高可用
                  
                 
                   主从切换
                  
                 
                  高并发
                 
                
                 至少3个master,3个slave
                
                
                 特点
                 
                
                  客户端可以连接任何一个主节点进行读写
                 
                 
                  支持在线增加、删除节点
                 
                 
                  不支持同时处理多个key(如MSET/MGET)
                 
                 
                  不支持事务
                 
                
                 key寻址(寻址算法)
                 
                
                  哈希算法
                  
                 
                   节点变动造成大量数据迁移
                  
    
                 
                  一致性哈希
                  
                 
                   不适合 少量数据节点 的分布式方案
                   
                   
                 
                    圆环分布不均,导致部分节点压力大
                   
                   
                    当一个节点挂点,数据迁移到临近节点,导致临近节点压力大
                   
                  
                  哈希槽
                  
                
                   
                    16384个slot
                   
                   ,每个node分配一个区间的slot
                  
                  
                   crc16(key)%16384 => slot => node
                   
                  
                    Q:如何让不同的key落在同一个节点?
                    
                  
                     A:key里加入{tag},用tag寻址
                     
                    
                   
                   扩容缩容
                   
                   
                 
                    key 与 slot 的关系是永远不会变的,会变的只有 slot 和 node 的关系
                   
                   
                    slot和node关系改变的过程中服务是可用的,所以可以动态扩缩容
                   
                   
                    扩容则从每个node中分出一部分slot给新node。
                    
缩容则把当前闹得的slot分给剩下的node
                    
                  缩容则把当前闹得的slot分给剩下的node
                     数据分布均匀
                    
                   
                 codis
                
               
              存在问题
              
             
               双写一致性
               
              
                不一致情景
                
               
                 先更新数据库,再更新缓存
                 
                 
                
                  更新缓存失败,存在不一致性
                 
                 
                  并发带来的问题,a线程先更新数据库,此时b线程更新数据库,更新缓存,最后a线程继续更新缓存,存在不一致
                 
                
                 先更新数据库,再删除缓存
                 
                
                  删除缓存失败,存在不一致
                 
                
                 先删除缓存,再更新数据库
                 
               
                  并发带来问题,a线程先删除,此时b线程未命中缓存,写入缓存,最后a更新数据库,产生不一致
                  
                 
                 
                  数据库主从带来问题,未来得及同步到从库,未命中缓存,写入缓存,读取的是旧数据
                 
                
                不一致原因
                
               
                 并发况下线程不安全
                
                
                 操作数据库或者缓存其中之一失败了
                
               
                方案
                
              
                 延时双删
                 
                
                  1、删除缓存
                  
2、更新数据库
3、rocketMQ延时消息删除
                 
                2、更新数据库
3、rocketMQ延时消息删除
                 删除缓存重试机制
                 
                
                  删除失败丢进mq重试
                 
                
                 订阅binlog日志(canal),监听到数据变化,通过mq删除
                
                
                 更新缓存,mq消息异步更新数据库
                
               
               缓存雪崩
               
              
                
                 大量缓存
                
                失效(或者Redis宕机),导致大量请求落到数据库
               
               
                解决方案
                
              
                 设置随机过期时间,防止同一时刻大量key过期
                
                
                 保证redis高可用
                 
                
               
               缓存穿透
               
              
                请求数据库中
                
                 
                  不存在
                 
                
                的数据,导致无法缓存,后续请求全部打在数据库
               
               
                解决方案
                
              
                 参数校验,非法参数直接拒绝
                 
                
                
                 缓存‘null’,并设置过期时间
                 
                
                  如果恶意攻击,不断请求不同的不存在的key,还是解决不了问题
                 
                
                 布隆过滤器
                 
               
                  位图/hash计算
                  
                 
                   位图实际是二进制数组,只存储0和1,分别表示不存在和存在。位图占用内存空间非常小
                  
                  
                   key经过n次hash计算得到n个hash值,映射到数组上,把0变为1
                   
                 
                    n个下标的值都是1表示存在,只有有0表示不存在
                   
                  
                  缺点
                  
                 
                   不能删除
                  
                  
                   误判
                   
                 
                    判断不存在肯定不存在
                   
                   
                    判断存在可能不存在
                    
                   
                     hash冲突
                    
                   
                    降低误判率
                    
                  
                     更长的二进制数组
                     
                    
                      需要更多内存空间
                     
                    
                     更多次的hash计算
                     
                   
                      需要更多cpu资源
                     
                    
                  实现
                  
                
                   Reddison
                  
                 
               缓存击穿
               
              
                
                 某一个
                 
                  热点key失效
                 
                
                ,导致大量请求落到数据库
               
               
                解决方案
                
              
                 分布式锁
                 
                
                  只有一个请求可以访问数据库,缺点是会导致其它请求排队。而且都会打到数据库
                 
                 
                  优化:zk锁获取锁成功之后,双重检查一下是否存在,存在说明更新了直接返回。否则查询db然后构建缓存
                  
                 
                
                 热点key永不过期,value添加逻辑过期时间,第一个抢到锁的更新缓存,其他线程直接返回旧值
                 
                
                  存在数据不一致
                  
                 
                
                 双key
                 
               
                  设置两个key
                  
                 
                   key
                   
                  
                    保存业务数据,过期时间较长
                   
                  
                   lock_key
                   
                 
                    过期时间较短
                   
                  
                  流程
                  
                
                   第一个线程setnx(lock_key,value)成功,说明lock_key之前已经过期了,则取读取db后set(key,value)
                  
                  
                   其他线程setnx失败,直接返回get(key)
                  
                 
               hot key
               
              
                发现
                
               
                 redis-cli --hotkeys
                
                
                 代理层统计
                 
                
                  饿了么开源框架Samaritan
                 
                
                 客户端统计
                
               
                hot key访问方案
                
              
                 增加一层本地缓存,如Map,Guava cache或Ehcache
                
                
                 热点key分散成多个子key,在集群多个机器上设置相同的值。请求进来通过一定的算法(比如一致性hash),选择一台机器访问,分散压力
                
                
                 读写分离:多个slave
                 
                
               
               big key
               
             
                value占用空间大或者元素个数多
               
               
                影响
                
               
                 请求耗时或超时
                
                
                 占用带宽和cpu
                
                
                 造成集群数据倾斜
                
                
                 删除导致阻塞
                
               
                发现
                
              
                 redis-cli --bigkeys
                
               
              应用
              
             
               事务
               
              
                实现方式
                
               
                 MULTI 、 EXEC 、 DISCARD 、WATCH
                
                
                 使用lua脚本
                
               
                ACID
                
              
                 原子性
                 
                
                  保证一组命令不被分割
                 
                 
                  不支持回滚
                  
                
                   因为发生错误是编程错误(只会是语法错误或者命令使用在不对的键值类型上),不应该发生在生产环境上
                  
                  
                   不支持回滚可以使其简单快速
                  
                 
                 一致性
                
                
                 隔离性
                 
                
                  redis是单线程的
                 
                
                 持久性
                 
               
                  开启aof备份,并且appendfsync always
                 
                
               lua
               
              
                保证命令间不会插入别的命令
               
              
               发布/订阅
              
              
               实现延时队列
               
              
              
               异步队列
               
              
                list作为队列,lpush生产消息,brpop消费消息
               
               
                发布订阅
                
              
                 不可靠,订阅者不一定能收到消息
                
               
               管道
               
              
                一次发送多个命令,节省往返时间(RTT,round trip time)
               
              
               分布式锁
              
             
              拓展
              
            
               客户端
               
              
                序列化协议
                
               
                 RESP
                 
               
                  发送命令格式
                 
                 
                  返回结果格式
                 
                
                Java客户端
                
              
                 Jedis
                 
                
                  Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持
                 
                
                 Redisson
                 
               
                  实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。
                 
                 
                  宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上
                 
                
               Redis变慢的原因(阻塞)
               
              
                内在原因
                
               
                 时间复杂度高的命令
                 
                 
                
                  定位
                  
                
                   慢查询日志
                   
                  
                    slowlog-log-slower-than num
                    
                   
                     记录执行时间大于num微秒的命令
                    
                   
                    slowlog-max-len num
                    
                   
                     保留最近num条慢日志
                    
                   
                    slowlog get num
                    
                  
                     获取指定数量的记录
                    
                   
                   查看命令统计信息
                   
                 
                    info commandstats
                   
                  
                 big key
                 
                
                  危害
                  
                 
                   慢查询
                  
                  
                   占用带宽
                  
                  
                   数据倾斜(集群)
                  
                 
                  定位
                  
                 
                   redis-cli --bigkeys
                  
                 
                  分类
                  
                 
                   比如hash、set、list等元素很多
                  
                  
                   String类型的value占用内存很大
                  
                 
                  优化
                  
                
                   lazy-free机制
                   
                 
                    删除大key的内存释放在后台异步线程执行,
                    
而不阻塞主线程
                    
                  而不阻塞主线程
                     主动删除
                     
                    
                      UNLINK KEY
                      
                    
                       DEL KEY是阻塞删除
                      
    
                      
                       key的元素大于64才会异步释放,否则同DEL命令
                      
                     
                     淘汰删除,过期删除等开启lazy-free
                    
                   
                 key集中过期
                
                
                 内存达到上限
                 
                
                  写入数据同时要淘汰数据
                 
                
                 CPU饱和
                 
                
                  指Redis把单核CPU使用率跑到接近100%
                  
                 
                   top
                  
                 
                  定位
                  
                 
                   redis-cli --stat
                   
                 
                    1s打印一次,可以看到每秒请求数
                   
                  
                  解决
                  
                
                   集群水平拓展,读写分离
                   
                  
                 
                 持久化阻塞
                 
               
                  fork阻塞
                  
                 
                   bgsave或者aof重写
                  
                  
                   定位
                   
                  
                    info stats查看latest_fork_usec指标:上一次fork耗时多少微秒
                    
                   
                  
                   优化
                   
                 
                    不要使用过大的redis内存
                   
                   
                    降低aof重写的触发频率
                   
                  
                  AOF阻塞
                  
                
                   AOF阻塞,fsync慢导致阻塞主进程
                  
                  
                   定位
                   
                  
                    info persistence查看aof_delayed_fsync
                    
                  
                     fsync调用超过2s则计数+1
                    
                   
                   优化
                   
                 
                    redis单独部署,不要跟高磁盘负载的服务部署在一起
                   
                   
                    使用sds硬盘
                   
                  
                外在原因
                
              
                 CPU竞争
                
                
                 内存交换
                 
                
                  如果操作系统把Redis使用的部分内存 换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的 Redis性能急剧下降
                 
                 
                  定位
                  
                 
                   info server查看process_id
                  
                  
                   cat /proc/{process_id}/smaps | grep Swap
                   
                   
                 
                    r如果值基本都是0K,则表示没有内存交换
                    
                   
                  
                  预防
                  
                
                   保证足够可用内存
                  
                  
                   设置合适maxmemory
                  
                  
                   Linux>3.5,vm.swappiness建议为1,否则建议为0
                   
                 
                    宁可OOM killer杀死进程,也不内环交换
                   
                  
                 网络问题
                
               
               scan & keys
               
              
                scan
               
              
               info
               
              
                info server:服务器信息
               
               
                info clients:客户端信息
               
               
                info memory:内存信息
               
               
                info persistence:持久化信息
               
               
                info stats:全局统计信息
               
               
                info replication:复制信息
               
               
                info cpu:cpu消耗信息
               
               
                info commmandstats:命令统计信息
               
               
                info cluster:集群信息
               
               
                info keyspace:键统计信息
               
              
               memory
               
             
                memory usage key
               
               
                memory stats
               
              
             MQ
             
            
              总览
              
             
               作用
               
              
                异步处理
                
               
                 异步线程是在一个进程,mq可以跨进程
                
                
                 宕机也不会丢失消息
                 
                
               
                应用解耦
               
               
                流量削峰
               
              
               缺点
               
              
                系统可用性降低
                
               
                 比如MQ服务挂了
                
               
                系统复杂度增加
                
               
                 比如重复消费、消息丢失、顺序消费等问题
                
               
                运维成本增加
               
              
               问题
               
              
                重复消费
                
               
                 原因
                 
                
                  消息发送重复
                  
                 
                   因为网络抖动等原因没有收到broker的确认导致消息重发
                  
                 
                  消息投递重复
                  
                 
                   因为网络导致没有收到ack,为保证至少消费一次而重新投递
                  
                 
                  负载均衡时候重复
                  
                
                   消费者宕机没有提交位移、扩容导致的reblance等
                  
                 
                 解决
                 
               
                  业务幂等
                 
                 
                  通过消息的唯一id进行去重
                 
                
                消息积压
                
               
                 消息发送过快
                
                
                 消费者消费过慢
                 
               
                  消费耗时
                  
                 
                   IO耗时
                   
                  
                    数据库读写慢
                    
                  
                     批量消费,批量更新
                    
                    
                     索引优化
                    
                    
                     分库分表
                    
                   
                   CPU耗时
                   
                  
                    递归或者循环
                   
                  
                   下游系统调用耗时
                  
                  
                   消费者是否消费异常
                  
                 
                  消费并发度
                  
                
                   增加消费者
                  
                  
                   增加机器配置
                  
                  
                   队列扩容,分区扩容
                  
                 
                消息丢失
                
               
                 从发送、存储、消费等维度保证消息不丢失
                
               
                顺序消费
                
               
                 全局顺序
                
                
                 分区顺序
                
               
                高可用
               
              
               MQ对比选型
              
             
              RabbitMQ
              
               
              
             
                核心概念
                
              
                 Broker
                
                
                 VHost
                 
                
                  一个broker可以有多个VHost,起到隔离作用
                  
                
                   可以新建一个vhost,本地测试使用该vhost
                  
                 
                 Exchange
                 
                
                  消息先投递到交换机上
                  
                 
                 
                  4种类型
                  
                
                   direct:直连交换机
                   
                  
                    根据消息携带的路由键将消息投递给对应队列
                    
                   
                   
                    简单说就是精准匹配
                    
                   
                  
                   fanout:扇形交换机
                   
                  
                    将消息路由给绑定到它身上的
                    
                     所有
                    
                    队列
                    
                   
                   
                    简单说就是广播
                    
                   
                  
                   topic:主题交换机
                   
                  
                    对路由键进行模式匹配后进行投递
                    
                   
                   
                    简单说就是模糊匹配
                    
                   
                  
                   headers:头交换机
                  
                 
                 Binding key
                 
                
                  交换机通过绑定键和队列绑定
                  
                 
                 
                  *: 1 个
                  
#:0 个或多个
                 
                #:0 个或多个
                 Routing key
                 
                
                  消息发送到指定交换机,消息设置路由键通过路由键投递到到对应的队列
                  
                 
                
                 Queue
                 
                
                  消息默认存在内存,开启持久化模式则持久化到磁盘
                  
                 
                
                 Connection
                 
                
                  TCP长连接
                 
                
                 Channel
                 
               
                  在tcp长连接里创建和释放channel,减少connection的频繁创建和销毁的开销
                 
                
               TTL
               
              
                队列
                
               
                 x-message-ttl
                
               
                消息
               
              
               死信
               
              
                消息变成死信的条件
                
               
                 (NACK || Reject) && requeue = false
                
                
                 消息过期
                
                
                 队列达到设定的最大消息条数或者字节数,最先入队的会变成死信
                
               
                使用步骤
                
              
                 定义
                 
                  死信交换机
                 
                 和
                 
                  死信队列
                 
                
                
                 绑定死信队列和死信交换机
                
                
                 定义死信队列的消费者
                
                
                 定义普通交换机和普通队列,并且普通队列设置死信交换机属性为前面定义的死信交换机
                
                
                 绑定普通交换机和普通队列
                
               
               延迟队列
               
              
                RabbitMQ不支持
               
               
                实现
                
              
                 数据库+定时器
                
                
                 消息TTL + 死信队列
                 
                
                  流转流程
                  
                 
                   生产者—>原交换机—>原队列(超过 TTL 之后)—>死信交换机—>死信队列—>最终消费者
                  
                 
                  缺点
                  
                
                   可能有时间误差
                  
                 
                 rabbitmq-delayed-message-exchange 插件
                
               
               流控
               
              
                消息发送速度远大于消费速度,造成消息积压,所以要流控
               
    
               
                措施
                
              
                 服务端限流
                 
                
                  queue
                  
                 
                   x-max-length
                   
                  
                    队列中最大消息条数,超过队头的被丢弃
                   
                  
                   x-max-length-bytes
                   
                 
                    队列中存储的最大消息容量(bytes),超过队头的被丢弃
                   
                  
                  内存控制
                  
                 
                   默认MQ占用内存达到40%则会抛出警告,阻塞所有连接
                  
                 
                  磁盘控制
                  
                
                   磁盘空余低于指定的值则触发流控
                  
                 
                 消费者限流
                 
               
                  默认是消息push给消费者(consumer在本地缓存所有的message),如果来不及消费会导致OOM或者其他影响
                 
                 
                  prefetch count
                  
                
                   autoAck为false才生效
                  
                  
                   在服务端设置,在channel或者connection上设置,未ack的最大消息数。
                   
比如该值为2,当有两个消费者,一个消费者有两条消息没有ack,那么接下来消息push给另一个消费者
                  
                 比如该值为2,当有两个消费者,一个消费者有两条消息没有ack,那么接下来消息push给另一个消费者
               集群模式
               
              
                普通集群模式
                
               
                 普通集群模式,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。
                 
你创建 的 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据
可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时
候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来
                
                你创建 的 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据
可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时
候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来
                 主要是用来提高吞吐量,非高可用
                
               
                镜像集群模式
                
                
              
                 你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,
                 
每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每
次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上
                每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每
次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上
                 高可用,无法拓展queue
                
               
               可靠性投递
               
              
                消息丢失
                
               
                 生产者
                 
                
                  使用事务机制,是同步的会阻塞
                  
                 
                 
                  使用confirm机制,是异步非阻塞的。broker回调发送结果
                  
                 
                
                 Broker
                 
                
                  消息持久化
                  
                  
                 
                   默认消息只存在内存,不会持久化到磁盘
                   
                  
                  
                   交换机、队列设置为持久化,发送消息设置发送模式deliveryMode=2,代表持久化消息
                   
                  
                 
                  镜像集群模式
                 
                
                 消费者
                 
               
                  手动Ack
                  
                 
                
                幂等性
               
               
                顺序消息
                
              
                 需要顺序消费的消息发送到同一个queue,
                 
并且只有一个消费者去消费,并且是单线程消费
                
                并且只有一个消费者去消费,并且是单线程消费
                 步骤
                 
                 
               
                  需要顺序消费的消息发送到同一个queue
                  
                 
                 
                  集群模式下也只有一个消费者消费:
                  
设置单活消费者
                  
                 设置单活消费者
                   
                    队列
                   
                   的参数
                   
x-single-active-consumer设置为true
                  
                 x-single-active-consumer设置为true
                  手动ack,prefetchCount=1
                  
                 
                
               优缺点
               
               
              
                优点
                
                
               
                 单机吞吐量高,达万级
                 
                
                
                 延迟低,达微秒级
                 
                
                
                 社区成熟活跃
                 
                
                
                 提供较为友好的后台管理页面
                
                
                 支持多种语言
                 
                
               
                缺点
                
                
              
                 吞吐量(单机万级)对于一些场景不够用
                 
                
                
                 不支持水平扩容
                
                
                 消息ack后就会删除,无法回查消息
                 
                
                
                 当产生消息堆积 ,性能下降明显
                
                
                 erlang语言,不利于二次开发和维护
                 
                
               
               Spring AMQP核心组件
               
             
                ConnectionFactory
                
               
                 用于创建连接
                
               
                RabbitAdmin
                
               
                 是AmqpAdmin的实现,主要是队交换机、队列、绑定的声明和创建
                
               
                Message
                
               
                 消息的封装
                
               
                RabbitTemplate
                
               
                 目前是AmqpTemplate的唯一实现,用来简化消息的收发。封装了创建连接、创建channel、
                 
收发消息、消息格式转换、关闭channel、关闭连接等操作
               收发消息、消息格式转换、关闭channel、关闭连接等操作
                MessageListener
                
               
                 处理消息
                
               
                MessageListenerContainer
                
               
                 messageListener的容器,一个container只有一个listener,但是可以生成多个线程使用相同的listener同时消费消息。
                 
可以管理listener生命周期,队消费者进行配置。比如动态添加移除队列、队消费者设置包括并发、消费者数量、消息确认模式等
               可以管理listener生命周期,队消费者进行配置。比如动态添加移除队列、队消费者设置包括并发、消费者数量、消息确认模式等
                MessageListenerContainerFactory
                
               
                 需要监听多个rabbit服务器,则可以指定不同factory
                
               
                MessageConvetor
                
              
                 消息的序列化,默认SimpleMessageConverter
                
               
              RocketMQ
              
               
               
              
             
                核心概念
                
              
                 组件
                 
                
                  NameServer
                  
                  
                 
                   注册中心
                   
                  
                    支持broker、topic路由信息的动态注册和发现
                    
                  
                     broker管理
                    
                    
                     topic路由信息管理
                    
                   
                   故障发现
                   
                  
                    每10s检查broker的心跳信息,超过120s没有心跳,则移除该broker的路由信息
                   
                  
                   可集群部署
                   
                  
                    多实例部署,互不通信,互相独立
                   
                  
                   为啥不用zk
                  
                 
                  Broker
                  
                 
                   消息投递、存储、查询以及服务高可用保证
                  
                  
                   路由注册
                   
                  
                    启动会在nameserver上注册路由信息
                   
                   
                    每30s发送心跳,心跳包含ip、port、topic信息
                   
                  
                   多master/slave
                   
                 
                    角色
                    
                   
                     ASYNC_MASTER(异步主机)
                    
                    
                     SYNC_MASTER(同步主机)
                    
                    
                     SLAVE(从机)
                    
                   
                    数据分片,实现横向拓展
                   
                   
                    master负责读写数据
                    
                   
                     brokerId=0的为master
                    
                   
                    slave复制mater数据
                    
                   
                     可靠性,防止数据丢失
                    
                    
                     只有brokerId=1的slave在master读取慢的情况才参与到读
                    
                   
                    读写分离
                    
                  
                     master负责读写
                    
                    
                     正常情况slave不参与读
                     
                   
                      当消息堆积大于物理内存40%则
                      
                       brokerId=1
                      
                      的
                      
slave可以读取数据
                     
                    slave可以读取数据
                  Producer/Producer Group
                 
                 
                  Consumer/Consumer Group
                 
                
                 topic
                 
                
                  生产者将消息发到主题(实际是队列),消费者订阅主题获取消息
                 
                 
                  创建topic会指定队列数量
                  
                
                   写队列数量(默认4)
                   
                  
                    决定message queue的数量
                    
                   
                     如果m个broker,n个写队列,那么总队列数就是m*n
                    
                   
                    consumequeue/{topic}/路径下就有多少个目录(目录按照0、1、2、...n命名)
                   
                   
                    MessageQueue
                    
                  
                     包含topic、brokerName和queueId信息
                    
                    
                     可以知道消息往哪个broker,哪个topic和哪个queue发送
                    
                   
                   读队列数量(默认4)
                   
                 
                    决定几个线程消费这些message queue
                   
                   
                    >=写队列数量,否则会有message queue的消息不能被消费
                   
                  
                 tag
                 
                
                  可以用来过滤消息
                 
                
                 key
                 
                
                  相当于消息的索引
                 
                 
                  业务层面的设置唯一标识
                 
                
                 message
                 
               
                  必须包含topic,可以没有tag,有唯一 Message ID,可以是具有业务标识的值
                 
                 
                  可以根据 Message ID & Key查询消息
                 
                
               特性&原理
               
              
                生产者
                
               
                 发送方式
                 
                
                  单向发送
                 
                 
                  同步发送
                 
                 
                  异步发送
                 
                
                 选择 message queue
                 
                
                  选择broker,选择topic,选择queue
                 
                 
                  策略
                  
                
                   轮询(默认)
                  
                  
                   随机
                  
                  
                   自定义实现MessageQueueSelector接口
                  
                 
                 事务消息
                 
                
                  保证了本地事务和消息发送的原子性
                 
                 
                  消息状态
                  
                 
                   TransactionStatus.Unknown
                   
中间状态,对消费者不可见
                  
                  中间状态,对消费者不可见
                   TransactionStatus.CommitTransaction
                   
提交,消费者可以拉取消费
                  
                  提交,消费者可以拉取消费
                   TransactionStatus.RollbackTransaction
                   
回滚,删除消息
                  
                 回滚,删除消息
                  流程
                  
                
                   消息发送到broker,状态unknown对消费者不可见,生产者执行本地事务,成功则commit消息,对消费者可见
                  
                  
                   补偿阶段(回查)
                   
                 
                    如果一直没有收到生产者的commit/rollback消息,则发起回查,根据本地事务状态来commit/rollback消息
                   
    
                   
                    默认回查15次,如果还是无法得知事务状态,则默认回滚消息
                   
                  
                 延时消息
                 
               
                  开源版本只支持18个级别的延迟消息
                 
                 
                  原理
                  
                 
                 
                  场景:订单超时取消
                 
                
                Broker
                
               
                 消息存储
                 
                
                  存储架构
                  
                 
                   commitlog
                   
                  
                    消息顺序写入commiltlog,单个broker实例所有topic共用一个commitlog存储
                   
                   
                    每个文件默认大小1G,文件名长度20位,左边补零,数字为消息起始偏移量,写满就写入下一个文件
                   
                  
                   consumequeue
                   
                  
                    consumequeue相当于索引,不同topic的消息在commitlog中的offset放入到对应的consumerque中,这样消费者
                    
只需去对应的consumeque获取物理偏移量offset去commitlog中拉取消息
                   
                   只需去对应的consumeque获取物理偏移量offset去commitlog中拉取消息
                    提高查询消息性能
                   
                  
                   indexFile
                   
                 
                    可以通过key或者时间区间查询消息
                   
                  
                  顺序读写
                  
                 
                   commitlog顺序写
                  
                  
                   consumequeue顺序读
                  
                 
                  page cache(页缓存)
                  
                 
                   写入缓存异步刷盘提升写性能
                  
                  
                   预读机制,将邻近数据预读写入缓存,提升查询性能
                  
                 
                  零拷贝:mmap(内存映射)
                  
                 
                   对文件的操作转化成对内存地址的操作,减少内核缓冲区和用户缓冲区的拷贝的开销
                  
                 
                  消息刷盘
                  
                
                   在broker.conf配置flushDiskType
                   
                 
                    ASYNC_FLUSH(默认)
                    
                   
                     消息先写入缓存中,达到一定程度则刷盘
                    
                    
                     吞吐量高,性能好,会丢数据
                    
                   
                    SYNC_FLUSH
                    
                  
                     消息写入缓存成功后,立刻刷盘
                    
                    
                     性能差,不会丢失数据
                    
                   
                 消息查询
                 
                
                  MessageId
                  
                 
                   解析MessageId得到存储消息broker的ip、port、commitlog offset,然后向目标broker发起rpc请求查询
                  
                 
                  Message key
                  
                
                   基于IndexFile索引文件实现
                  
                 
                 文件清理
                 
                
                  commitlog清理
                  
                 
                   条件
                   
                 
                    默认每天凌晨4点删除超过72小时的文件
                   
                   
                    磁盘使用超过85%,没有到凌晨4点也会清除过期文件,直到低于85%
                   
                   
                    达到90%拒绝写入
                   
                  
                  consumerqueue清理
                 
                
                 主从同步&故障转移
                 
               
                  集群模式
                  
                
                   2m-2s-async
                  
                  
                   2m-2s-sync
                  
                  
                   2m-noslave
                  
                 
                消费者(组)
                
              
                 消费模式
                 
                
                  集群模式
                  
                 
                   一条消息只会被消费者组里一个消费者消费
                  
                  
                   消费位移存放在broker
                  
                 
                  广播模式
                  
                
                   一条消息被消费者组所有消费者消费
                  
                  
                   消费位移存放在客户端
                  
                 
                 负载均衡
                 
                
                  平均分配(默认)
                 
                 
                  一致性哈希
                 
                
                 重平衡机制
                 
                
                  对topic进行扩容(增加consumequeue个数)或者消费者扩容缩容则会触发消费队列重新分配
                  
                 
                
                 消费模型
                 
                
                  并发消费
                  
                 
                   对一个队列中消息,每一个消费者内部都会创建一个线程池,对队列中的消 息多线程处理,
                   
即偏移量大的消息比偏移量小的消息有可能先消费
                  
                  即偏移量大的消息比偏移量小的消息有可能先消费
                   该模式下消费失败默认会16次衰减重试
                  
                 
                  顺序消费
                  
                
                   该模式消息消费失败,会一直重试,直到成功
                  
                 
                 重试 & 死信队列
                 
                
                  重试16次失败则丢进死信队列
                 
                 
                  RocketMQ 控制台提供对死信消息的查询、导出和重发的功能
                 
                
                 支持 push & pull
                 
                 
               
                  push
                  
                  
                 
                   实时性高
                   
                  
                  
                   消费端没有做好流控会导致积压甚至崩溃
                   
                  
                 
                  pull
                  
                
                   普通轮询
                   
                  
                    每隔一段时间去拉取消息
                   
                   
                    缺点
                    
                  
                     大部分时间没有消息,很多无效请求,浪费服务器资源
                    
                    
                     定时请求,造成消息延迟
                    
                   
                   长轮询
                   
                 
                    如果没有消息,会hold住请求直到有消息或者超时返回,然后发起下一次长轮询
                   
                   
                    broker端属性 longPollingEnable 标记是否开启长轮询。默认开启
                   
                   
                    缺点
                    
                  
                     耗费内存
                    
                   
               其他
               
              
                保证消息不丢失(可靠性)
                
               
                 producer
                 
                
                  同步或异步 + 失败重试
                 
                
                 broker
                 
                
                  同步刷盘 + 主从同步复制
                 
                
                 consumer
                 
               
                  手动ack
                 
                
                顺序消息
                
                
               
                 生产者
                 
                 
                
                  同步发送
                 
                 
                  根据sharding key把消息发送到同一个messageQueue(定义MessageQueueSlector)
                 
                
                 消费者
                 
                 
               
                  设置为有序消费模式,使用
                  
                   顺序监听器
                  
                  
                 
                
                消息积压
                
               
                 原因
                
                
                 解决
                 
               
                  messageQueue数量 < 消费者数量
                  
                 
                   增加消费者
                  
                 
                  messageQueue数量 >= 消费者数量
                  
                
                   messageQueue扩容
                  
                 
                并发消费下位移提交
                
               
                 比如msg1,msg2,msg3三条消息并发消费,msg3先消费完成ack如何提交位移呢,是提交msg1的位移还是msg3的位移
                
                
                 提交最小offset
                 
                
                  不会丢失消息
                 
                 
                  宕机重启会重复消费,幂等性处理
                 
                
                 实现原理
                 
               
                  消息存放在key为offset的treeMap中,消费完一个则从map中删除,返回最小的key并提交位移
                 
                
                broker宕机,nameserver自检有
                
延时,这段窗口期如何规避
                
              延时,这段窗口期如何规避
                 重试机制,默认两次
                
                
                 sendLatencyFaultEnable
                 
Broker故障延迟机制
                 
               Broker故障延迟机制
                  false默认值
                  
                 
                   如果broker不可用,重试选择其他broker,可能还是不可用broker(浪费一次重试机会)
                  
                 
                  true
                  
                
                   故障规避
                   
                 
                    判断broker是否可用,不可用则轮询下个broker(在一次重试中切换)
                   
                   
                    如何判断是否可用
                   
                  
               优缺点
               
               
             
                优点
                
                
               
                 高性能:单机吞吐十万级
                 
                
                
                 高拓展:支持弹性伸缩
                 
                
                
                 高可用:分布式架构
                 
                
                
                 合理的参数可以保证消息0丢失
                 
                
                
                 支持10亿消息堆积,且不影响性能
                 
                
                
                 支持消息回查、重新消费、事务消息、延时消息、顺序消息等
                 
                
                
                 java编写,易于二次改造
                 
                
                
                 经过双十一实战考验
                
               
                缺点
                
                
              
                 支持语言少(java\go\c++)
                 
                
                
                 社区活跃度一般
                 
                
               
              Kafka
              
            
               思维导图
              
             
             配置&注册中心
             
            
              Nacos
              
             
               配置中心
               
              
                源码
                
               
                 NacosConfigService
                 
                
                  构造方法
                  
                
                   HttpAgent
                   
                  
                    负责网络请求等
                   
                  
                   
                    ClientWorker
                   
                   
                 
                    clientWorker主要就是每10ms检查是否需要有
                    
                     新的
                    
                    cacheData,有则发起长轮询(LongPollingRunnable)
                   
                   
                    LongPollingRunnable
                    
                  
                     CacheData
                     
                    
                      taskId
                      
                     
                       因为检查配置是分批的,taskId用来标记哪一批
                      
                     
                      dataId、group、tenant
                     
                     
                      content
                      
                     
                       配置内容
                      
                     
                      md5
                      
                     
                       contend通过md5算法得到的值,用于快速比较配置是否更新了
                      
                     
                      isUseLocalConfig
                     
                     
                      localConfigLastModified
                      
                     
                       上一次修改时间戳
                      
                     
                      CopyOnWriteArrayList<ManagerListenerWrap> listeners
                      
                    
                       监听配置是否改变
                      
    
                     
                     在run方法finally块里不停重复执行,周期默认30s(29.5s)
                    
                    
                     主要做了两件事
                     
                   
                      检查本地配置信息,更新cacheData的一些属性。如果配置更新了,还会回调该配置的listener
                     
                     
                      通过不断长轮询从服务端获取变化了的dataId列表,遍历dataIds去服务端查询最新配置,更新本地快照,更新cacheData
                      
                     
                    
                 客户端感知配置变化:推还是拉?
                 
                  
                 
                
                
                  push
                  
                  
                 
                   tcp长连接可能会出现假死,需要通过心跳机制保证连接可用性
                   
                  
                  
                   长连接会消耗大量的系统资源
                   
                  
                 
                  pull
                  
                  
                 
                   通过轮询的方式,缺点就是实时性低,如果降低轮询的间隔又会造成很大的系统压力
                   
                  
                  
                   长轮询
                   
                   
                 
                    客户端不断长轮询服务端,如果配置更改了则立即响应,否则会hold住请求(29.5s)再响应客户端,实现了服务端push的效果
                   
                   
                    服务端将客户端请求封装成ClientLongPolling任务放入到allSubs队列里,延时任务29.5s执行ClientLongPolling,如果期间接收到
                    
                     LocalDataChangeEvent
                    
                    则从allSubs里删除订阅关系,同时响应客户端配置变化了。否则到了29.5s则自动从队列删除,并响应客户端没有配置变化
                    
                   
                  
                 配置变更的两个Event
                 
                
                  ConfigDataChangeEvent
                  
                 
                   控制台修改或者新增配置发布该事件
                   
                 
                    监听ConfigDataChangeEvent事件,则会调用dumpService#dump方法,
                    
dump方法就是往任务中心添加一个DumpTask(后台异步执行)
                    
                   dump方法就是往任务中心添加一个DumpTask(后台异步执行)
                     dumpService#dump
                     
                   
                      ConfigService#dump
                     
                     
                      updateMd5
                      
                    
                       updateMd5方法如果md5改变了,则更新,
                       
同时发布LoaclDataChangeEvent
                      
                     同时发布LoaclDataChangeEvent
                    DumpTask主要做什么
                    
                  
                     保存磁盘中的配置信息
                    
                    
                     更新本地缓存的md5和修改时间戳
                    
                    
                     发布LocalDataChangeEvent
                    
                   
                  LocalDataChangeEvent
                  
                
                   发布时机
                   
                  
                    ConfigDataChangeEvent触发dumpService#dump,发布LocalDataChangeEvent
                   
                   
                    后台定期检查配置有没有更改,更改则将数据库配置更新到磁盘,发布LocalDataChangeEvent
                   
                  
                   事件监听者LongPollingService
                   
                 
                    遍历allSubs队列,如果是当前配置,则从allSubs中删除,
                    
同时响应客户端的长轮询请求通知配置变更
                   
                  同时响应客户端的长轮询请求通知配置变更
                 客户端获取配置
                 
               
                  NacosConfigService#getConfig
                  
                
                   优先从本地获取
                  
                  
                   本地获取不到去Server获取
                   
                  
                    为了提升响应速度,服务端会将
                    
                     
                      mysql的配置数据dump到磁盘
                     
                    
                    ,从而提升速度;
                    
会有后台线程定期更新磁盘里的数据
                   
                  会有后台线程定期更新磁盘里的数据
                   server请求
                   
                    
                     异常
                    
                   
                   从本地快照(snashot)获取
                  
                 
                持久化
                
               
                 单机
                 
                
                  Derby数据库(默认),可以配置MySQL
                 
                
                 集群
                 
               
                  MySQL
                 
                
                集群
                
              
                 CP
                
               
               注册中心
               
                
               
               
             
                服务分级模型
                
                 
                
               
               
                源码
                
               
                 NacosNamingService
                
                
                 服务注册
                 
                
                  client
                  
                 
                   将服务名称,ip,端口等实例信息发送给server
                  
                 
                  server
                  
                
                   收到client的注册请求,如果service还没有创建,则先创建,并为其添加一个
                   
心跳检查的定时任务。然后封装成instance放入到缓存中
                  
                  心跳检查的定时任务。然后封装成instance放入到缓存中
                   向其他nacos servers同步数据
                   
                 
                    如何同步
                    
                   
                  
                 服务发现
                 
                
                  client
                  
                 
                   订阅模式
                   
                  
                    直接从本地缓存获取服务实例列表,如果不存在则请求server,
                    
并且订阅服务,拿到server返回的服务信息更新到本地缓存和磁盘文件中
                    
                  并且订阅服务,拿到server返回的服务信息更新到本地缓存和磁盘文件中
                     tips:
                     
1、磁盘里的服务信息会被后台任务写入到FailoverReactor的缓存中
2、正常情况是读取的HostReactor类的缓存。只有failover-mode = true才会
读取FailoverReactor的缓存
3、定时任务也会将HostReactor类的服务缓存写入到磁盘
                   1、磁盘里的服务信息会被后台任务写入到FailoverReactor的缓存中
2、正常情况是读取的HostReactor类的缓存。只有failover-mode = true才会
读取FailoverReactor的缓存
3、定时任务也会将HostReactor类的服务缓存写入到磁盘
                   非订阅模式
                   
                 
                    直接请求server获取服务的实例列表
                   
                  
                  server
                  
                
                   接收client的请求,返回服务列表
                   
                 
                    如果订阅服务的话,则将其封装成pushClient放入到缓存,用于后期推送服务变化
                   
                  
                 服务续约
                 
                
                  心跳续约,在服务端设置心跳时间戳,也就是更新instance的lastBeat为当前时间戳
                 
                
                 服务下线
                 
               
                  主动下线
                 
                 
                  故障下线
                  
                
                   客户端发送心跳,发送心跳的周期默认是 5 秒,服务端会在 15 秒没收到心跳后将实例设置为不健康,
                   
在 30 秒没收到心跳时将这个临时实例摘除,并通知订阅者
                  
                 在 30 秒没收到心跳时将这个临时实例摘除,并通知订阅者
                高可用
                
              
                 集群部署
                 
                
                  一致性协议
                  
                 
                   自研Distro
                   
                  
                    AP
                    
                   
                     如果数据丢失的话,通过心跳续约机制作为数据补偿,达到最终一致性
                    
                   
                    临时节点
                    
                  
                     不健康则会删除
                    
                   
                   简化的Raft
                   
                 
                    CP
                   
                   
                    持久节点
                    
                  
                     持久化到nacos服务端,不健康实例不会
                     
从服务端删除,只标记为不健康服务
                    
                   从服务端删除,只标记为不健康服务
                  serverMode配置
                  
                
                   AP(默认)、CP、MIXD
                  
                 
                 client存储服务列表
                 
               
                  缓存、快照、failover
                 
                
              ZooKeeper
              
             
               ZNode类型
               
              
                持久节点
               
               
                持久有序节点
               
               
                临时节点
               
               
                临时有序节点
               
              
               Watcher机制
               
              
                客户端可以在服务端注册一个watcher监听,当服务端的一些指定事件触发watcher,服务端会向客户端发送事件通知
                
               
               
                注册的watcher监听是一次性的
               
               
                EventType
                
              
                 NodeCreated
                
                
                 NodeChildrenChanged
                
                
                 NodeDataChanged
                
                
                 NodeDeleted
                
                
                 None
                 
               
                  客户端的连接状态发生变更
                 
                
               集群
               
              
                角色
                
               
                 leader
                 
                
                  处理读写请求,处理完写请求会广播事务,当过半节点写入成功,则会提交事务
                 
                 
                  脑裂
                  
                
                   集群中出现两个leader
                  
                  
                   避免
                   
                 
                    过半机制,集群机器数为2n+1
                   
                  
                 follower
                 
                
                  只能处理读请求,当收到写请求会转发给leader处理。参与选举
                 
                
                 observer
                 
               
                  不参与选举投票,处理非事务请求
                 
                
                ZAB协议
                
               
                 崩溃恢复模式
                 
                
                  当集群启动或者失去leader则会进入崩溃恢复模式,选举出leader,完成后退出该模式
                 
                
                 原子广播模式
                 
               
                  在leader正常工作时,加入一个新节点,则会进入原子广播模式,就会和leader进行数据同步
                 
                
                ZK的顺序一致性
                
               
                 比如leader事务提交,flower1已经同步到最新数据,follower2还没完成同步。在2为完成同步前,client连接到follower读取到某数据,并记录事务id(zxid:递增的数字),当由于某原因断开与flower1连接,下次重新连接,如果连接到follower2发现zxid小于自己记录的zxid则会连接失败。所以说client只要连接过一次zk,下次重新连接就不会读到比之前读的旧的数据
                
               
                leader选举
                
              
                 源码中几个关键参数
                 
                
                  Vote(myid, zxid,epoch)
                  
                 
                   myid
                   
                  
                    机器编号,在zk配置文件里指定的,数字越大,选举权重越大
                   
                  
                   zxid
                   
                  
                    事务id,数值越大,说明本机数据越新,选举权重越大
                   
                  
                   epoch-logicclock
                   
                  
                    逻辑时钟,也叫投票轮数。同一轮中,该值是一样的。每投完一轮则会递增
                   
                  
                   比较优先级:epoch>zxid>myid
                  
                 
                  节点状态
                  
                
                   LOOKING
                   
                  
                    竞选状态,只有该状态的机器才能参与选举投票
                   
                  
                   FOLLOWING
                   
                  
                    随从状态,同步leader数据,参与投票
                   
                  
                   OBSERVING
                   
                  
                    观察状态,不参与投票
                   
                  
                   LEADING
                   
                 
                    领导状态
                   
                  
                 选举时机
                 
                
                  启动选举
                  
                 
                   源码入口QuorumPeerMain#main(), zkServer.sh脚本执行该main方法
                  
                  
                   流程
                   
                 
                    开始每个节点状态都是LOOKING,接下来进行选举。每个server会发出投票,开始都是投自己,同时会接收其他server发送的投票,首先会判断投票的有效性。如果是有效投票,则会和自己的投票进行比较(优先级:epoch > zxid > myid),如果收到的投票优先级高于自己的,则更新自己的投票,并继续投票。每次投票后,会进行统计,如果有过半机器投票和自己当前投票一样,则选举出leader。最后如果是leader则更新自己为LEADING状态,如果是follower则更新自己为FOLLOWING
                   
    
                  
                  leader宕机后选举
                  
                
                   和启动选举流程基本一致,当Leader挂了,余下的非Observer服务器都会更新自己状态为LOOKING
                  
                 
                 3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法
                
               
               客户端
               
              
                Curator
               
               
                ZkClient
               
              
               应用
               
             
                实现分布式锁
               
               
                实现leader选举
               
               
                注册中心
               
               
                配置中心
               
               
                分布式协调
               
              
              Eureka
              
             
               client定时去server拉取服务列表缓存到本地
              
              
               自我保护机制
               
              
                发现大量实例心跳失败,可能是网络问题,启动自我保护机制。
                
防止误杀下线服务
               防止误杀下线服务
                客户端恢复心跳,则退出自我保护机制
               
              
               缓存
               
              
                server、client都是缓存存储服务信息
               
              
               部署
               
              
                单点
               
               
                集群
                
              
                 集群中的节点都是可以读写的
                
                
                 服务注册到其中一个节点,然后同步给其他节点
                
               
               负载均衡:Ribbon
              
             
              nacos & eureka & zk对比
              
            
               CAP
               
              
                zk是CP,leader宕机,会进行选主,期间服务不可用
               
               
                eureka是AP,server节点宕机,依然可以从其他节点进行
                
服务注册和发现,保证可用性。等节点恢复后会进行数据
同步,保证最终一致性
               
               服务注册和发现,保证可用性。等节点恢复后会进行数据
同步,保证最终一致性
                nacos默认是AP,可CP
               
              
               服务感知时效性
               
              
                (推)nacos通过推的方式把服务变化实时推送给client
               
               
                (拉)eureka采用定期拉取的方式,有较大的延迟
               
               
                zk可以立即感知
               
              
               存储容量
               
              
                eureka不适合大规模服务实例数。因为单机要内存存储所有服务实例信息
                
               
               
                zk 不适合大规模服务实例数。因为服务上下线的时候需要瞬间推送数据通知到其他
                
所有的实例,所以服务实例到几千个的时候,可能会导致网络带宽被打满
               
               所有的实例,所以服务实例到几千个的时候,可能会导致网络带宽被打满
                nacos支持百万级实例注册、十万级服务
               
              
               访问协议
               
             
                zk:TCP
               
               
                eureka:HTTP
               
               
                nacos:HTTP | DNS
               
              
             分库分表
             
            
              为什么分库分表
              
             
               分库:1、单机连接数受限、减轻数据库压力;
               
2、单机存储容量受限
              2、单机存储容量受限
               分表:单表数据量大,索引和sql优化不能解决问题
              
             
              分片键选择
              
             
               算法
               
             
                hash取模
               
               
                一致性hash
               
               
                range
               
              
              带来问题
              
             
               跨库导致无法使用本地事务
              
              
               没有分片键作为条件的分页、范围查询、排序等
               
              
                查询所有表内存中聚合处理
               
               
                分片键和非分片键建立映射表,覆盖索引性能可以
               
               
                分片键中含有非分片键信息
                
              
                 比如订单号中含有uid
                
               
               数据倾斜
              
             
              数据迁移
              
             
               停机迁移
              
              
               不停机迁移
               
             
                双写法
                
               
                 旧表和新表双写
                
                
                 数据迁移
                
                
                 数据一致性校验
                 
                
                  不一致以旧表为准
                 
                
                 流量切换到新表
                
               
                Sharding-proxy
               
              
              扩容
             
             
              方案
              
            
               ShardingSphere
               
              
                sharding-jdbc(JDBC层)
                
               
                 功能
                 
                
                  数据分片
                  
                 
                   分库分表
                  
                  
                   读写分离
                  
                  
                   分布式主键
                  
                 
                  分布式事务
                  
                 
                   XA强一致事务
                  
                  
                   柔性事务
                  
                 
                  数据库治理
                  
                
                   配置动态化
                  
                  
                   熔断&禁用
                  
                  
                   调用链路追踪
                  
                  
                   弹性伸缩
                  
                 
                 分片
                 
               
                  数据源分片&数据表分片
                 
                 
                  分片键
                  
                 
                   支持单个和多个
                  
                 
                  分片算法
                  
                
                   自定义一致性hahs
                  
                 
                sharding-proxy(代理端)
               
              
               Mycat
              
              
               mybatis的拦截器改写sql
              
             
             分布式&微服务
             
            
              分布式理论
              
             
               CAP
               
              
                C:一致性
                
               
                 所有节点同一时间的数据完全一致
                
               
                A:可用性
                
               
                 服务一直可用,正常响应
                
               
                P:分区容错性
                
              
                 某节点故障或者网络分区故障时候,仍然能够对外提供满足一致性和可用性的服务
                
               
               BASE
               
              
                基本可用
               
               
                软状态
               
               
                最终一致性
               
              
               一致性协议
               
             
                paxos
               
               
                raft(redis哨兵间选主)
                
               
               
                zab(zookeeper)
               
               
                gossip(redis cluster)
               
               
                distro(nacos)
               
              
              分布式事务
              
             
               相关理论
               
              
                模型
                
              
                 RM(resource manager)
                 
                
                  资源管理器,比如数据库
                 
                
                 TM(transaction manager)
                 
                
                  事务管理器,负责事务的提交和回滚等
                 
                
                 AP(application)
                 
               
                  应用程序
                 
                
               方案
               
               
              
                2PC
                
               
                 过程
                 
                
                  询问阶段
                  
                 
                   TM询问所有RM是否可以提交事务,并等待响应
                  
                  
                   RM执行本地事务,并未提交,根据是否成功返回yes或者no
                  
                 
                  提交阶段
                  
                
                   TM根据RM的响应结果,告知RM是commit还是rollback
                  
                 
                 存在问题
                 
               
                  单点故障
                  
                 
                   比如TM挂了,那么RM就会一直是prepare,不会commit也不会rollback
                  
                 
                  数据不一致
                  
                
                   比如TM通知所有RM提交事务,由于某些原因只有一部分RM收到的commit,则出现了不一致性问题
                  
                 
                3PC
                
               
                 针对2PC的问题做了改进,引入超时机制。
                 
在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的
                
                在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的
                 过程
                 
               
                  canCommit
                 
                 
                  preCommit
                 
                 
                  doCommit
                 
                
                TCC
                
               
                 Try阶段:对业务系统做检测以及资源预留
                
                
                 Confirm阶段:确认执行业务操作
                
                
                 Cancell阶段:取消执行业务操作
                
               
                最大努力通知
                
               
                 没有响应成功则不断重试
                
                
                 提供查询是否成功接口
                
               
                本地消息表
                
                
               
                 实现案例
                
               
                可靠消息最终一致性
                
               
                 支持事务消息的MQ,比如RocketMQ
                
                
                 不支持事务MQ。消息发到独立消息服务,prepare状态,本地事务提交,才真正发送消息
                
               
                Seata
                
              
                 事务模式
                 
               
                  AT
                 
                 
                  TCC
                 
                 
                  SAGA
                 
                 
                  XA
                 
                
               强弱一致性选择
               
             
                强一致性性能损耗
               
              
              分布式寻址算法
              
             
               hash取模
               
               
              
                key通过hash计算,取模
               
               
                缺点
                
              
                 节点数变动时候,需要按照新的节点数
                 
                  全部
                 
                 重新取模计算,然后迁移,影响数据范围大
                
               
               一致性hash
               
             
                解决什么
                
               
                 解决在分布式环境下,hash 表中可能存在的动态扩容和缩容的问题。
                 
造成数据需要全部重新hash取模,导致大量数据的迁移
               造成数据需要全部重新hash取模,导致大量数据的迁移
                原理
                
               
                 通过一个hash圆环,从0开始,到是 2^32-1结束
                
                
                 将存储节点的ip通过hash计算,在圆环上确定位置
                
                
                 要存储的值的key通过hash计算落在圆环的一个位置,通过顺时针查找,找到第一个节点存储
                
                
                 存储节点扩容缩容
                 
               
                  只会影响临近节点少部分数据
                 
    
                
                存在缺陷
                
              
                 存储节点过少,导致数据倾斜
                 
               
                  解决:虚拟节点
                 
                
              分布式锁
              
             
               Redis
               
              
                原则
                
               
                 安全性(safety)
                 
                
                  互斥性
                  
                 
                   同一时刻只有一个客户端可以持有锁
                  
                 
                  锁只能由加锁客户端释放
                  
                
                   value设置一个随机值,删除key时比较value,一致才可以删
                  
                 
                 活性(liveness)
                 
               
                  无死锁
                  
                 
                   设置过期时间
                  
                 
                  容错(只要大部分Redis节点都活着,客户端就可以获取和释放锁)
                 
                
                使用
                
               
                 加锁
                 
                
                  setnx(lock,random_value)
                  
expire(lock,expire_time)
通过lua脚本保证操作原子性
                 
                 expire(lock,expire_time)
通过lua脚本保证操作原子性
                  set(key,random_value,NX,PX, expire_time)
                  
                 
                
                 释放锁
                 
                
                  获取锁、判断random_value是否一致、一致则del(lock)
                  
                 
                   三步保证原子性,使用lua脚本
                  
                 
                  锁过期释放
                 
                
                 Redisson
                 
                 
               
                  存储类型:hash
                  
                  
                 
                   key为lock名称,field为线程id,value为重入次数
                   
                  
                 
                  发布订阅
                  
                  
                 
                   释放锁会发布消息通知订阅者
                   
                  
                 
                  watch dog
                  
                  
                
                   如果lock
                   
                    没有
                   
                   设置过期时间,则默认是30s,有定时任务每10s
                   
看锁是否还存在,存在则会续期重置过期时间为30s
                  
                 看锁是否还存在,存在则会续期重置过期时间为30s
                存在问题
                
              
                 单机
                 
                
                  不能高可用
                 
                
                 集群
                 
               
                  在master上获取锁成功,还没有将key同步到slave,这时候master宕机,主从切换时,新主没有该key,
                  
导致另一个客户端在新主上获得锁,违背了同一时刻只有一个客户端持有锁的原则
                 
                 导致另一个客户端在新主上获得锁,违背了同一时刻只有一个客户端持有锁的原则
                  方案
                  
                 
                   RedLock算法
                   
                 
                    有 N 个 Redis Master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制
                   
                   
                    多master上获取锁,过半成功且在设定的时间内完成才算成功,否则向所有实例发送删除指令。当释放锁时,要向所有实例发送del
                    
                   
                     前提:各服务器之间较小的时间漂移
                    
                   
                    存在问题
                    
                   
                     如果在持有锁期间master挂了,比如5个实例,原来3个获取锁成功,这是其中一个挂了,
                     
且没有持久化到磁盘,重启后,这时另一个客户端写入成功,这时有两个客户端持有锁
                    
                    且没有持久化到磁盘,重启后,这时另一个客户端写入成功,这时有两个客户端持有锁
                     解决
                     
                   
                      开启aof备份,并且记录每条每次指令都立即备份
                      
                     
                       性能差
                      
                     
                      延时重启,延时的时间大于设置的key过期时间
                      
                    
                       如果大部分master挂了,延时重启会造成期间服务不可用
                      
                     
                    Java实现
                    
                  
                     org.redisson.RedissonRedLock
                    
                   
                  redis锁是AP模型
                  
                
                   所以在集群架构下由于数据的一致性问题导致极端情况下出现多个线程抢占到锁的情况很难避免
                   
                  
                 
               Zookeeper
               
              
                实现原理
                
                
               
               
                 临时有序节点 + watcher机制监听删除事件
                 
                
                
                 流程
                 
                
                  创建临时有序节点,并监听上一个节点的删除事件(序号最小的就是锁持有者)
                  
                 
                
                Curator实现
                
              
                 InterProcessMutex
                 
                
                  分布式可重入排它锁
                 
                
                 InterProcessSemaphoreMutex
                 
                
                  分布式排它锁
                 
                
                 InterProcessReadWriteLock
                 
               
                  分布式读写锁
                 
                
               两者对比
               
             
                性能消耗
                
               
                 redis锁:需要不断尝试获取锁,消耗性能
                
                
                 zk锁:只需要创建临时有序节点排队,监听上一个节点的删除事件即可,性能损耗小
                
               
                公平性
                
               
                 redis锁:非公平锁
                
                
                 zk锁:公平锁
                
               
                锁释放
                
               
                 获取到redis锁的服务宕机,必须等到锁过期释放
                
                
                 zk只要客户端断开连接,就会删除节点,无需等待
                
               
                CAP
                
              
                 zk是cp
                 
                
                  保证锁不会被多方持有,基于这方面考量,zk锁更优
                 
                
                 redis是ap
                 
               
                  不能保证一致性,那么锁可能会被多个客户端持有
                 
                
              分布式ID
              
              
             
               SnowFlake
               
              
                分段说明
                
                
                
               
               
                时间回拨
                
                
              
                 服务器时钟可能会因为各种原因发生不准,而网络中会提供 NTP 服务来做时间校准,
                 
因此在做校准的时候,服务器时钟就会发生时钟的跳跃或者回拨问题
                
                因此在做校准的时候,服务器时钟就会发生时钟的跳跃或者回拨问题
                 产生相同的id或者异常
                 
                
                
                 解决
                 
                 
               
                  美团Leaf
                  
                 
                   根据最大容忍回拨时间,小于则等待时间回拨后再生成id,大于则异常
                   
                  
                 
                  百度uid-generator
                 
                 
                  滴滴Tinyid
                 
                
               UUID
               
              
                优点
                
                
               
                 生成速度快、简单易用
                 
                
               
                缺点
                
                
              
                 长度较大,无序,不太适合作为数据库主键
                 
                
                
                 存在重复的可能
                 
                
                
                 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置
                 
                
               
               Redis#incr
               
             
                优点
                
                
               
                 性能不错,并且id自增
                 
                
               
                aof & 同步刷盘防止因为丢失数据生成重复id
                
               
              
              分布式Session
              
              
             
               集群模式下,在一台机器创建session,因为负载均衡请求别的机器没有session
               
              
              
               解决
               
               
             
                不使用session:JWT
                
               
               
                spring-session + redis
               
              
              微服务组件
              
              
            
               网关
               
              
              
               配置中心
               
              
              
               注册中心
               
              
              
               服务调用
               
              
              
               限流&降级
               
              
              
               熔断器
               
              
              
               负载均衡
               
              
              
               链路追踪&监控
               
              
              
               分布式事务
               
              
             
             限流
             
             
            
              限流
              
              
             
               算法
               
              
                固定时间窗口
                
               
                 优点:实现简单,易于理解
                
                
                 缺点:存在
                 
                  临界问题
                 
                 -两个窗口交界请求数可大于阈值而不被限流
                
               
                滑动时间窗口
                
                
               
                 将固定窗口划分n个小窗口,每次大窗口向后滑动一个小窗口,并保证大的窗口内流量不会超出最大值
                 
                
                
                 缺点:无法处理突发请求
                
               
                漏桶算法
                
               
                 超出漏桶的请求被拒绝,漏桶内的请求以
                 
                  
                   恒速
                  
                 
                 从漏口流出
                
                
                 优点
                 
                
                  恒定的速率处理请求,避免系统的过载和过度闲置
                 
                
                 缺点
                 
               
                  对请求进行缓存,消耗内存
                 
                 
                  恒定速率处理请求,对于突发流量影响用户体验,比如秒杀
                 
                 
                  难以动态调整桶的大小
                 
                
                令牌桶算法
                
              
                 定期往令牌桶中放进一定数量的令牌,请求进来从令牌桶中获取令牌,获取不到则拒绝
                
                
                 优点
                 
                
                  因为桶中预留令牌,可以处理突发流量,适用于高并发场景比如秒杀
                 
                 
                  可动态调整令牌生成的速率
                 
                
                 缺点
                 
               
                  需要预热(预先在桶中放置令牌),否则会导致请求被误杀
                 
                
               被限流的请求可以排队、失败或者走降级逻辑
               
              
             
              熔断
              
             
             
              降级
              
             
             
              组件
              
            
               guava:限流工具类 RateLimiter基于令牌桶
              
              
               sentinel
               
              
                基本使用
               
               
                步骤
                
              
                 定义资源
                
                
                 定义规则
                
               
               hystrix
               
              
              
               Nginx限流模块 limit_req_zone
              
             
             运维
             
             
            
              Linux
              
             
               常用命令
               
              
                CPU飙升,排查
                
                 
               
               - 定位进程: top 找出占用cpu的pid;
 - 定位线程: top -Hp pid 找到cpu消耗最多的线程号;
 - 打印线程号的16进制线程号: printf %x 线程号 ;
 - 定位代码: jstack pid | grep -A 200 16进制线程号
 
                查看磁盘空间
                
               
                 df -h
                
               
                查看内存空间
                
               
                 free -m
                
               
                查看端口占用
                
               
                 lsof -i:端口号
                
               
                查看进程
                
               
                 ps -ef | grep xx
                
               
                查看网络状况
                
               
                 netstat
                
               
                搜索文件位置
                
               
                 find
                 
               
                  find -name 文件名
                  
                 
                   比较慢
                  
                 
                  find 路径 -name 文件名
                  
                
                   路径可以是一部分,加快查找速度
                  
                 
                删除
                
               
                 rm 文件名
                
                
                 rm -r 文件名/文件夹
                
                
                 rm -f -r 文件名/文件夹
                 
               
                  强制删除文件夹或文件,无需确认
                 
                
                vim
                
               
                 vim 文件名
                
                
                 G
                 
                
                  跳转到最后一行
                 
                
                 gg
                 
                
                  跳转到第一行
                 
                
                 page up/page down键
                 
                
                  上一页/下一页
                 
                
                 /关键字
                 
               
                  搜索关键字
                 
                 
                  n
                  
                 
                   下一个搜索结果
                  
                 
                  N
                  
                
                   上一个搜索结果
                  
                 
                grep
                
               
                 grep 关键字 文件名
                 
               
                  -C n 查看关键字前后n行
                 
                 
                  --color 颜色标注
                 
                
                tail
               
               
                结束进程
                
              
                 kill pid
                
               
               文件描述符(FD)
              
             
              Arthas
             
             
              CI/CD工作流
             
             
              Jenkins
              
              
             
               是一个
               
                持续集成、交付、部署
               
               (软件/代码的编译、打包、部署)的基于web界面的平台
              
             
              Docker
              
              
             
               用于构建、分发、运行(Build, Ship and Run)容器的平台和工具
              
             
              k8s
              
              
             
               是一个使用 Docker 容器进行编排的系统,主要围绕 pods 进行工作。
               
Pods 是 k8s 生态中最小的调度单位,可以包含一个或多个容器
              
             Pods 是 k8s 生态中最小的调度单位,可以包含一个或多个容器
              网关
              
            
               Nginx
               
              
                轻量级 / 高性能的反向代理Web 服务器
               
               
                功能
                
                
              
                 web服务器
                 
                 
                
                  用作静态资源(如HTML、CSS、JavaScript、图像等)的Web服务器
                 
                
                 反向代理
                 
                 
                
                  请求nginx然后转发到目标服务器,对于用户来说目标服务器是无感知的
                  
                 
                
                 负载均衡
                 
                 
                
                  轮询
                  
                 
                 
                  权重轮询
                  
                 
                 
                  ip哈希计算
                  
                 
                
                 限流
                 
                 
                
                  令牌桶
                  
                 
                 
                  漏桶
                  
                 
                
                 动静分离
                 
                
                
                 解决跨域
                 
                
                
                 配置SSL证书(https)
                 
                
               
               Kong
               
              
             
             网络&操作系统
             
            
              操作系统
              
               
              
             
             
               内核态&用户态
               
              
                为什么区分内核态&用户态
               
               
                怎么切换
               
              
               DMA
              
              
               内核缓存
              
              
              网络
              
            
               OSI七层结构
               
              
                应用层
               
               
                表示层
               
               
                会话层
               
               
                传输层
               
               
                网络层
               
               
                数据链路层
               
               
                物理层
               
              
               http & https
               
              
                http
                
               
                 什么是http协议
                 
                
                  Hyper Text Transfer Protocol(超文本传输协议)
                 
                 
                  应用层协议,默认端口80
                 
                
                 http报文
                 
                
                  请求报文
                  
                 
                   请求行
                  
                  
                   请求头
                   
                  
                    常见请求头字段
                    
                  
                     Accept
                     
                    
                      可以接收的mime类型
                     
                    
                     Authorization
                     
                    
                      服务器可以对一些资源进行认证保护,如果你要访问这些资源,就要提供用户名和密码,
                      
这个用户名和密码就是在Authorization头中附带的
                     
                    这个用户名和密码就是在Authorization头中附带的
                     Cache-Control
                     
                    
                      可以指定是否使用缓存,缓存存活时间
                     
                     
                      取值
                      
                    
                       no-store
                       
                      
                        不使用缓存
                       
                      
                       no-cache
                       
                      
                        每次发送请求都要去服务器验证一下,如果服务器告诉可以使用缓存,才使用本地缓存
                       
                      
                       pulbic
                      
                      
                       private
                       
                      
                        只有发起请求的浏览器才能缓存
                       
                      
                       max-age=<seconds>
                       
                      
                        指定缓存存储时间
                       
                      
                       ....
                      
                     
                     Connection
                     
                    
                      是否会关闭网络连接
                     
                     
                      取值
                      
                    
                       keep-alive
                       
                      
                        网络连接就是持久的,不会关闭,使得对同一个服务器的请求可以继续在该连接上完成
                       
                       
                        HTTP 1.1默认值
                       
                      
                       close
                       
                     
                        response后马上关闭连接
                       
                       
                        HTTP 1.0默认值
                       
                      
                     Keep-Alive
                     
                    
                      是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数
                     
                    
                     Content-Type
                     
                    
                      请求体中的内容的mime类型
                     
                    
                     Cookie
                    
                    
                     User-Agent
                     
                   
                      用户的浏览器相关信息
                     
                    
                   请求正文
                  
                 
                  响应报文
                  
                
                   状态行
                   
                  
                    常见响应码
                    
                  
                     2xx:成功响应
                     
                    
                      200
                      
                    
                       OK
                       
                     
                        服务器已成功处理了请求
                       
                      
                     3xx:重定向
                    
                    
                     4xx:指客户端错误
                     
                    
                      400
                      
                     
                       Bad Request
                       
                     
                        (错误请求) 服务器不理解请求的语法
                       
                      
                      401
                      
                     
                       Unauthorized
                       
                     
                        需要身份验证后才能获取所请求的内容
                       
                      
                      403
                      
                     
                       Forbidden
                       
                     
                        客户端没有权利访问所请求内容,服务器拒绝本次请求
                       
                      
                      404
                      
                    
                       Not Found
                       
                     
                        服务器找不到所请求的资源
                       
                      
                     5xx:服务端错误
                     
                   
                      500
                      
                     
                       Internal Server Error
                       
                     
                        服务器遇到未知的无法解决的问题
                       
                      
                      502
                      
                     
                       Bad Gateway
                       
                     
                        服务器作为网关且从上游服务器获取到了一个无效的HTTP响应
                       
                      
                      504
                      
                    
                       Gateway Timeout
                       
                     
                        服务器作为网关且不能从上游服务器及时的得到响应返回给客户端
                       
                      
                   响应头
                   
                  
                    常见响应头字段
                    
                  
                     Server
                    
                    
                     Cache-Control
                    
                   
    
                   响应正文
                  
                 
                 特点
                 
                
                  支持B/S模式
                 
                 
                  简单快速
                 
                 
                  灵活
                  
                 
                   允许传输任意类型的数据对象。传输的类型由Content-Type加以标记
                  
                 
                  无连接
                  
                 
                   限制每次连接只处理一个请求,服务器处理完客户端的请求并收到客户端的应答后即断开连接
                  
                  
                   不利于客户端与服务端会话保持,通过cookie和session弥补这个不足
                  
                 
                  无状态
                  
                
                   对事务处理没有记忆能力,意味着如果后续处理需要前面的信息,则必须被重传
                  
                 
                 存在问题
                 
               
                  明文传输,数据可能被窃取
                  
                 
                 
                  内容可能被篡改
                  
                 
                 
                  不验证身份,导致身份可能被伪装
                  
                 
                
                https
                
              
                 什么是https协议
                 
                
                  http + SSL/TLS
                  
                 
                   在http的基础上通过
                   
                    传输加密
                   
                   和
                   
                    身份认证
                   
                   保证安全性
                   
                  
                  
                   SSL
                   
                  
                    Secure Socket Layer(安全套接字层)
                   
                   
                    SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持
                   
                  
                   TLS
                   
                 
                    Transport Layer Security(传输层安全)
                   
                   
                    前身是 SSL,目前广泛使用的是TLS 1.1、TLS 1.2
                   
                  
                  通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密
                 
                 
                  默认端口443
                 
                
                 https传输数据的流程
                
                
                 缺点
                 
               
                  除了三次握手还有ssl握手,响应速度较http慢
                 
                 
                  连接缓存不如HTTP高效,会增加数据开销和功耗
                 
                 
                  CA证书一般不免费
                 
                 
                  SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大
                 
                
               tcp协议
               
              
                一种
                
                 
                  面向连接的
                 
                
                ,
                
                 
                  可靠的
                 
                
                ,基于
                
                 
                  字节流(无界)
                 
                
                的
                
                 
                  传输层
                 
                
                通信协议
               
               
                UDP
                
               
                 无需连接,不可靠的传输协议,支持一对多,多对多
                
                
                 性能好,适合视频通话,直播等允许少量丢包的场景
                
               
                拆包粘包
                
               
                 只会发生在tcp中,不会发生在udp,因为tcp是无界字节流
                
                
                 拆包
                 
                
                  把一个完整的数据包拆分成多个小包进行发送,
                  
而接收端可能无法一次性接收到所有小包,导致
接收到的数据不完整
                 
                而接收端可能无法一次性接收到所有小包,导致
接收到的数据不完整
                 粘包
                 
                
                  把多个数据包粘合在一起一次性发送,而接收端
                  
可能无法正确区分每个数据包,导致接收到的数
据出现错位或混乱
                 
                可能无法正确区分每个数据包,导致接收到的数
据出现错位或混乱
                 解决方案
                 
               
                  添加特殊字符标识包的开始和结束
                 
                 
                  自定义协议,协议头保存包的长度,接收方跟据长度来解析保证消息的完整性
                  
                 
                 
                  基于定长消息,也就是发送端的消息长度是固定的(不足补0),服务端按照
                  
固定长度来解析
                 
                固定长度来解析
                Linux查看tcp连接状态
                
               
                 netstat -napt
                 
                 
                
                
                 linux下可同时建立多少个tcp连接?
                 
               
                  取决于多个因素
                  
                 
                   进程允许的最大文件描述符数
                  
                  
                   内存大小
                   
                  
                    每个tcp连接都是占用内存的
                   
                  
                   服务器临时端口范围
                  
                  
                   服务器IP_TABLES限制
                  
                 
                  理想情况
                  
                
                   总ip数 * 总端口数
                   
                 
                    2的32次方(ip数)×2的16次方(port数)
                   
                  
                tcp的三次握手四次挥手
                
              
                 三次握手(建立连接)
                 
                  
                  
                
                
                  过程
                  
                  
                  
                 
                   1.建立连接时,客户端发送SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认。
                   
2.服务器收到 SYN 包,必须确认客户的 SYN ( ack=i+1 ) , 同时自己也发送一个 SYN 包( SYN=k ) , 即 SYN+ACK 包,此时服务器进入 SYN-RECV 状态。
3.客户端收到服务器的 SYN+ACK 包,向服务器发送确认报 ACK ( ack=k+1 ) , 此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手,客户端与服务器开始传送数据。
                  
                 2.服务器收到 SYN 包,必须确认客户的 SYN ( ack=i+1 ) , 同时自己也发送一个 SYN 包( SYN=k ) , 即 SYN+ACK 包,此时服务器进入 SYN-RECV 状态。
3.客户端收到服务器的 SYN+ACK 包,向服务器发送确认报 ACK ( ack=k+1 ) , 此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手,客户端与服务器开始传送数据。
                  目的
                  
                 
                   保证双方都有发送和接收的能力
                  
                 
                   为什么不是两次、四次握手
                   
                  
                    没有第三次
                    
                   
                     避免历史连接
                    
                    
                     无法知道客户端具有接收能力
                    
                   
                    三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数
                   
                  
                   SYNC攻击
                   
                 
                    大量不同ip地址发送syn,服务端收到后发出ack+syn,但是客户端并不ack,导致服务端的syn接收队列占满,不能再接收正常请求
                    
                   
                   
                    防范
                    
                  
                     缩短服务器接收SYN后的等待时间,但可能影响正常请求
                    
                   
                 四次挥手(断开连接)
                 
                  
                  
                
               
                  过程
                  
                  
                  
                 
                   1.第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
                   
2.第二次挥手: Server 收到 FIN 后,发送一个 ACK 给 Client ,确认序号为收到序号 +1 (与 SYN 相同,一个 FIN 占用一个序号), Server 进入 CLOSE_WAIT 状态。
3.第三次挥手: Server 发送一个 FIN ,用来关闭 Server 到 Client 的数据传送(数据传送完毕), Server 进入 LAST_ACK 状态。
4.第四次挥手: Client 收到 FIN 后, Client 进入 TIME_WAIT 状态,发送一个 ACK 给 Server ,确认序号为收到序号 +1 。等待2MS后,Server 进入 CLOSED 状态,完成四次挥手。
                  
                 2.第二次挥手: Server 收到 FIN 后,发送一个 ACK 给 Client ,确认序号为收到序号 +1 (与 SYN 相同,一个 FIN 占用一个序号), Server 进入 CLOSE_WAIT 状态。
3.第三次挥手: Server 发送一个 FIN ,用来关闭 Server 到 Client 的数据传送(数据传送完毕), Server 进入 LAST_ACK 状态。
4.第四次挥手: Client 收到 FIN 后, Client 进入 TIME_WAIT 状态,发送一个 ACK 给 Server ,确认序号为收到序号 +1 。等待2MS后,Server 进入 CLOSED 状态,完成四次挥手。
                   为什么要四次挥手
                   
                  
                    没有第四次挥手ACK,client可能没收到FIN,而处于一直等待关闭状态。
                    
有第四次的话,服务端没有收到ACK,会重发FIN
                  有第四次的话,服务端没有收到ACK,会重发FIN
                   为什么 TIME_WAIT 等待的时间是 2MSL
                   
                 
                    防止最后一次挥手(ACK)没有被server接收,server没收到ACK则会重新发送FIN
                   
                   
                    2MSL可以让此次连接中的报文段都消失,如果不等待就可能收到上一次连接的旧报文段,造成混乱
                   
                  
               GET & POST请求区别
               
              
                GET一般是用于获取资源,POST一般是用于提交表单
               
               
                GET请求的参数是拼接在URL后面,而POST是放在请求报文的请求体里面
                
               
                 POST通过抓包还是可以看到请求参数的,也是不安全的。https加密安全
                
               
                GET的URL是有长度限制的,POST没有限制
               
               
                ......
               
              
               浏览器输入网址发生了什么
               
              
                DNS域名解析,得到ip地址
               
               
                浏览器向服务器建立TCP连接
                
               
                 三次握手
                
               
                浏览器向服务器发送Http请求
               
               
                服务器处理请求,将结果返回给浏览器
               
               
                断开TCP连接
                
               
                 四次挥手
                
               
                浏览器解析资源渲染页面
               
              
               RPC & HTTP 的区别
               
              
                前者主要是服务于不同计算机应用间的数据通信(屏蔽复杂的通信细节,像调用本地方法)。
                
后者主要是浏览器和服务器之间的数据通信
               
               后者主要是浏览器和服务器之间的数据通信
                前者是一个协议规范,rpc框架需要实现rpc协议,如dubbo。
                
后者是一个可以直接应用的应用层协议
               后者是一个可以直接应用的应用层协议
                rpc的底层通信协议可以用http实现,比如feign
               
              
               Session & Cookie & Token
               
              
                原因:http请求是无状态
               
               
                作用:记录服务器和客户端
                
                 会话状态
                
                的机制
               
               
                Cookie
                
               
                 存储在浏览器,保存服务端
                 
响应的数据,下次请求可以携带这些数据
                
                响应的数据,下次请求可以携带这些数据
                 无法防止CSRF攻击
                
               
                Session
                
               
                 存储在服务端
                
               
                Token
                
              
                 JWT(Json Web Token)
                 
               
                  jwt储存在客户端,一般存在localStorage。服务端不保存
                 
                 
                  解决CSRF攻击
                 
                 
                  格式:xxxxx.yyyyy.zzzzz,分别为头、荷载、签名
                 
                 
                  流程
                  
                
                   秘钥保存在服务端
                  
                  
                   收到客户端的jwt,拿到头和荷载评接后使用秘钥生成签名,如果和jwt中签名一致则验证通过
                  
                 
               IP
               
             
                IP掩码/子网划分/nat/ARP/mac地址/VPN原理等基础概念
               
              
             设计模式
             
            
              七大原则
              
             
               开闭原则
              
              
               依赖倒置原则
              
              
               单一职责原则
              
              
               接口隔离原则
              
              
               迪米特法则
              
              
               里氏替换原则
              
              
               合成复用原则
              
             
              三大类型
              
            
               创建型
               
              
                简单工厂
                
               
                 i
                 
                  f/else
                 
                 : 违背开闭原则
                
               
                工厂方法
                
               
                 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
                
                
                 符合开闭原则
                
               
                抽象工厂
                
               
                 与工厂方法的区别是生产的产品不是一个而是一系列产品
                
               
                单例
                
               
                 好处
                 
                 
                
                  避免频繁的创建和销毁对象
                  
                 
                 
                  避免频繁的gc
                  
                 
                
                 注意点
                 
                
                  保证线程安全
                 
                 
                  私有构造函数
                 
    
                 
                  提供静态对外获取方法
                 
                
                 实现方式
                 
                
                  饿汉式
                  
                 
                   静态成员变量
                  
                  
                   枚举
                  
                 
                  懒汉式
                  
                 
                   静态内部类
                  
                  
                   双重检查锁
                  
                 
                  容器
                  
                 
                   Spring容器
                  
                 
                  ThreadLocal
                  
                
                   线程级别单例
                  
                  
                   应用
                   
                 
                    Mybatis中ErrorContext
                   
                  
                 破坏
                 
               
                  反射调用私有构造器
                  
                 
                   构造器null判断
                  
                 
                  反序列化
                  
                 
                   重写readResolve()
                  
                 
                  克隆
                  
                 
                   重写clone()方法,返回自身
                  
                 
                  不同类加载器
                 
                
                原型
                
               
                 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
                 
                
                  复制,不会执行构造方法
                 
                
                 实现 Cloneable 接口
                
                
                 场景
                 
               
                  对象创建非常复杂,使用原型模式可以快速创建
                 
                
                建造者
               
              
               结构型
               
              
                代理
                
               
                 静态代理
                 
                
                  编译期就生成了代理类
                 
                
                 动态代理
                 
                
                  运行时创建代理类
                 
                
                 好处
                 
               
                  通过代理类去调用目标类,低耦合
                 
                
                装饰器
                
               
                 demo
                
                
                 特点
                 
                
                  不改变原类文件
                 
                 
                  不使用继承
                  
                 
                   继承的替代模式
                  
                 
                  包装对象包裹原对象,动态拓展
                 
                
                 优点
                 
               
                  是继承的有力补充,比继承灵活,可以再不原有对象的情况下动态的给一个对象扩展功能,即插即用
                 
                 
                  使用不同的装饰类及这些装饰类的排列组合,可以实现不同的效果
                 
                 
                  完全符合开闭原则
                 
                
                适配器
                
               
                 不是最初设计,而是用于一种补救。代码复用
                
                
                 形式
                 
               
                  类适配器
                  
                 
                   继承
                   
                  
                    Adapter extends Adaptee implements Target
                   
                  
                   优点
                   
                  
                    使用方便,代码简化
                   
                  
                   缺点
                   
                 
                    高耦合,灵活性低
                   
                  
                  对象适配器
                  
                
                   组合
                   
                  
                    Adapter implements Target
                   
                   
                    Adapter持有Adaptee的引用
                   
                  
                   优点
                   
                  
                    灵活性高、低耦合
                   
                  
                   缺点
                   
                 
                    使用复杂(需要引入对象实例)
                   
                  
                组合(Composite)
                
               
                 又名“部分整体模式”
                
                
                 定义:将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性(对象都拥有相同的接口)。
                
                
                 结构特点
                 
                
                  整体和部分实现相同接口
                  
                 
                   父子文件夹,都可以删除。删除父也会同时删除子文件夹,子文件夹也可以单独删除
                  
                 
                  整体持有部分的集合
                  
                 
                   父文件夹含有子文件夹列表
                  
                 
                  DEMO
                 
                
                 应用场景
                 
               
                  如果你想表示“部分整体”的层次结构,可以使用组合模式
                 
                 
                  如果你想让客户端可以忽略复杂的层次结构,使用统一的方式去操作层次结构中的所有对象,也可以使用组合模式
                 
                
                门面
               
               
                享元
               
               
                桥接
               
              
               行为型
               
             
                模板
                
               
                 它在父类中定义一系列算法的步骤,而将具体的实现都推迟到子类
                
                
                 好处
                 
               
                  代码复用
                 
                 
                  拓展性
                 
                
                策略
                
               
                 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
                
                
                 使用场景
                 
                
                  就是有一系列的可相互替换的算法的时候,我们就可以使用策略模式将这些算法做成接口的实现,
                  
并让我们依赖于算法的类依赖于抽象的算法接口,这样可以彻底消除类与具体算法之间的耦合
                 
                并让我们依赖于算法的类依赖于抽象的算法接口,这样可以彻底消除类与具体算法之间的耦合
                 好处
                 
                
                  开闭原则,避免if-else
                 
                
                 应用
                 
               
                  dubbo的SPI机制
                 
                
                责任链
                
               
                 为请求创建了一个接收者对象的链
                
                
                 好处
                 
                
                  将请求的发送者和处理者解耦。客户端只认识一个Hanlder接口,降低了客户端(即请求发送者)与处理者的耦合度
                 
                 
                  客户端和处理者都不关心职责链的具体结构,而是交给职责链的创造者.也正因为如此,当在职责链中添加处理者的时候,
                  
这对客户端和处理者来说,都是透明的,二者不知道也不必要知道职责链的变化
                 
                这对客户端和处理者来说,都是透明的,二者不知道也不必要知道职责链的变化
                 结构特点:每个处理对象实现相同接口,并且设置next处理对象
                
                
                 场景
                 
               
                  有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
                 
                 
                  在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
                 
                 
                  可动态指定一组对象处理请求
                 
                
                观察者
                
               
                 对象间有一对多的依赖关系,对象发生变动需要通知依赖它的对象
                
                
                 优点
                 
                
                  解耦
                 
                
                 应用
                 
               
                  事件驱动
                 
                 
                  Observer & Observable简单快速的实现观察者模式
                 
                
                状态
               
               
                委派
               
               
                迭代器
               
               
                命令
               
               
                备忘录
               
               
                中介者
               
               
                解析器
               
               
                访问者
               
              
             系统设计
             
            
              秒杀系统设计
              
             
               秒杀特点
               
              
                高并发,瞬间请求量大
               
               
                防止超卖
               
              
               思路
               
              
                系统隔离,防止影响其服务
               
               
                尽可能的拦截掉大部分请求
               
               
                通过缓存,减轻数据库压力
               
               
                限流
               
               
                消息队列异步削峰
               
               
                ......
               
              
               具体实现
               
             
                前端
                
               
                 页面静态化
                
                
                 数据缓存在客户端
                
                
                 限流削峰
                 
               
                  开始前秒杀按钮置灰
                 
                 
                  限制点击秒杀按钮频率
                  
                 
                   过快给予友好提示
                  
                 
                  对恶意请求拦截
                  
                 
                   秒杀答题
                  
                 
                  基于时间片削峰
                  
                
                   秒杀答题
                  
                  
                   支付宝咻一咻
                  
                  
                   微信摇一摇
                  
                 
                后端
                
              
                 限流
                
                
                 集群部署
                
                
                 秒杀系统单独弄一个微服务,使用独立数据库,防止影响其它服务
                
                
                 库存预热到Redis
                 
                
                  集群模式,哨兵,持久化
                 
                 
                  库存提前写入缓存,直接从缓存扣减库存
                 
                 
                  redis事务或者lua脚本,先判断库存再扣减,防止超卖
                 
                 
                  热点商品,超高tps,redis也扛不住
                  
                
                   解决
                   
                  
                    应用层本地缓存库存
                   
                   
                    定时去redis拉取最新库存更新本地缓存
                   
                  
                   缓存不一致,是否导致超卖?
                   
                 
                    不会
                   
                   
                    读的场景可以允许一定的脏数据,因为这里的误判只会导致少量一些原本已经没有库存的下单请求误认为还有库存而已,等到真正写数据(查询redis中是否还有库存)时再保证最终的一致性
                   
                  
                 MQ
                 
                
                  秒杀成功用消息队列异步削峰
                 
                
                 数据库
                 
               
                  读写分离
                 
                 
                  建立合适的索引
                 
                
              多级缓存架构设计
              
             
               http缓存
              
              
               CDN缓存
              
              
               Nginx缓存
              
              
               进程内缓存(JVM)
              
              
               分布式缓存(redis)
              
             
              接口超时处理
              
             
               方案
               
              
                调大timeout
               
               
                重试
               
               
                MQ
               
               
                回滚
               
               
                异步
               
              
               注意
               
             
                幂等
               
    
               
                数据回滚
               
              
              幂等
              
             
               产生原因
               
               
              
                网络抖动,可能重复请求
                
               
               
                一些组件的重试机制:比如mq、nginx、rpc框架等
                
               
               
                用户双击、浏览器回退重复提交、页面重复刷新等
                
               
              
               解决
               
               
             
                前端
                
                
               
                 页面控制:按钮置灰或加载中避免重复提交
                 
                
                
                 提交后重定向到另一个页面
                 
                
               
                后端
                
                
              
                 唯一标识
                 
                
                
                 状态检查
                 
                
               
              系统稳定性保证
             
             
              高并发系统设计
             
             
              MapReduce
             
            
             算法
             
           
              时间 & 空间复杂度
             
             
              数据结构
              
             
               链表
               
              
                单向
                
               
                 数据结构
                
                
                 技巧
                 
                
                  单向链表如何遍历
                 
                 
                  双指针
                 
                 
                  快慢指针
                 
                
                 题目
                 
               
                  反转单向链表
                 
                 
                  环形链表
                 
                 
                  合并两个有序链表
                 
                
                双向
               
              
               二叉树
               
              
                数据结构
               
               
                遍历方式
                
               
                 前序遍历
                 
                
                  递归
                 
                 
                  迭代
                 
                
                 中序遍历
                 
                
                  递归
                 
                 
                  迭代
                 
                
                 后序遍历
                 
                
                  递归
                 
                 
                  迭代
                  
                
                   后序迭代
                  
                  
                   前序迭代(前右左)+ 翻转
                  
                 
                 层序遍历
                
               
                技巧
                
               
                 深度优先搜索
                
                
                 广度优先搜索
                
               
                题目
                
              
                 合并二叉树
                 
                
                  递归
                 
                
                 翻转二叉树
                 
                
                  递归
                 
                
                 对称二叉树
                 
                
                  递归
                 
                 
                  迭代
                 
                
                 二叉树最大深度
                 
               
                  广度优先搜索
                 
                 
                  深度优先搜索
                 
                
               数组
               
              
                技巧
                
               
                 Arrays.sort(int[] nums)
                
               
                数组排序
                
               
                 快速排序
                
                
                 冒泡排序
                
                
                 插入排序(类似扑克)
                
                
                 选择排序
                
                
                 归并排序
                
                
                 堆排序
                
                
                 力扣
                
               
                矩阵
                
              
                 int[][] matrix = new int[m][n]
                 
               
                  m行n列
                 
                 
                  m = matrix.length
                 
                 
                  n = matrix[0].length
                 
                
               队列 & 栈
               
              
                两个队列实现栈
               
               
                两个栈实现队列
               
              
               字符串
              
              
               哈希表
              
              
               堆
               
             
                定义
                
               
                 完全二叉树
                 
               
                  大顶堆
                 
                 
                  小顶堆
                 
                
                一种特殊二叉树,面向排序设计
                
              
                 查找平均时间复杂度o(n)
                
                
                 有序二叉树面向搜索设计,查找平均时间复杂度o(logn)
                
               
              基础技巧
              
             
               二分
               
              
                二分查找
               
              
               双指针
               
              
                判断子序列
               
               
                环形链表
               
              
               贪心
               
              
                跳跃游戏
               
              
               递归
              
              
               分治
              
              
               滑动窗口
               
             
                无重复字符的最长子串
               
               
                长度最小的子数组
               
              
              搜索算法
              
             
               回溯
              
              
               深度优先遍历
              
              
               广度优先遍历
              
             
              动态规划
              
             
               最长子序和
              
             
              其他
              
              
             
               LRU
               
             
                继承LinkedHashMap
               
               
                Map + 双向链表
               
               
                Map + 队列
               
              
              top100
             
            
              
             
             
              
             
             
              
             
             
              
             
             
              
             
             
              
               0
              
              条评论
             
             
              下一页
              
               
              
             
            
             为你推荐
            
            
             
              查看更多