java面试
2021-10-12 16:55:12   3  举报             
     
         
 AI智能生成
  Java后台 P7面试知识点大全,持续更新
    作者其他创作
 大纲/内容
  java基础    
     String    
     StringBuffer    
     线程安全  
     StringBuilder    
     非线程安全  
     String.intern    
     如果字符串常量池中存在,则返回常量池中的对象,如果不存在则将字符串放入常量池,返回当前对象  
     使用场景    
     字符串有重复的时候,为了避免重复创建,可以指向常量池的同一个  
     集合    
     HashTable  
     HashMap    
     JDK1.7    
     数组+链表  
     操作    
     put  
     get  
     zise  
     JDK1.8    
     null元素    
     头结点  
     操作    
     put    
     1、对key进行hashcode  
     2、hash表是否为空,如果为空则初始化hash表  
     3、计算hashcode所在位置有没有数据,如果没有数据则直接放置  
     4、如果有数据,则判断key是否相等,如果相等则直接替换  
     5、如果不同则判断是否为红黑树  
     6、如果是红黑树,则直接插入树  
     7、如果是链表,则遍历链表,添加到最后  
     8、判断链表长度是否大于8,如果大于8则转为红黑树  
     9、如果元素个数大于阈值,则进行扩容  
     get    
     链表  
     红黑树  
     resize    
     1、将容量扩充为2倍  
     2、将原始hash表的元素重新hash放置在新的hash表中  
     原理    
     数组+链表+红黑树  
     参数    
     capitail  
     loadfactor  
     lambda  
     面向对象    
     封装  
     继承  
     多态  
     架构图    
     子主题  
     反射    
     反射创建对象  
     反射调用方法  
     反射获取属性值  
     原理    
     JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。  
     实现方式    
     Class clazz1 = Class.forName("全限定类名");  
     Class clazz2 = User.class;  
     Class clazz3 = p.getClass();  
     优缺点    
     在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。  
     反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;  
     反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。  
     finalize    
     描述    
     保证对象在回收前完成特定资源回收  
     Java9中以标记为过时  
     问题    
     使用不当会造成死锁和效率低的问题  
     替代方案    
     Cleaner替换    
     利用幻象引用和引用队列,我们可以保证对象被彻底销毁前做一些类似资源回收的工作,比如关闭文件描述符(操作系统有限的资源),它比 finalize 更加轻量、更加可靠  
     它有自己的运行线程,所以可以避免意外死锁等问题  
     并发编程    
     线程    
     线程状态    
     New  
     Runnable  
     Blocking  
     watting  
     Terminated  
     子主题  
     实现    
     new Thread  
     implement Runnable  
     implement Callable  
     操作    
     线程协作    
     fork  
     join  
     wait  
     notify  
     notifyall  
     线程切换    
     sleep  
     yield  
     线程互斥    
     synchornized    
     JVM提供  
     锁升级,性能一般情况下没有问题  
     不能实现公平锁  
     不可中断  
     reentrantLock    
     JDK实现  
     可以对多个Condition加锁  
     可以实现公平锁  
     可中断  
     线程安全    
     三大特性    
     原子性  
     可见性  
     有序性  
     一致性原理    
     MESI协议    
     E(exclusive)、M(modified)、S(shared)、I(invalid)  
     描述    
     主存与私有内存的通知与同步  
     常见实现    
     synchorize    
     锁状态    
     无锁  
     偏向锁  
     轻量级锁  
     重量级锁  
     优点    
     JDK的实现,比较简单  
     能保证原子性、可见性、有序性  
     缺点    
     不能自定义锁的状态  
     不支持公平锁  
     使用    
     对象  
     类  
     静态变量  
     原理    
     monitorEnter、monitorExit  
     对象结构    
     对象头    
     子主题  
     元素数据  
     对齐填充  
     valitile    
     特性    
     不完全原子性    
     原子操作能保证原子性  
     可见性    
     内存屏障    
     LoadLoad  
     StoreStore  
     StoreLoad  
     LoadStore  
     有序性    
     happens-before语义    
     as if seriazes  
     start  
     监视器规则  
     传递性  
     join规则  
     原理lock  
     final    
     禁止重排序,解决可见性问题  
     实现方式    
     互斥同步    
     synchronized、ReentrantLock  
     非阻塞同步    
     CAS、AtomicXXX  
     线程私有    
     ThreadLoal    
     ThreadLocalMap    
     原理    
     主要是用到了Thread对象中的一个ThreadLocalMap类型的变量threadLocals, 负责存储当前线程的关于XXX的对象, Key, 以新建的XXX对象为Value; 这样的话, 线程第一次读取的时候如果不存在就会调用ThreadLocal的initialValue方法创建一个XXX对象并且返回  
     问题    
     key为null时会出现内存泄漏  
     set 、remove  
     实现案例    
     CountDownLatch  
     CyclicBarrier  
     Semaphore  
     JUC    
     Lock    
     ReentrantLock    
     优点    
     可多个Condition条件  
     可中断  
     基于JDK实现的,操作更灵活  
     可实现公平锁  
     AQS    
     CountDownLatch    
     允许一个或多个线程等待某些操作完成  
     不可以重置的,所以无法重用  
     基本操作组合是 countDown/await  
     关注的是事件  
     CyclicBarrier    
     一种辅助性的同步结构,允许多个线程等待到达某个屏障  
     基本操作组合,则就是 await,当所有的伙伴(parties)都调用了 await,才会继续进行任务,并自动进行重置。  
     关注的是线程  
     Semaphore    
     信号量  
     ,其基本逻辑基于 acquire/release  
     原理    
     将基础的同步相关操作抽象在 AbstractQueuedSynchronizer 中  
     private volatile int state;  
     一个先入先出(FIFO)的等待线程队列(使用Node的双向链表来实现),以实现多线程间竞争和等待  
     基于 CAS 的基础操作方法,以及各种期望具体同步结构去实现的 acquire/release 方法。  
     排他锁与共享锁    
     独占模式下有线程获取到许可的过程就是state值从0=>1的过程,只有获取到许可的线程进行,重入的操作state值会从1=>2,在共享模式下当调用countDown()方法时,就是一个线程获取到一个许可,state的值-1,只有当许可消耗完,也就是state=0时才会恢复队列中挂起的线程。
  
     两个虚拟队列: 同步队列sync queue 和条件队列condition queue,  
     AQS内部维护了一个CLH队列来管理锁。线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列sync queue里。 接着会不断的循环尝试获取锁,条件是当前节点为head的直接后继才会尝试。如果失败就会阻塞自己直到自己被唤醒。而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。  
     LockSupport    
     park  
     unpark  
     Collection    
     BlockingQueue    
     ConcurrentLinkedQueue    
     基于 lock-free(无锁),在常见的多线程访问场景,一般可以提供较高吞吐量  
     LinkedBlockingQueue    
     基于锁,并提供了 BlockingQueue 的等待性方法  
     改进了锁操作的粒度,头、尾操作使用不同的锁,所以在通用场景下,它的吞吐量相对要更好一些  
     ArrayBlockingQueue    
     源码分析  
     SynchronousQueue    
     这是一个非常奇葩的队列实现,每个删除操作都要等待插入操作,反之每个插入操作也都要等待删除动作。那么这个队列的容量是多少呢?是 1 吗?其实不是的,其内部容量是 0。  
     PriorityBlockingQueue    
     是无边界的优先队列,虽然严格意义上来讲,其大小总归是要受系统资源影响。  
     CurrentHashMap    
     原理    
     分离锁  
     内部使用volatile的value字段来保证可加性  
     使用CAS操作,保证无锁并发操作  
     put    
     如果key或者value为null,则抛出空指针异常;
如果table为null或者table的长度为0,则初始化table,调用initTable()方法。
计算当前键值的索引位置,如果Hash表中当前节点为null,则将元素直接插入。(注意,这里使用的就是前面锁说的CAS操作)
如果当前位置的节点元素的hash值为-1,说明这是一个ForwaringNodes节点,即正在进行扩容。那么当前线程加入扩容。
当前节点不为null,对当前节点加锁,将元素插入到当前节点。在Java8中,当节点长度大于8时,就将节点转为树的结构。
addCount
是否需要扩容
    如果table为null或者table的长度为0,则初始化table,调用initTable()方法。
计算当前键值的索引位置,如果Hash表中当前节点为null,则将元素直接插入。(注意,这里使用的就是前面锁说的CAS操作)
如果当前位置的节点元素的hash值为-1,说明这是一个ForwaringNodes节点,即正在进行扩容。那么当前线程加入扩容。
当前节点不为null,对当前节点加锁,将元素插入到当前节点。在Java8中,当节点长度大于8时,就将节点转为树的结构。
addCount
是否需要扩容
 get     
如果该位置为 null,那么直接返回 null 就可以了
如果该位置处的节点刚好就是我们需要的,返回该节点的值即可
如果该位置节点的 hash 值小于 0,说明正在扩容,或者是红黑树,后面我们再介绍 find 方法
如果以上 3 条都不满足,那就是链表,进行遍历比对即可
      
    如果该位置为 null,那么直接返回 null 就可以了
如果该位置处的节点刚好就是我们需要的,返回该节点的值即可
如果该位置节点的 hash 值小于 0,说明正在扩容,或者是红黑树,后面我们再介绍 find 方法
如果以上 3 条都不满足,那就是链表,进行遍历比对即可
 size    
     private transient volatile long baseCount;
  
     @sun.misc.Contended 
static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
    static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
 在计算 size 的时候,会将 baseCount 和 CounterCell 数组中的元素的 value 累加,得到总的大小,但这个数字仍旧可能是不准确的。  
     ConcurrentSkipListMap  
     CopyOnWriteList    
     任何修改操作,如 add、set、remove,都会拷贝原数组,修改后替换原来的数组,通过这种防御性的方式,实现另类的线程安全。  
     比较适合读多写少的操作  
     Atomic    
     实现原理    
     CAS+volatile 依赖于 Unsafe 提供的一些底层能力  
     常见实现    
     原子更新基本类型    
     AtomicBoolean  
     AtomicInteger  
     AtomicLong  
     原子更新数组    
     AtomicIntegerArray  
     AtomicLongArray  
     AutomicReferenceArray  
     原子更新引用类型    
     AtomicReference  
     AtomicStampedReference  
     AtomicMarkableReferce  
     原子更新字段类型    
     AtomicIntegerFieldUpdater  
     AtomicLongFieldUpdater  
     AtomicStampedFieldUpdater  
     AtomicReferenceFieldUpdater  
     Executor    
     原理    
     workSet    
     存放线程  
     workQueue    
     存放任务  
     使用    
     优点    
     提高执行效率,不用每次创建和销毁  
     提高系统吞吐量  
     ThreadPoolExecutor    
     参数    
     corePoolSize    
     CPU密集型    
     CPU核数+1  
     IO密集型    
     CPU核数 × 目标CPU利用率 ×(1 + 平均等待时间/平均工作时间)  
     maximumPoolSize  
     keepAliveTime  
     unit    
     时分秒  
     BlockingQueue workQueue    
     ArrayBlockingQueue  
     SynchronousQueue    
     不存储元素的阻塞队列  
     LinkedBlockingQueue    
     基于链表的阻塞队列  
     PriorityBlockingQuene    
     有优先级的队列  
     RejectedExecutionHandler    
     直接丢弃    
     DiscardPolicy  
     丢弃老的    
     DiscardOldestPolicy  
     调用当前线程执行任务    
     CallerRunsPolicy  
     异常(默认)    
     AbortPolicy  
     Executor方式    
     newFixedThreadPool(固定大小)    
     LinkedBlockingQueue    
     基于数组的有界阻塞队列,Integer.MAX_VALUE  
     newSingleThreadExecutor(一个线程)
    
     LinkedBlockingQueue    
     基于数组的有界阻塞队列,Integer.MAX_VALUE  
     newCachedThreadPool(Integer最大数的线程)    
     SynchronousQueue  
     ScheduleThreadPoolExecutor    
     为任务提供延迟或周期执行  
     原理    
     DelayedWorkQueue  
     ScheduledFutureTask  
     newWorkStealingPool    
     ForkJoinPool.defaultForkJoinWorkerThreadFactory  
     锁    
     种类    
     自旋锁    
     CAS    
     问题    
     ABA问题    
     AtomicStampedReference解决ABA问题  
     只能保证一个变量的原子操作  
     循环开销时间长  
     java中的实现    
     Unsafe  
     可重入锁、不可重入锁    
     可重复可递归调用的锁    
     ReentrantLock、Synchorized  
     公平锁、非公平锁    
     ReentrantLock  
     独占所、共享锁    
     ReentrantLock  
     ReentrantReadWriteLock  
     乐观锁、悲观锁    
     CAS  
     ReentrantLock、synchorized  
     分段锁    
     ConcurrentHashMap  
     偏向锁、轻量级锁、重量级锁    
     synchorized  
     读写锁、互斥锁    
     ReentrantReadWriteLock  
     JVM    
     结构图    
     子主题  
     java对象    
     对象头    
     运行时元数据(mark word)    
     hash地址  
     GC分代年龄  
     锁状态位  
     类型指针    
     指向元空间中对象所属的类型  
     实例数据    
     类中定义的成员变量    
     规则:先放父类的成员变量,在放子类的成员变量。相同宽度的变量被放在一起。如果compactFileds参数为true,子类的窄变量可能插入到父类变量的空隙  
     对其填充    
     缓存行是CUP与内存交互的最小工作单元,按块进行处理的,每块为64位处理器每次处理 8Byte(64bit)  
     JMM    
     内存模型    
     主内存  
     本地内存  
     重排序  
     缓存一致性原理  
     过程    
     加载    
     类加载器    
     AppClassLoader    
     jre/lib  
     ExtClassLoader    
     ext  
     BootstrapClassLoader    
     classpath  
     自定义类加载器    
     自定义类加载的方式  
     作用    
     加载非classpath下的文件  
     加载网络传输的class  
     加载需要加解密的class  
     实现热部署功能  
     加载机制    
     双亲委派机制    
     不会自己先加载,会交给父加载器加载  
     缓存机制    
     加载后的类会放入缓存,如果缓存中已有则不加载  
     父类委托机制    
     优先让父类进行加载,只有父类无法加载的时候,自己再加载  
     全盘负责    
     当一个类被加载是,该类依赖的其他类也由该类加载  
     如何打破双亲委派    
     重写loadClass  
     加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。  
     加载方式    
     从本地系统中直接加载  
     通过网络下载.class文件  
     从zip,jar等归档文件中加载.class文件  
     链接    
     验证    
     验证class文件的正确性  
     准备    
     为类的静态变量准备内存  
     解析    
     在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。  
     初始化    
     静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。  
     普通原始类型静态变量和引用类型(即使是常量),是需要额外调用 putstatic 等 JVM 指令的,这些是在显式初始化阶段执行,而不是准备阶段调用;而原始类型常量,则不需要这样的步骤。  
     使用  
     卸载  
     结构    
     线程公用    
     堆    
     新生代    
     Eden    
     Survivor    
     from  
     to  
     Thread Local Allocation Buffer(TLAB)    
     多线程同时分配内存时,为避免操作同一地址,可能需要使用加锁等机制,进而影响分配速度  
     结构    
     start  
     top  
     end  
     Old    
     放置长生命周期的对象,通常都是从 Survivor 区域拷贝过来的对象。  
     我们知道普通的对象会被分配在 TLAB 上;如果对象较大,JVM 会试图直接分配在 Eden 其他位置上;如果对象太大,完全无法在新生代找到足够长的连续空闲空间,JVM 就会直接分配到老年代。  
     老年代  
     用来放置 Java 对象实例,几乎所有创建的 Java 对象实例都是被直接分配在堆上  
     方法区    
     存储所谓的元(Meta)数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。  
     实现    
     1.8以前永久代  
     1.8以后元空间  
     线程私有    
     本地方法栈    
     本地方法的调用  
     在 Oracle Hotspot JVM 中,本地方法栈和 Java 虚拟机栈是在同一块儿区域,这完全取决于技术实现的决定,并未在规范中强制。  
     虚拟机栈    
     一次次的 Java 方法调用  
     结构    
     局部变量表  
     操作数(operand)栈  
     动态链接  
     方法正常退出或者异常退出的定义  
     程序计数器    
     存储当前线程正在执行的 Java 方法的 JVM 指令地址  
     元空间    
     堆外内存    
     Native Memory Tracking (NMT)分析  
     垃圾回收算法    
     原理    
     可达性分析    
     问题    
     循环引用  
     引用计数器    
     引用类型    
     强引用    
     不会被回收  
     软引用    
     内存不够时才会被回收  
     弱引用    
     一定会被回收  
     虚引用    
     虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。  
     问题    
     并发情况下,需要保证同步,性能低    
     加锁(效率低)  
     Lock Free(复杂)  
     循环引用(导致垃圾泄漏)  
     引起连锁回收    
     对象引用的对象也会回收,导致效率低  
     GC Roots    
     虚拟机栈和本地方法栈中正在引用的对象、静态属性引用的对象和常量  
     算法基础    
     复制算法    
     原理    
     将存活的对象Copy到另一块内存  
     优点    
     无内存碎片  
     实现简单,通过指针后移即可  
     新生代效率高  
     问题    
     浪费一半内存空间    
     S0/S1,可配置 -XX:SurvivorRatio  
     会有停顿  
     实现方式    
     引用指针    
     Forwarding指针  
     图的DFS    
     BFS和DFS的区别是什么    
     CPU空间利用率  
     标记-清除算法    
     原理    
     使用链表管理所有的空闲区域,将不存活的对象所占用的内存交还给链表  
     freelist  
     特点    
     内存利用率高  
     可以用很小的代价实现并发标记和清除  
     问题    
     有内存碎片    
     标记-整理算法  
     标记-整理算法    
     标记 - 清除,但为避免内存碎片化,它会在清理过程中将对象移动,以确保移动后的对象占用连续的内存空间。  
     主要因素    
     分配效率  
     回收效率  
     是否产生内存分片  
     空间利用率  
     是否停顿  
     时间复杂度  
     实现的复杂度  
     调优    
     JVM参数    
     -Xms  
     -Xmx  
     -Xss
  
     -XX:NewRatio  
     -XX:SurvivorRatio  
     工具    
     JConsole  
     JMap  
     Arthas  
     垃圾收集器           
     Serial 收集器    
     新生代  
     STW  
     一个线程串行  
     复制算法  
     ParNew 收集器    
     多个线程并行,减少STW时间  
     Parallel Scavenge 收集器    
     可控制吞吐量  
     Serial Old 收集器    
     标记整理算法  
     Parallel Old 收集器    
     目标:优先吞吐量  
     CMS 收集器    
     目标:减少停顿时间  
     实现:标记清除算法    
     初始标记    
     只标记根节点    
     不在堆上的引用    
     栈上的引用  
     全局变量等  
     虚拟机的引用  
     并发标记    
     漏标问题    
     CMS解决办法    
     解决办法:三色标记    
     往前进    
     黑引用白,直接将白标灰  
     会有垃圾  
     往后退    
     黑引用白,将黑变灰  
     重新标记    
     remark    
     把剩余的标记  
     如果年轻代太大,比较耗时,需要先进行一次yong gc  
     并发清除  
     问题    
     内存碎片,会增多FullGC  
     多CPU竞争,会与用户线程抢占CPU资源  
     G1 收集器    
     分块GC    
     目标    
     一种兼顾吞吐量和停顿时间  
     Region 之间是复制算法,但整体上实际可看作是标记 - 整理(Mark-Compact)算法,可以有效地避免内存碎片,尤其是当 Java 堆非常大的时候,G1 的优势更加明显。  
     G1 的很多开销都是源自 Remembered Set,例如,它通常约占用 Heap 大小的 20% 或更高,这可是非常可观的比例。并且,我们进行对象复制的时候,因为需要扫描和更改 Card Table 的信息,这个速度影响了复制的速度,进而影响暂停时间。  
     分区类型    
     Eden  
     Surviver  
     Old  
     H  
     挑战    
     跨区引用    
     跨区引用    
     Point In(专属记录集)    
     问题:可能会变得很大    
     多    
     粗粒度表(整个region)  
     中    
     细粒度表(bitmap)  
     少    
     稀疏表(hash表)  
     Point Out(通用记录集)  
     记录集    
     Remember Set    
     用于记录和维护 region 之间对象的引用关系。垃圾回收的主力  
     合理选择回收区域    
     根据停顿时间,计算需要回收的块  
     优先回收垃圾多的,才能提高吞吐量  
     同时需要考虑分代  
     兼顾大对象分配  
     其他概念    
     Remember Set    
     用于记录和维护 region 之间对象的引用关系。垃圾回收的主力  
     card table    
     JVM将内存划分成了固定大小的Card。这里可以类比物理内存上page的概念。  
     主要因素    
     分配效率  
     回收效率  
     是否产生内存分片  
     空间利用率  
     是否停顿  
     时间复杂度  
     实现的复杂度  
     JVM调优思路    
     明确需求:是吞吐量还是延迟还是内存占用  
     找到具体的垃圾回收器    
     jstat  
     考虑硬件  
     重复完成分析、调整、验证这个过程。  
     开发框架    
     Spring    
     AOP    
     实现方式    
     Cglib动态代理(默认)    
     面向类,通过字节码增强实现  
     JDK动态代理    
     面向接口,通过JDK反射实现,性能差  
     IoC    
     控制反转,是一种思想,不用亲力亲为,用的时候去获取即可。  
     设计模式    
     单例模式  
     工厂模式    
     BeanFactory    
     ApplicationContext    
     XMLApplicationContext  
     ClasspathApplicationContext  
     代理模式    
     ***Proxy  
     监听者模式    
     Observer  
     模板方法模式    
     JdbcTemplate  
     Springboot    
     原理    
     SpringBootApplicatin    
     SpringBootConfiguration    
     @Configuration
  
     EnableAutoConfiguration    
     @AutoConfigurationPackage
  
     Import    
     ImportSelect  
     Registar  
     spring.factories  
     Configuration  
     ComponentScan  
     优点    
     1、上手容易、开发快捷  
     2、开箱即用,远离繁琐的配置  
     3、提供了一系列大型项目通用的非业务性功能  
     4、避免大量的 Maven 导入和各种版本冲突  
     特性    
     EnableAutoConfiguration 自动装配  
     Starter 启动依赖依赖于自动装配的技术  
     Actuator 监控 , 提供了一些endpoint ,http、jmx形式去进行访问, health信息。 metrics 信息  
     Spring Boot CLI(命令行操作的功能, groovy脚本)客户端, groovy  
     MyBatis  
     Netty    
     高性能原因    
     更加优雅的 Reactor 模式实现、灵活的线程模型、利用 EventLoop 等创新性的机制,可以非常高效地管理成百上千的 Channel。  
     充分利用了 Java 的 Zero-Copy 机制    
     使用池化的 Direct Buffer 等技术,在提高 IO 性能的同时,减少了对象的创建和销毁;利用反射等技术直接操纵 SelectionKey,使用数组而不是 Java 容器等。  
     使用更多本地代码。    
     直接利用 JNI 调用 Open SSL 等方式,获得比 Java 内建 SSL 引擎更好的性能。  
     在通信协议、序列化等其他角度的优化。  
     定义    
     它是一个异步的、基于事件 Client/Server 的网络框架,目标是提供一种简单、快速构建网络应用的方式,同时保证高吞吐量、低延时、高可靠性。  
     中间件    
     MySQL    
     整体结构    
     结构图    
     接口层  
     服务层  
     存储层  
     事务    
     特性    
     ACID  
     事务隔离级别    
     读未提交    
     脏读、幻读、不可重复读  
     读已提交    
     脏读、不可重复读  
     可重复读    
     不可重复读(innodb没有)  
     串行化    
     无  
     存储引擎    
     InnoDB    
     结构    
     *.idb  
     特性    
     支持行级锁  
     支持事务  
     主键递增  
     ACID实现原理    
     A原子性    
     undo log    
     当事务对数据库进行修改时,InnoDB 会生成对应的 undo log  
     结构    
     row transation_id  
     data_roll_ptr  
     db_row_id  
     C一致性    
     事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障  
     I隔离性    
     多个事务之间的隔离    
     写隔离    
     LBCC    
     意向锁、排它锁  
     读隔离    
     MVCC    
     read view  
     D持久性    
     redo log    
     产生的原因    
     InnoDB 作为 MySQL 的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘 IO,效率会很低。
为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:
当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 Buffer Pool。当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool 的使用大大提高了读写数据的效率,但是也带来了新的问题:如果 MySQL 宕机,而此时 Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
于是,redo log 被引入来解决这个问题:当数据修改时,除了修改 Buffer Pool 中的数据,还会在 redo log 记录这次操作;当事务提交时,会调用 fsync 接口对 redo log 进行刷盘。
  
    为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:
当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 Buffer Pool。当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool 的使用大大提高了读写数据的效率,但是也带来了新的问题:如果 MySQL 宕机,而此时 Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
于是,redo log 被引入来解决这个问题:当数据修改时,除了修改 Buffer Pool 中的数据,还会在 redo log 记录这次操作;当事务提交时,会调用 fsync 接口对 redo log 进行刷盘。
 利用顺序IO,避免写入数据的随机IO性能低的问题  
     索引    
     类型    
     聚簇索引    
     含有数据内容  
     非聚簇索引    
     不含有数据内容  
     原理    
     B+Tree    
     减少层级  
     效率平衡  
     性质    
     最左匹配原则  
     索引要尽量小,这样可以一次可以加载更多数据  
     MyISAM  
     问题    
     不支持sharding    
     CAT  
     ShardingJDBC  
     为什么要分库分表    
     一、数据库瓶颈    
     不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,
进而逼近甚至达到数据库可承载活跃连接数的阈值。
在业务Service来看就是,可用数据库连接少甚至无连接可用。
接下来就可以想象了吧(并发量、吞吐量、崩溃)
    
    进而逼近甚至达到数据库可承载活跃连接数的阈值。
在业务Service来看就是,可用数据库连接少甚至无连接可用。
接下来就可以想象了吧(并发量、吞吐量、崩溃)
 IO瓶颈    
     第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,
每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表
  
    每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表
 第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库  
     CPU瓶颈    
     第一种:SQL问题,如SQL中包含join,group by,order by,
非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,
在业务Service层进行业务计算
  
    非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,
在业务Service层进行业务计算
 第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,
CPU率先出现瓶颈 -> 水平分表
  
    CPU率先出现瓶颈 -> 水平分表
 二、分库分表    
     1、水平分库           
     概念    
     以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中  
     结果    
     每个库的结构都一样  
     每个库的数据都不一样,没有交集  
     所有库的并集是全量数据  
     场景    
     系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库  
     分析    
     库多了,io和cpu的压力自然可以成倍缓解  
     2、水平分表           
     概念    
     以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中  
     结果    
     每个表的结构都一样  
     每个表的数据都不一样,没有交集  
     所有表的并集是全量数据  
     场景    
     系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,
加重了CPU负担,以至于成为瓶颈。推荐:一次SQL查询优化原理分析
  
    加重了CPU负担,以至于成为瓶颈。推荐:一次SQL查询优化原理分析
 分析    
     表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担  
     3、垂直分库           
     概念    
     以表为依据,按照业务归属不同,将不同的表拆分到不同的库中  
     结果    
     每个库的结构都不一样  
     每个库的数据也不一样,没有交集  
     所有库的并集是全量数据  
     场景    
     系统绝对并发量上来了,可以抽象出单独的业务模块
  
     分析    
     到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,
这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,
这时可以将相关的表拆到单独的库中,甚至可以服务化
  
    这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,
这时可以将相关的表拆到单独的库中,甚至可以服务化
 4、垂直分表           
     概念    
     以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中  
     结果    
     每个表的结构都不一样  
     每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据  
     所有表的并集是全量数据  
     场景    
     系统绝对并发量并没有上来,表的记录并不多,
但是字段多,并且热点数据和非热点数据在一起,
单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,
查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈
  
    但是字段多,并且热点数据和非热点数据在一起,
单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,
查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈
 分析    
     可以用列表页和详情页来帮助理解。
垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,
非热点数据放在一起作为扩展表。
这样更多的热点数据就能被缓存下来,进而减少了随机读IO。
拆了之后,要想获得全部数据就需要关联两个表来取数据。
但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。
关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据
  
    垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,
非热点数据放在一起作为扩展表。
这样更多的热点数据就能被缓存下来,进而减少了随机读IO。
拆了之后,要想获得全部数据就需要关联两个表来取数据。
但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。
关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据
 三、分库分表工具    
     sharding-sphere:jar,前身是sharding-jdbc  
     TDDL:jar,Taobao Distribute Data Layer  
     Mycat:中间件  
     四、分库分表问题    
     1、非partition key的查询问题
基于水平分库分表,拆分策略为常用的hash法
    
    基于水平分库分表,拆分策略为常用的hash法
 端上除了partition key只有一个非partition key作为条件查询    
     映射法           
     基因法           
     注:写入时,基因法生成user_id,如图。关于xbit基因,例如要分8张表,23=8,故x取3,即3bit基因。
根据user_id查询时可直接取模路由到对应的分库或分表。
根据user_name查询时,先通过user_name_code生成函数生成user_name_code再对其取模路由到对应的分库或分表。
id生成常用snowflake算法
  
    根据user_id查询时可直接取模路由到对应的分库或分表。
根据user_name查询时,先通过user_name_code生成函数生成user_name_code再对其取模路由到对应的分库或分表。
id生成常用snowflake算法
 端上除了partition key不止一个非partition key作为条件查询    
     映射法           
     冗余法           
     注:按照order_id或buyer_id查询时路由到db_o_buyer库中,按照seller_id查询时路由到db_o_seller库中。
感觉有点本末倒置!有其他好的办法吗?改变技术栈呢?
  
    感觉有点本末倒置!有其他好的办法吗?改变技术栈呢?
 后台除了partition key还有各种非partition key组合条件查询    
     NoSQL法           
     冗余法           
     2、非partition key跨库跨表分页查询问题
基于水平分库分表,拆分策略为常用的hash法
    
    基于水平分库分表,拆分策略为常用的hash法
 注:用NoSQL法解决(ES等)  
     3、扩容问题
基于水平分库分表,拆分策略为常用的hash法
    
    基于水平分库分表,拆分策略为常用的hash法
 水平扩容库(升级从库法)           
     注:扩容是成倍的  
     水平扩容表(双写迁移法)           
     第一步:(同步双写)修改应用配置和代码,加上双写,部署;   
     第二步:(同步双写)将老库中的老数据复制到新库中;   
     第三步:(同步双写)以老库为准校对新库中的老数据;   
     第四步:(同步双写)修改应用配置和代码,去掉双写,部署;  
     数据库优化思路    
     架构优化    
     主从、分库分表、添加缓存  
     SQL优化    
     SQL语句优化    
     只搜索需要的字段  
     优先使用ID查询  
     条件使用索引  
     索引优化    
     联合索引、索引的离散度分析  
     数据库优化    
     表设计优化    
     适当冗余  
     配置优化    
     连接数优化  
     Redis    
     数据结构    
     常用结构    
     String    
     数据结构    
     raw  
     int  
     embstr  
     适用场景  
     Hash    
     数据结构    
     ziplist    
     子主题  
     hashtable  
     适用场景    
     对象,更省空间  
     List    
     数据结构    
     quicklist  
     适用场景    
     消息队列  
     Set    
     数据结构    
     hashtable  
     intset    
     原理  
     适用场景    
     签到、点赞  
     ZSet    
     数据结构    
     ziplist    
     原理    
     只记录长度不记录内容  
     skiplist    
     实现简单  
     以空间换时间  
     高度是随机的  
     为什么使用红黑树    
     当我们插入或删除节点时,会移动节点进行平衡操作,这导致在并发场景中难以进行合理粒度的同步。  
     SkipList 结构则要相对简单很多,通过层次结构提高访问速度,虽然不够紧凑,空间使用有一定提高(O(nlogn)),但是在增删元素时线程安全的开销要好很多。  
     适用场景    
     排行榜  
     子主题  
     进阶结构    
     Geo  
     Bitmap    
     使用场景    
     布隆过滤器  
     Hayperloglog    
     使用场景    
     基数统计    
     每日访问用户  
     UV等  
     适用场景    
     分布式锁  
     分布式缓存  
     客户端    
     Redisson    
     封装了通用的数据结构  
     Jedis    
     命令齐全  
     单线程,线程安全问题,线程池  
     Lutuce    
     线程安全  
     集群    
     主从    
     主从同步    
     bgsave  
     主从数据同步    
     连接  
     同步  
     命令传播  
     Redis Sentinel    
     server  
     Sentinel    
     服务下线    
     Raft算法  
     故障转移  
     Redis Cluster    
     数据分片    
     16384个slot,每个节点管理一些slot。  
     一致性hash    
     虚拟节点,避免单点问题  
     亮点    
     高性能    
     ZeroCopy  
     事件驱动  
     单线程  
     数据结构简单  
     丰富的数据类型  
     持久化    
     AOF    
     优点    
     没1s记录一次日志  
     缺点    
     恢复慢    
     命令聚合  
     文件大    
     数据压缩  
     模式    
     always    
     append到缓冲区以后调用系统fsync操作同步到aof文件中,fsync操作完成后主线程返回;  
     no    
     不对aof文件做fsync同步,同步到硬盘操作由操作系统负责,通常同步周期最长30秒  
     everysec    
     表示命令写入aof缓冲区后调用操作系统write操作,write操作完成后主线程返回,由专门的线程每秒去进行fsync同步文件操作  
     优化    
     日志重写  
     日志压缩  
     RDB    
     优点    
     文件小    
     LZF压缩算法  
     恢复快  
     缺点    
     丢失数据大,不能做到秒级  
     每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;  
     单线程    
     执行命令是单线程的,其他过程是多线程的  
     Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。  
     处理网络数据的读写和协议的解析,而执行命令依旧是单线程  
     特性    
     支持事务    
     MULTI  
     EXEC  
     DISCARD  
     WATCH  
     其他    
     内存淘汰策略    
     原理    
     LRU    
     redis原理    
     链表+HashMap实现  
     从所有的key中随机选择N个(N可以配置)要么从所有的设置了过期时间的key中选出N个键,然后再从这N个键中选出最久没有使用的一个key进行淘汰。所以准确度不高,但是执行效率高!  
     设置lru的值,不是获取的当前时间的时间戳,而是设置的为全局的server.lruclock的值。  
     这里值得注意的是全局时钟只有24位,按秒为单位来表示才能存储194天,所以可能会出现key的时钟大于全局时钟的情况,如果这种情况出现那么就两个相加而不是相减来求最久的key。  
     LFU  
     RANDOM  
     分类    
     allkeys-random  
     allkey-lru  
     volatile-ttl    
     将要过期的key  
     volatile-lru  
     volatile-random  
     volatile-ttl  
     noeviction    
     当内存使用超过配置的时候会返回错误,不会驱逐任何键  
     内存过期    
     主动过期    
     每个设置过期时间的key都会创建一个定时器,到期后立即清除。这个方案的问题是会有太多的定时器,比较消耗CPU。  
     惰性过期(使用)    
     只有当访问一个key的时候,才会判断这个key是否已经过期,过期则删除。这个方案的问题是如果有大量的过期key没有被访问,岂不是一直占用着内存空间。  
     被动过期(使用)    
     每隔一段时间扫描数据库中的expires字典中的key,清除掉已经过期的。可以看到这是一个折中方案,是在CPU和内存中取得了一个平衡。  
     缓存风险    
     缓存穿透    
     解决办法    
     缓存null  
     加锁,只能一个线程访问  
     缓存雪崩    
     解决办法    
     随机过期时间,不要同时过期  
     预热加载  
     缓存击穿    
     解决办法    
     加锁同步  
     缓存污染    
     解决办法    
     写操作:删缓存、修改数据、删缓存  
     线程    
     采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;  
     MQ
    
     Kafka    
     特性    
     高拓展  
     高性能    
     顺序写  
     批量写  
     ZeroCopy  
     异步处理  
     原理    
     Broker  
     Topic    
     partions  
     适用场景  
     亮点  
     集群    
     ISR    
     In-Sync Replicas 能够和 leader 保持同步的 follower + leader本身 组成的集合  
     OSR    
     Out-Sync Relipcas 不能和 leader 保持同步的 follower 集合  
     AR  
     使用场景    
     消息丢失    
     Producer    
     ack=all  
     Broker  
     Consumer    
     手动ack  
     消息顺序    
     写入到同一个Partition,一个Consumer消费  
     消息重复    
     Producer    
     开启ProducerId  
     Consumer    
     幂等支持  
     消息事务    
     引入TC,进行2PC提交  
     RocketMQ    
     原理    
     Broker  
     NameServer  
     Producer  
     Consumer  
     Topic    
     queue  
     MessageQueue  
     ConsumerQueue  
     commit.log  
     使用场景    
     有序消息    
     放到同一个queue  
     事务消息           
     延时消息           
     1 所有的延迟消息到达broker后,会存放到SCHEDULE_TOPIC_XXX的topic下(这个topic比较特殊,对客户端是不可见的,包括使用rocketmq-console,也查不到这个topic)
2 SCHEDULE_TOPIC_XXX这个topic下存在18个队列,每个队列中存放的消息都是同一个延迟级别消息
3 broker端启动了一个timer和timerTask的任务,定时从此topic下拉取数据,如果延迟时间到了,就会把此消息发送到指定的topic下,完成延迟消息。
    2 SCHEDULE_TOPIC_XXX这个topic下存在18个队列,每个队列中存放的消息都是同一个延迟级别消息
3 broker端启动了一个timer和timerTask的任务,定时从此topic下拉取数据,如果延迟时间到了,就会把此消息发送到指定的topic下,完成延迟消息。
 所有的延迟消息由producer发出之后,都会存放到同一个topic(SCHEDULE_TOPIC_XXXX)下,不同的延迟级别会对应不同的队列序号,当延迟时间到之后,由定时线程读取转换为普通的消息存的真实指定的topic下,此时对于consumer端此消息才可见,从而被consumer消费。  
     消息丢失    
     Broker    
     同步刷盘  
     异步刷盘  
     Producer    
     ack  
     Consumer    
     手动回执  
     问题    
      消息积压    
     Producer    
     限流    
     减少生产  
     Consumer    
     扩容    
     Consumer与ConsumerQueue一一对应,如果Queue不够多,则扩容也无解  
     丢弃    
     将一些日志消息丢弃  
     RabbitMQ    
     原理  
     消息类型    
     fanout  
     direct  
     topic  
     headers  
     使用场景    
     消息丢失  
     ES    
     子主题  
     一致性算法    
     PecificA  
     倒排索引    
     什么是倒排索引    
     正排索引根据ID查找内容,根据内容查找ID  
     实现    
     分词  
     索引  
     相关概念  
     MongoDB    
     非关系型数据库    
     无固定的结构、灵活拓展  
     基于文档  
     优点    
     支持全文搜索,地理位置索引  
     水平伸缩性  
     自动分片部署数据到机器集群的功能  
     数据压缩    
     自从MongoDB 3.0推出以后,MongoDB引入了一个高性能的存储引擎WiredTiger,并且它在数据压缩性能上得到了极大的提升,跟之前的MMAP引擎相比,压缩比至少可增加5倍以上,可以极大地改善磁盘空间使用率。  
     缺点    
     不支持事务  
     不支持join  
     Clickhouse    
     优点    
     真正的面向列的 DBMS  
     数据压缩  
     磁盘存储的数据  
     天然支持分布式  
     数据不仅按列存储,而且由矢量 - 列的部分进行处理,这使开发者能够实现高 CPU 性能  
     原理  
     缺点    
     没有完整的事务支持,  
     缺少完整的Update/Delete操作,缺少高频率、低延迟的修改或删除已存在数据的能力,仅能用于批量删
除或修改数据
    除或修改数据
 Hbase    
     特点    
     大:一个表可以有上亿行,上百万列。  
     面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。  
     稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。  
     无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可以有截然不同的列。  
     数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时的时间戳。  
     数据类型单一:HBase中的数据都是字符串,没有类型。  
     promethus  
     架构    
     微服务    
     网关    
     原理  
     目标    
     轻量  
     安全  
     稳定  
     实现    
     Kong  
     Zuul2.0  
     SpringCloudGateway  
     服务发现    
     服务注册    
     zookeeper  
     nacos  
     服务发布  
     服务治理    
     服务容错    
     FailFast  
     服务限流    
     原理    
     令牌桶策略  
     漏斗策略  
     实现    
     单体实现    
     基于Guava实现  
     集群实现    
     基于Lua实现  
     成熟方案    
     sentinel  
     服务降级  
     服务监控    
     度量(Metrics)    
     Promethus  
     告警    
     Alter-Manager  
     日志(Logging)    
     Graylog  
     链路(Trace)    
     Pinpoint  
     其他    
     Skywalking  
     框架    
     dubbo    
     架构    
     分支主题  
     角色    
     Regisger  
     Provider  
     Consumer  
     Monitor    
     异步线程,定时存储  
     核心功能    
     服务提供、服务调用、连接处理、通信协议、序列化方式、服务发现、服务路由、日志输出  
     容错机制    
     failover    
     失败自动切换到其他服务器(默认)  
     failfast     
     快速失败,立即报错  
     failsafe    
     失败安全,直接忽略  
     failback    
     失败后记录下来,定时重试  
     forking    
     并行调用多个,一个成功就成功  
     thrift  
     负载均衡    
     计算机协议    
     四层协议    
     F5  
     七层协议    
     Nginx  
     LVS  
     HAProxy  
     算法    
     随机  
     轮训  
     加权轮训  
     最小连接数  
     hash  
     日志分析    
     应用日志输出    
     日志规范gelf规范  
     日志规范    
     命名规范  
     路径规范  
     滚动策略  
     日志格式  
     日志收集    
     Filebeat  
     缓冲    
     Kafka  
     加工聚合    
     Graylog  
     存储    
     ES  
     查询分析    
     Graylog  
     分布式    
     分布式理论    
     CAP    
     解释    
     一致性C  
     可用性A  
     分区容错性P  
     实现    
     CP  
     AP  
     BASE    
     解释    
     基本 B  
     可用 S  
     软状态 S  
     最终 E  
     表现形式    
     因果一致性  
     读己之所读  
     会话一致性  
     一致性理论    
     2PC    
     原理    
     准备阶段(Prepare)  
     提交阶段(commit)  
     缺点    
     协调者单点问题  
     数据不一致问题  
     阻塞问题  
     3PC    
     原理    
     准备阶段(Prepare)  
     尝试阶段(canCommit)  
     提交阶段(doCommit)  
     缺点    
     数据不一致问题  
     实现复杂  
     分布式一致性算法    
     Paxos    
     Basic Paxos  
     Multi Paxos    
     Raft    
     原理  
     使用案例    
     Redis Sentinel  
     Etcd  
     Console  
     ZAB    
     原理  
     使用案例    
     Zookeeper  
     Gossip    
     原理  
     使用案例    
     Redis Cluster  
     分布式事务    
     理论基础    
     XA    
     资源管理器RM  
     事务管理器TM  
     应用  
     实现    
     2PC    
     问题    
     协调者单点问题  
     同步阻塞问题  
     一致性问题  
     3PC    
     问题    
     单点问题  
     效率问题  
     实现方式    
     SEATA    
     描述    
     全局事务协调器  
     SAGA    
     描述    
     基于数据补偿替换回滚  
     优缺点    
     实现复杂  
     HalfMessage    
     描述    
     本地事务发起,发送一个MQ消息,本地事务提交则提交这个MQ消息,消费端可消费  
     优缺点    
     实现简单  
     最终一致,可能会出现不一致  
     依赖中间件  
     TCC    
     描述    
     Try,Commit,Cancel  
     优缺点    
     优点:    
     用户操作灵活  
     缺点:    
     代码耦合度高  
     分布式锁    
     实现方案    
     基于mysql    
     描述    
     基于MySQL的锁  
     缺点    
     单点问题  
     基于Redis    
     原理    
     基于redis的setnx expire  
     缺点    
     需要考虑超时情况  
     释放锁没有通知,需要客户端自旋  
     setnx与expire非原子操作,可能会出现没有过期的情况  
     基于Zookeeper    
     原理    
     基于ZK的临时节点,事件通知  
     缺点    
     每次要持久化数据,效率低  
     死锁的条件    
     解决办法    
     1、有序分配  
     2、银行家算法  
     条件    
     不可剥夺  
     循环等待    
     设置超时  
     互斥  
     请求和保持    
     无法获取就释放  
     分布式ID    
     要求    
     全局唯一  
     自动递增  
     高可用  
     实现    
     Redis  
     Zookeeper  
     mongodb  
     mysql  
     算法    
     Snowflake  
     计算机基础    
     IO模型    
     分类    
     阻塞  
     非阻塞  
     同步  
     异步  
     形式    
     BIO  
     NIO    
     实现    
     Channel  
     Buffer  
     Selector  
     多路复用模型    
     Reactor模式  
     AIO  
     ZeroCopy    
     概念    
     用户态  
     内核态  
     所谓0Copy是指减少从内核态到用户态的Copy  
     原理    
     mmap    
     减少一次copy,但是还有一次上下文切换  
     适用于小数据量读写  
     使用场景    
     RocketMQ  
     sendfile    
     减少一次上下文切换  
     适用于大文件传输  
     可用于DMA方式,减少CPU拷贝  
     使用场景    
     Kafka  
     IO多路复用    
     原理  
     实现    
     select  
     poll  
     epoll  
     (1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
    (2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
 网络    
     HTTP    
     子主题  
     TCP/IP  
     操作系统    
     IO    
     随机IO    
     需要多次磁道寻址  
     顺序IO  
     SSD与机械硬盘    
     SSD使用电学信号,寻址更快  
     数据结构与算法    
     算法基础    
     时间复杂度    
     O(1)  
     O(n)    
     图的遍历  
     树的遍历  
     DFS/BFS  
     O(logn)    
     二分查找  
     nO(logn)  
     O(n2)  
     O(n3)  
     O(n!)  
     O(2^n)  
     空间复杂度    
     数组的长度  
     递归的深度  
     主定理    
     二分查找    
     O(logn)  
     二叉树    
     O(n)  
     矩阵    
     O(n)  
     归并排序    
     O(nlogn)  
     练习方法    
     1、5-10分钟阅读题目  
     2、写出实现思路  
     3、尝试去实现  
     4、阅读官方实践,对比实现方案  
     5、反复练习5次  
     数据结构    
     数组    
     数组(Array)    
     add    
     O(logn)  
     remove    
     O(logn)  
     get    
     O(1)  
     问题    
     连续内存  
     固定大小  
     越界  
     链表    
     链表(LinkedList)    
     类型    
     单向链表    
     next  
     双向链表    
     next  
     previous  
     循环链表    
     next  
     操作    
     add    
     O(1)
  
     remove    
     O(1)  
     get    
     O(n)
  
     问题    
     内存分散,不利于CPU缓存  
     容易造成内存碎片  
     使用场景    
     LRU  
     链表反转  
     二叉树    
     结构  
     遍历    
     前序    
     递归    
     public void preOrderTraverse1(TreeNode root) {
if (root != null) {
System.out.print(root.val + "->");
preOrderTraverse1(root.left);
preOrderTraverse1(root.right);
}
}
    if (root != null) {
System.out.print(root.val + "->");
preOrderTraverse1(root.left);
preOrderTraverse1(root.right);
}
}
 非递归    
     public void preOrderTraverse2(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.empty()) {
if (node != null) {
System.out.print(node.val + "->");
stack.push(node);
node = node.left;
} else {
TreeNode tem = stack.pop();
node = tem.right;
}
}
}
    Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.empty()) {
if (node != null) {
System.out.print(node.val + "->");
stack.push(node);
node = node.left;
} else {
TreeNode tem = stack.pop();
node = tem.right;
}
}
}
 中序    
     递归    
     public void inOrderTraverse(TreeNode root) {
if (root != null) {
inOrderTraverse(root.left);
System.out.print(root.val + "->");
inOrderTraverse(root.right);
}
}
    if (root != null) {
inOrderTraverse(root.left);
System.out.print(root.val + "->");
inOrderTraverse(root.right);
}
}
 非递归    
     public void inOrderTraverse(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
TreeNode tem = stack.pop();
System.out.print(tem.val + "->");
node = tem.right;
}
}
}
    Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
TreeNode tem = stack.pop();
System.out.print(tem.val + "->");
node = tem.right;
}
}
}
 后序    
     递归    
     public void postOrderTraverse(TreeNode root) {
if (root != null) {
postOrderTraverse(root.left);
postOrderTraverse(root.right);
System.out.print(root.val + "->");
}
}
    if (root != null) {
postOrderTraverse(root.left);
postOrderTraverse(root.right);
System.out.print(root.val + "->");
}
}
 非递归    
     public void postOrderTraverse(TreeNode root) {
TreeNode cur, pre = null;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.empty()) {
cur = stack.peek();
if ((cur.left == null && cur.right == null) || (pre != null && (pre == cur.left || pre == cur.right))) {
System.out.print(cur.val + "->");
stack.pop();
pre = cur;
} else {
if (cur.right != null)
stack.push(cur.right);
if (cur.left != null)
stack.push(cur.left);
}
}
}
    TreeNode cur, pre = null;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.empty()) {
cur = stack.peek();
if ((cur.left == null && cur.right == null) || (pre != null && (pre == cur.left || pre == cur.right))) {
System.out.print(cur.val + "->");
stack.pop();
pre = cur;
} else {
if (cur.right != null)
stack.push(cur.right);
if (cur.left != null)
stack.push(cur.left);
}
}
}
 AVL  
     红黑树  
     Trie    
     原理  
     使用场景  
     常用算法  
     散列表  
     算法    
     算法思想    
     贪心算法    
     原理  
     使用场景  
     分治算法  
     回溯算法  
     动态规划  
     排序    
     快速排序  
     计数排序  
     冒泡排序     
// 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
 
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
}    
    // 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
}
 特点    
     稳定  
     O(n2)  
     思想    
     后一个元素和前一个元素进行比较  
     图解    
     子主题  
     插入排序    
     实现方式     
// 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}    
    // 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}
 优化方式    
     希尔排序    
     实现方式    
     public static void shellSort(int[] arr) {
int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}
    int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}
 特点    
     稳定  
     O(n2)  
     思想    
     找到比指定元素小的元素位置进行替换  
     图解    
     子主题  
     选择排序    
     思想    
     找到最小的元素放在最前面  
     图解    
     子主题  
     特点    
     不稳定  
     O(n2)  
     算法    
     算法思想    
     贪心算法    
     原理  
     使用场景  
     分治算法  
     回溯算法  
     动态规划  
     排序    
     快速排序  
     计数排序  
     冒泡排序     
// 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
 
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
}    
    // 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
}
 特点    
     稳定  
     O(n2)  
     思想    
     后一个元素和前一个元素进行比较  
     图解    
     子主题  
     插入排序    
     实现方式     
// 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}    
    // 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}
 优化方式    
     希尔排序    
     实现方式    
     public static void shellSort(int[] arr) {
int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}
    int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}
 特点    
     稳定  
     O(n2)  
     思想    
     找到比指定元素小的元素位置进行替换  
     图解    
     子主题  
     选择排序    
     思想    
     找到最小的元素放在最前面  
     图解    
     子主题  
     特点    
     不稳定  
     O(n2)  
     堆排序  
     桶排序  
     堆排序  
     递归  
     迭代  
     广度优先  
     深度优先  
     项目    
     AB号项目    
     核心功能    
     号码绑定    
     号码调度  
     号码解绑    
     手动解绑  
     自动解绑    
     延迟消息  
     续绑    
     AXB续绑    
     先取消,然后再绑定  
     XB续绑    
     凌晨续绑  
     ElasticJob任务续绑  
     技术栈    
     MySQL数据存储  
     Redis缓存    
     绑定关系  
     RocketMQ    
     事件通知  
     延迟消息解绑  
     ElasticJob    
     数据状态  
     话单数据  
     续绑  
     背景    
     自如寓接听率底下,且无法知道中间状态,以及接听率底下的原因  
     需求:提升接听率,且将数据线上化  
     项目成果    
     1、接听率提升60%-90%  
     2、数据线上化、一键切换供应商,保证业务高可用,绑定不上用原号码兜底,且异常日志输出会第一时间知道具体原因  
     2、Saas平台化建设多个业务线使用  
     3、技术平台化建设,延迟消息平台    
     延迟消息原理    
     producer要将一个延迟消息发送到某个Topic中
Broker判断这是一个延迟消息后,将其通过临时存储进行暂存。
Broker内部通过一个延迟服务(delay service)检查消息是否到期,将到期的消息投递到目标Topic中。这个的延迟服务名字为delay service,不同消息中间件的延迟服务模块名称可能不同。
消费者消费目标topic中的延迟投递的消息
    Broker判断这是一个延迟消息后,将其通过临时存储进行暂存。
Broker内部通过一个延迟服务(delay service)检查消息是否到期,将到期的消息投递到目标Topic中。这个的延迟服务名字为delay service,不同消息中间件的延迟服务模块名称可能不同。
消费者消费目标topic中的延迟投递的消息
 业务大盘项目    
     核心功能    
     日志平台    
     收集阶段:日志规范SDK    
     日志格式:gelf  
     滚动策略  
     文件名称  
     采集阶段    
     Filebeat  
     http上报  
     处理阶段  
     流转阶段:kafka    
     一个业务线一个topic  
     存储阶段:ES  
     查询阶段:graylog    
     对整个日志做了管理,包括采集、流转和存储  
     FlinkSQL平台    
     日志分析  
     FlinkSQL平台  
     数据图表  
     技术栈    
     ES  
     Kafka    
     子主题  
     Flink    
     基本原理    
     state    
     MapState  
     ListState  
     BroadcastState  
     window    
     Tumbling Windows(不重复)  
     Sliding Windows(重复的)  
     Session Windows(会话窗口)  
     自定义窗口  
     time:乱序数据处理    
     处理时间 Processing Time  
     事件时间 Event Time  
     提取时间 Ingestion Time  
     实现watermark的机制  
     checkpoint    
     为了保证一致性  
     Exactly-Once    
     子主题  
     基本概念           
     Client  
     JobManager  
     TaskManager  
     Client 用来提交任务给 JobManager,JobManager 分发任务给 TaskManager 去执行,然后 TaskManager 会心跳的汇报任务状态。  
     功能开发    
     数据源拓展  
     自定义函数  
     clickhouse    
     优点    
     完备的DBMS功能  
     列式存储与数据压缩    
     按列存储与按行存储相比,前者可以有效减少查询时所需扫描的数据量  
     按列存储相比按行存储的另一个优势是对数据压缩的友好性  
     子主题  
     向量化执行引擎    
     向量化执行,可以简单地看作一项消除程序中循环的优化。  
     关系模型与SQL查询    
     关系模型相比文档和键值对等其他模型,拥有更好的描述能力,也能够更加清晰地表述实体间的关系。  
     多样化的表引擎  
     多主架构  
     多线程与分布式  
     数据分片与分布式查询  
     项目成果    
     日志平台  
     FlinkSQL平台  
     背景    
     日志查询分散,无统一的平台  
     日志接收有延迟,导致告警数据有延迟  
     数据实时分析需求  
     在线客服项目    
     项目主要功能  
     子主题  
    
 
 
 
 
  0 条评论
 下一页