something just like this


解决问题:
1、正则表达式,导致cpu飙升,拖慢了其他业务响应。
2、redis aof,导致redis连接超时,进程假死,jetty连接数爆了。
3、updateagent批量插入优化:
a)    目标库建表时先不加索引,导入完成后添加索引。
b)    尝试改造程序逻辑,保留多线程从uds获取,减少insert线程数,增加mysql顺序写磁盘,记录性能表现。
c)    减少通信次数,增加单次写入量,insert单次包大小保持在32M左右。
d)    Mysql配置max_allowed_packet默认1M,改为64M,innodb_log_buffer_size 默认8M改为64M,innodb_buffer_pool_size默认128M不变。
e)    观察JVM情况,调整XmX  Xms  newRation等值,记录GC表现。
f)    代码优化,字符拼接、大对象的使用等优化。

==================================================
浅拷贝:仅仅克隆基本类型变量,而不克隆引用类型的变量
深克隆:既克隆基本类型变量,也克隆引用类型变量
    
    a.重写clone方法,clone目标对象中的引用对象
    @Override
    public Object clone() throws CloneNotSupportedException {
        //注意以下代码
        Teacher teacher = (Teacher)super.clone();
        teacher.setStudent((Student)teacher.getStudent().clone());
        return teacher;
    }

    b.使用序列化也能完成深复制:对象序列化后写入流中,再从流中读取,生成新的对象。非侵入的,不需要修改目标代码就可以实现
    //将对象写到流里
    ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
    ObjectOutputStream objOut=new ObjectOutputStream(byteOut);
    objOut.writeObject(father);
    //从流里读出来
    ByteArrayInputStream byteIn=new ByteArrayInputStream(byteOut.toByteArray());
    ObjectInputStream objInput=new ObjectInputStream(byteIn);
    fatherCopy = (Son) objInput.readObject()
==================================================

伪共享:
1、CPU在工作时,将对象从主存一级一级读至寄存器(l3、l2、l1、寄存器)
2、Core1改了a对象中一个x变量,需要将Core2缓存中a对象置无效,下次Core2再操作a.y变量时,还需要重新加载缓存行
3、多核CPU交替修改同一个对象不同变量,就会不停的装载缓存,即伪共享

应对:
缓存填充,一个对象填满64字节(缓存行大小),如long p1,p2,p3,p4,p5,p6,p7。jdk8字段注解:@Contended

==================================================
synchronized(非公平、可重入),jdk1.6优化:无锁-》偏向锁-》轻量级自旋锁-》重量级锁

java对象:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)
java对象头:MarkWord(对象的HashCode,分代年龄、3个级别的锁标志位信息)+klassPointer(指向类元数据的指针)

偏向锁:上个线程优先获得,仅判断是否为上个线程threadId。线程不挂起。
轻量级锁:偏向锁未获得,当前线程不挂起,开始自旋,cas判断是否获得锁。缺点:自旋cas消耗cpu。
重量级锁:自旋未获得锁,升级成为重量级锁(操作系统的mutex lock,线程挂起:内核态->用户态)。对象的monitor指针,指向新的线程。


synchronized与Lock区别:
synchronized是关键字,是JVM层面的底层啥都帮我们做了,而Lock是一个接口,是JDK层面的有丰富的API。
synchronized会自动释放锁,而Lock必须手动释放锁。
synchronized是不可中断的,Lock可以中断也可以不中断。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。 
synchronized能锁住方法和代码块,而Lock只能锁住代码块。 
Lock可以使用读锁提高多线程读效率。 
synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。

==================================================
hashmap、concurrentHashmap---------------------------------------

concurrentHashmap1.8,put:
根据 key 计算出 hashcode 。
判断是否需要进行初始化。
f 即为当前 key 定位出的 Node,如果为空,利用 CAS 尝试写入,失败则自旋保证成功。
如果当前位置的 hashcode == -1,则需要进行扩容。数组扩容因子0.75,每次扩容为原来2倍:保证2的幂次方(查找快:与操作,散列均衡)
如果node有值,则利用 synchronized 锁写入数据。
如果链表数量大于 TREEIFY_THRESHOLD(默认为8) 则要转换为红黑树。


hashmap1.8 做了什么优化?
数组+链表 -》 数组+链表+红黑树,链表头插法改为尾插法,链表长度>8转为红黑树


ConcurrentHashMap 是如何实现的? 1.7、1.8 实现有何不同?为什么这么做?
entry改为node,segment+reentrantlock  改为 node + CAS(当前node为空,cas写入,失败则自旋) + synchronized(当前node有值,替换,加锁) 来保证并发安全性

==========================================
Disruptor相对于传统方式的优点:
1、ringbuffer(内存读取,缓存行预加载) +  两个volatile(内存屏障)读指针、写指针,
2、没有竞争=没有锁=非常快。
3、所有访问者都记录自己的序号的实现方式,允许多个生产者与多个消费者共享相同的数据结构。
4、在每个对象中都能跟踪序列号(ring buffer,claim Strategy,生产者和消费者),加上神奇的cache line padding,就意味着没有为伪共享和非预期的竞争。
==========================================
jvm---------------------------------
堆 -Xmx -Xms 
线程共享,新生代(eden 满了youngGC通常是俩s区的10倍大、survivor0 、survivor1)、老生代 满了fullGC

栈  -Xss
线程独有,局部变量表、操作栈、动态连接、方法返回地址

程序计数器
线程独有,字节码行号指示器,唯一不会OOM的区域

本地方法栈
线程独有,大量本地方法出现时,势必会削弱JVM对系统的控制力  如 System.currentTimeMillis()

方法区/永久代(JDK1.8:元数据区)
元空间在本地内存中分配,只要本地内存足够,它不会出现类似永久代的java.lang.OutOfMemoryError: PermGen space
线程共享,类信息,常量,静态变量,即时编译器(JIT)编译后的代码等数据
区满时也会引发Full GC,会导致Class、Method元信息的卸载

==========================================
关键字final的好处小结
final关键字提高了性能。JVM和Java应用都会缓存final变量。
final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
使用final关键字,JVM会对方法、变量及类进行优化。
对于不可变类,它的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销。

==========================================

在Spring中循环依赖处理分为3种情况

1. 构造器循环依赖(无法解决)
因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
2. setter循环依赖(可以解决)
解决方式:Spring容器提前暴露刚完成构造器注入,但未完成属性注入(setter方法)的bean来完成的。
3. prototype范围的依赖处理(无法解决)
因为spring容器不进行缓存prototype作用域的bean

spring对象三级缓存:
三级:singletonFactories : 单例对象工厂的cache 
二级: earlySingletonObjects :提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥】
一级: singletonObjects:单例对象的cache

==========================================

fail-fast与fail-safe有什么区别?
    Java.util包中的所有集合类都被设计为fail-fast的,而java.util.concurrent中的集合类都为fail-safe的。
    Fail-fast迭代器抛出ConcurrentModificationException,不克隆对象;而fail-safe迭代器从不抛出ConcurrentModificationException,会克隆对象。
==========================================
GC-----------
标准GC情况:
Minor GC执行时间不到50ms;
Minor GC执行不频繁,约10秒一次;
Full GC执行时间不到1s;
Full GC执行频率不算频繁,不低于10分钟1次;
eden:survivor = 10:1,因为99%的eden区对象,都会在第一次创建后销毁,留下1%在MinorGC时进入survivor区,所以大约10次MinorGC,survivor区才满。
吞吐量优先:Parallel Scavenge收集器(复制算法),-XX:+UseAdaptiveSizePolicy ,新生代大小、eden/survivor比例,交给jvm完成。新生代尽量大(NewRatio=1) ,大量短期对象在新生代中回收。
相应时间优先:CMS收集器(标记清除算法)
G1(标记整理算法):将new、old都划分区域,每次回收垃圾最多的区域,兼顾了响应时间、吞吐量

==========================================
jvm监控命令-------------------------------
jstack:
死锁,Deadlock(重点关注)!!  资源争抢互斥
阻塞,Blocked(重点关注)    !!等待资源超时
等待资源,Waiting on condition(重点关注)!!等待资源(网络IO、分布式锁等)
等待获取监视器,Waiting on monitor entry(重点关注)!!等待资源(Class的锁)    
暂停,Suspended
对象等待中,Object.wait() 或 TIMED_WAITING
停止,Parked
执行中,Runnable

jmap
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况. 
-histo[:live] 打印每个class的实例数目,内存占用,类全名信息.
-dump:生成java堆转储快照

jstat
-class:统计class loader行为信息 
-compile:统计编译行为信息 
-gc:统计jdk gc时heap信息 
-gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况 
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件 
-gcnew:统计gc时,新生代的情况 
-gcnewcapacity:统计gc时,新生代heap容量 
-gcold:统计gc时,老年区的情况 
-gcoldcapacity:统计gc时,老年区heap容量 
-gcpermcapacity:统计gc时,permanent区heap容量 
-gcutil:统计gc时,heap情况 ,输出参数内容 
    S0  — Heap上的 Survivor space 0 区已使用空间的百分比 
    S1  — Heap上的 Survivor space 1 区已使用空间的百分比 
    E   — Heap上的 Eden space 区已使用空间的百分比 
    O   — Heap上的 Old space 区已使用空间的百分比 
    P   — Perm space 区已使用空间的百分比 
    YGC — 从应用程序启动到采样时发生 Young GC 的次数 
    YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒) 
    FGC — 从应用程序启动到采样时发生 Full GC 的次数 
    FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒) 

top -H -p [PID] 
最耗cpu的线程--》转16进制--》去jstack中找

==========================================
jdk动态代理、cglib动态代理
一、原理区别:
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP ,也可以强制使用CGLIB
2、如果目标对象没有实现接口,必须采用CGLIB实现
==========================================
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。

==========================================
 mmap
 在Unix/Linux系统下读写文件,一般有两种方式。

一种是open一个文件,这里就涉及到了数据的两次拷贝:磁盘->内核,内核->用户态。

而且当存在多个进程同时读取同一个文件时,每一个进程中的地址空间都会保存一份副本,这样肯定不是最优方式的,造成了物理内存的浪费。
 第二种方式就是使用内存映射的方式。具体操作方式是:open一个文件,然后调用mmap系统调用,将文件的内容的全部或一部分直接映射到进程的地址空间,映射完成后,进程可以像访问普通内存一样做其他的操作,比如memcpy等等。mmap并不分配物理地址空间,它只是占有进程的虚拟地址空间。这跟第一种方式不一样的,第一种方式需要预先分配好物理内存,内核才能将页高速缓冲中的文件数据拷贝到用户进程指定的内存空间中。

==========================================
AQS    
AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。AQS 定义了两种资源共享方式:
1.Exclusive:独占,只有一个线程能执行,如ReentrantLock
2.Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch

AQS使用一个整数state以表示状态,并通过getState、setState及compareAndSetState等protected类型方法进行状态转换。巧妙的使用state,可以表示任何状态,如:
ReentrantLock用state表示所有者线程已经重复获取该锁的次数。
Semaphore用state表示剩余的许可数量。
CountDownLatch用state代表CountDownLatch所剩余的计数次数。
FutureTask用state表示任务的状态,如尚未开始、正在运行、已完成、已取消。

CountDownLatch:
计数器,某个线程等待N个线程执行完毕后再继续执行,N就是对应的计数,每个执行完毕就减一,直到所有完成。可以看作是1个等多个。
CyclicBarrier:
循环计数器,一般是N个线程相互执行等待,所有执行完毕再继续下一步动作。可以看作是n个互相等待。
Semaphore:
信号量,一般是获取到信号量的执行某个动作,完毕后释放,其他继续获取。类似锁。它的作用是限制某段代码块的并发数

==========================================

ThreadLocal 原理:
每个Thread类中有两个变量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部类ThreadLocalMap类型的变量,它是一个 Map(key,value)数据格式,key 是一个弱引用,也就是 ThreadLocal 对象本身,而 value 存的是线程变量的值。


1.ThreadLocal 的内存泄露是怎么回事?
ThreadLocal 在 ThreadLocalMap 中是以一个弱引用身份被 Entry 中的 Key 引用的,因此如果 ThreadLocal 没有外部强引用来引用它,那么 ThreadLocal 会在下次 JVM 垃圾收集时被回收。这个时候 Entry 中的 key 已经被回收,但是 value 又是一强引用不会被垃圾收集器回收,这样 ThreadLocal 的线程如果一直持续运行(线程池中的线程,任务结束后又被重复利用),value 就一直得不到回收,这样就会发生内存泄露。

2.为什么 ThreadLocalMap 的 key 是弱引用?
key 使用强引用:这样会导致一个问题,引用的 ThreadLocal 的对象被回收了,但是 ThreadLocalMap 还持有 ThreadLocal 的强引用,如果没有手动删除,ThreadLocal 不会被回收,则会导致内存泄漏。
key 使用弱引用:这样的话,引用的 ThreadLocal 的对象被回收了,由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使没有手动删除,ThreadLocal 也会被回收。value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候会被清除。

==========================================
线程池 ThreadPoolExecutor
corePoolSize:核心线程数(最新线程数),超过了进队列queue
maximumPoolSize:最大线程数,超过这个数量的任务会被拒绝,用户可以通过RejectedExecutionHandler接口自定义处理方式
keepAliveTime:线程保持活动的时间
workQueue:工作队列,存放执行的任务
    SynchronousQueue: 一个无容量的等待队列,一个线程的insert操作必须等待另一线程的remove操作,采用这个Queue线程池将会为每个任务分配一个新线程,内部无AQS,直接CAS操作,吞吐量大:
    LinkedBlockingQueue : *队列,线程池将忽略 maximumPoolSize参数,仅用corePoolSize的线程处理所有的任务,未处理的任务便在LinkedBlockingQueue中排队
    ArrayBlockingQueue: 有界队列
    吞吐量实践:
        以SynchronousQueue作为参数,使maximumPoolSize发挥作用,以防止线程被无限制的分配,同时可以通过提高maximumPoolSize来提高系统吞吐量
        自定义一个RejectedExecutionHandler,当线程数超过maximumPoolSize时进行处理,处理方式为隔一段时间检查线程池是否空闲,如果有空闲则把拒绝的Task重新放入到线程池,检查的时间小于keepAliveTime。
RejectedExecutionHandler
    ThreadPoolExecutor.AbortPolicy():     抛出java.util.concurrent.RejectedExecutionException异常
       ThreadPoolExecutor.CallerRunsPolicy():     重试添加当前的任务,他会自动重复调用execute()方法
    ThreadPoolExecutor.DiscardOldestPolicy():     抛弃旧的任务
    ThreadPoolExecutor.DiscardPolicy():     抛弃当前的任务

==========================================
LinkedBlockingQueue与ArrayBlockingQueue 区别
1.队列大小有所不同,ArrayBlockingQueue必须指定大小,而LinkedBlockingQueue可以是有界、也可以*的(Integer.MAX_VALUE),对于后者而言,当添加速度大于移除速度时,在*的情况下,可能会造成内存溢出等问题。
2.由于ArrayBlockingQueue采用的是数组的存储容器,因此在插入或删除元素时不会产生或销毁任何额外的对象实例,而LinkedBlockingQueue则会生成一个额外的Node对象。这可能在长时间内需要高效并发地处理大批量数据的时,对于GC可能存在较大影响。
3.两者的实现队列添加或移除的锁不一样,ArrayBlockingQueue实现的队列中的锁是没有分离的,即添加操作和移除操作采用的同一个ReenterLock锁,而LinkedBlockingQueue实现的队列中的锁是分离的,其添加采用的是putLock,移除采用的则是takeLock,这样能大大提高队列的吞吐量,也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

CopyOnWriteArrayList 合适读多写少的场景,写操作时,内存拷贝,消耗内存,可能造成频繁的fullGC

==========================================
unsafe:
1、内存操作:堆外内存的分配、拷贝、释放、给定地址值。如:DirectByteBuffer是Java用于实现堆外内存的一个重要类,通常用在通信过程中做缓冲池,如在Netty、MINA等NIO框架中应用广泛。
    使用对外内存好处:
    a、对垃圾回收停顿的改善。由于堆外内存是直接受操作系统管理而不是JVM,所以当我们使用堆外内存时,即可保持较小的堆内内存规模。从而在GC时减少回收停顿对于应用的影响。
    b、提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存,用mmap内存映射。
2、CAS相关:CAS在java.util.concurrent.atomic相关类、Java AQS、CurrentHashMap等,如:compareAndSwapInt()
3、线程调度:Java锁和同步器框架的核心类AbstractQueuedSynchronizer,就是通过调用LockSupport.park()和LockSupport.unpark()

==========================================
Enumeration的速度是Iterator的两倍,也使用更少的内存。
Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合
==========================================
spring生命周期,springcontext生命周期
根据图示的生命周期,具体过程如下:

1、实例化
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
Instant
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
2、属性赋值
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
Bean.setXxx()
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
3、初始化
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
Bean.init-method()
BeanPostProcessor.postProcessAfterInitialization()
4、销毁
在配置文件中指定了bean的scope=”prototype”为多例,新实例化一个bean,返回给用户,不再spring容器里管理。
对于scope=”singleton”的Bean(默认情况),把这个Bean缓存到在Spring IOC容器中
容器关闭时,DisposableBean.destroy()


==========================================
BeanFactory和ApplicationContext的区别

可以看到,ApplicationContext继承了BeanFactory,BeanFactory是Spring中比较原始的Factory,它不支持AOP、Web等Spring插件,而ApplicationContext不仅包含了BeanFactory的所有功能,还支持Spring的各种插件,还以一种面向框架的方式工作以及对上下文进行分层和实现继承。

BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了更多面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext而不是底层的BeanFactory。

==========================================

springAOP的实现原理
1. @EnableAspectJAutoProxy开启注解功能,并在容器中注册一个组件——AnnotationAwareAspectJProxyCreator,这个组件实现了SmartInstantiationAwareBeanPostProcessor,是一个后置处理器;
2.  在创建springIOC容器时,有一个步骤是refresh()即刷新容器,方法中有一步是registerBeanPostProcessors,这一步会初始化所有的后置处理器,就是在这时生成了AnnotationAwareAspectJProxyCreator组件;
3. refresh后面还有一步,finishBeanFactoryInitilization,即初始化剩下的单实例bean,实例化目标类组件和切面类组件;
4. AnnotationAwareAspectJProxyCreator会对目标类组件和切面类组件进行拦截,即在这些组件创建完成并初始化之后,调用postProcessAfterInitialization方法,判断目标类组件是否需要增强,如果需要,会将切面类的通知方法包装成增强器(Advisor),然后用cglib动态代理(如果目标类实现了接口,也可以使用jdk动态代理)给目标类对象创建一个代理对象,这个代理对象中就有上述增强器;
5. 经过2-4步,容器就创建完毕,接下来代理对象执行目标方法,首先获取目标方法的拦截器链(即MethodInterceptor,由增强器包装而来),利用拦截器的链式机制依次进入每一个拦截器进行增强;
6. 拦截器链的执行效果
目标方法成功:前置通知 → 目标方法 → 后置通知 → 返回通知;
目标方法抛出异常:前置通知 → 目标方法 → 后置通知 → 异常通知。

 
基于注解的声明式事务的原理
1. 在容器配置类上使用@EnableTransactionManagement注解,该注解在容器中注册了两大组件——AutoProxyRegistrar、ProxyTransactionManagementConfiguration;
2. AutoProxyRegistrar通过导入方式在容器中注册了InfrastructureAdvisorAutoProxyCreator,后置处理器;
3. ProxyTransactionManagementConfiguration本身就是一个容器配置类,它注册了transactionAdvisor(事务增强器),然后又在这个事务增强器中注入了两个属性transactionAttributeSource、transactionInterceptor:
    a. transactionAttributeSource用于解析@Transactional注解的各种属性;
    b. transactionInterceptor实现了MethodInterceptor,是一个拦截器链,这个拦截器链会从容器中获取事务管理器,利用事务管理器,在目标方法发生异常时执行回滚,在目标发生正常完成后提交事务;
4. 第2步的InfrastructureAdvisorAutoProxyCreator后置处理器,会在目标对象创建完成之后调用postProcessAfterInitialization方法,对需要增强的代理对象用cglib或jdk动态代理,将其包装为代理对象;
5、代理对象在执行目标方法时,会首先获取拦截器链,这个拦截器链就是第3.b步的transactionInterceptor。


==========================================
算法时间复杂度
排序方法        时间(平均)    时间(最坏)    时间(最好)    空间复杂度    稳定性    复杂性
直接插入排序    O(n2)        O(n2)        O(n)            O(1)            稳定    简单
希尔排序        O(nlog2n)    O(n2)        O(n)            O(1)            不稳定    
直接选择排序    O(n2)        O(n2)        O(n2)        O(1)            不稳定    
堆排序        O(nlog2n)    O(nlog2n)    O(nlog2n)    O(1)            不稳定    
冒泡排序        O(n2)        O(n2)        O(n)            O(1)            稳定    简单
快速排序        O(nlog2n)    O(n2)        O(nlog2n)    O(nlog2n)    不稳定    
归并排序        O(nlog2n)    O(nlog2n)    O(nlog2n)    O(n)            稳定    较复杂
基数排序        O(d(n+r))    O(d(n+r))    O(d(n+r))    O(n+r)O(n+r) 稳定    


==========================================
为什么说B+tree比B树更适合实际应用中操作系统的文件索引和数据索引.  
B+-tree的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了.
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

他们认为数据库索引采用B+树的主要原因是:B树在提高了IO性能的同时并没有解决元素遍历的效率低下的问题,正是为了解决这个问题,B+树应用而生.B+树只需要去遍历叶子节点就可以实现整棵树的遍历.而且在数据库中基于范围的查询是非常频繁的,  

==========================================
设计模式
单例模式,jdk中的单例模式: runtime类,恶汉模式
工厂模式
装饰模式:允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
模板方法模式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
观察者模式
策略模式:我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。一个类的行为或其算法可以在运行时更改
职责链模式:避免将一个请求的发送者与接受者耦合在一起,让多个对象都有机会处理请求。将接受请求的对象接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。

==========================================
启动类加载器(Bootstrap ClassLoader):加载JAVA_HOME/lib 目录
扩展类加载器(Extension ClassLoader):加载JAVA_HOME/lib/ext 目录
应用程序类加载器(Application ClassLoader)

tomcat 为了实现隔离性,没有遵守双亲委派的约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。
==========================================
Redis,集群,哨兵,一致性哈希,单线程问题,Aof效率,分布式锁

Sorted Set(有序集合 ZSet)
内部实现:跳跃表(SkipList<obj>) + HashMap<obj, score> 


缓存穿透:key没命中,打到db
    1、空值也存redis,过期时间短
    2、布隆过滤器,快速判断key存在
缓存击穿:单个key过期,大量并发到db
    1、不设过期
    2、取db加锁、set值 + 自旋判断value是否有值
缓存雪崩:多个key同时过期
    1、热点数据不过期
    2、过期时间加随机值,定期更新

sentinel:
优点:高可用、自动故障迁移,raft算法,节点间心跳连接,投票选举master
缺点:切换需要时间,丢数据,没有解决 master 写的压力

cluster(3.0开始):
优点:
1、无中心,数据按照key进行crc16算法哈希, 分布在16383个slot,多个备份。可横向扩展,节点动态添加。
2、实现故障自动 failover,raft算法,节点之间通过 gossip 协议交换状态信息,投票选举master。
缺点:
1、资源隔离性较差,容易出现相互影响的情况。
2、数据通过异步复制,不保证数据的强一致性

一致性哈希:
key对2的32次方取模,为防止key分布不均,可添加虚拟节点

为什么快:
1、内存操作,查询效率o1
2、单线程,减少线程切换消耗。
3、利用epoll多路复用,nio管理多个连接,不在io上浪费时间。

aof:
重建慢,但一致性强。集群模式下aof多节点同步,阻塞主线程:
1、fork子进程(写日志)阻塞:10G物理内存,需要fork20M的内存页表,正常情况fork1G,20ms。
2、aof刷盘阻塞:AOF fsync,大于2秒,阻塞主线程,直到上次刷盘完成,才开始本次刷盘。
3、HugePage写操作阻塞:开启Transparent HugePages的操作系统,每次写命令引起的复制内存页由4K变为2MB,拖慢整体。

分布式锁:
释放锁时,需要用lua脚本,保证原子性
redlock:
过半数节点完成,才算完成。牺牲效率。

==========================================
MySQL,undo redo log,主从主主复制,间隙锁,mvcc机制,性能监控调优explain profile解读

MyISAM和InnoDB都使用B+树来实现索引:
MyISAM的主键索引和普通索引,都是非聚集索引:索引与数据分开存储
MyISAM的索引叶子存储指针。
InnoDB的聚集索引存储数据行本身,普通索引存储主键
InnoDB一定有且只有一个聚集索引
InnoDB建议使用趋势递增整数作为PK,而不宜使用较长的列作为PK(普通索引存储量大)

主从复制:
1、slave开启两个线程:IO线程(同步binlog,写入relaylog),sql线程(从relaylog读sql,插入)
2、master开启一个线程:binlog dump
3、全同步复制(全部从节点ack即完成)、异步复制(只通知dump线程)、半同步复制(其中一个从节点ack即完成)

InnoDB整体分为三层:
(1)内存结构(In-Memory Structure),这一层在MySQL服务进程内;
    (1)缓冲池(Buffer Pool);加速读请求,避免每次数据访问都进行磁盘IO。技术点包括:预读,局部性原理,LRU,预读失败+缓冲池污染(大量结果返回,替换了原有用缓存)
    (2)写缓冲(Change Buffer);加速写请求,避免每次写入都进行磁盘IO。(innodb_change_buffer_max_size,默认值是25%,读多写少的业务:5-10%)
    (3)自适应哈希索引(Adaptive Hash Index);mysql自己维护,为了加速读请求。当业务有大量like或者join,AHI会成为负担,可手动关闭。
    (4)日志缓冲(Log Buffer);极大优化redo日志性能,并提供了高并发与强一致性的折衷方案。技术点包括:redo log作用,流程,三层架构,随机写优化为顺序写,次次写优化为批量写
(2)OS Cache,这一层属于内核态内存;
(3)磁盘,这一层在文件系统上,主要包括日志与表空间;

redo log,保证已提交事务的ACID特性。
(1)事务提交的时候,会写入内存Log Buffer
(2)MySQL发起系统调用写文件write(优化为:顺序写),Log Buffer里的数据,才会写到OS cache。此时认为文件已经写完。什么时候fsync到磁盘,是操作系统决定的(当然,MySQL也可以主动flush)
(3)由操作系统将OS cache里的数据,最终fsync到磁盘上(批量写)(但唯一索引每次都会fsync,保证唯一性检查)
MySQL事务提交时刷redo log有三种策略(参数):
(1)innodb_flush_log_at_trx_commit=0:每秒write一次OS cache,同时fsync刷磁盘,性能好;
(2)innodb_flush_log_at_trx_commit=1:每次都write入OS cache,同时fsync刷磁盘,一致性好;
(3)innodb_flush_log_at_trx_commit=2:每次都write入OS cache,每秒fsync刷磁盘,折衷;业界最佳实践。
undo log有两个作用:
(1)回滚:undo log和redo log记录物理日志不一样,它是逻辑日志,记录delete、insert、update的反向操作,用来rollback。
(2)MVCC:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么
(3)undo log也会产生redo log,因为undo log也要实现持久性保护
(4)mysql支持 128*1024=10w个undo操作

读缓冲池:
(1)缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;
(2)缓冲池通常以页(page)为单位缓存数据;
(3)缓冲池的常见管理算法是LRU,memcache,OS,InnoDB都使用了这种算法;
(4)InnoDB对普通LRU进行了优化:
    将缓冲池分为老生代和新生代,入缓冲池的页,优先进入老生代,页被访问,才进入新生代,以解决预读失效的问题
    页被访问,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰(缓冲池污染)的问题
参数:
innodb_buffer_pool_size  配置缓冲池的大小,默认是128M,越大越好,80G
innodb_old_blocks_pct    老生代占整个LRU链长度的比例,默认是37
innodb_old_blocks_time  老生代停留时间窗口,默认是1000毫秒


distinct:hash结构,key为列的值,内存占用大。
group by:将col排序、sort。时间复杂为nlogn,空间复杂度为1。


分库分表
sharding-jdbc 这种 client 层方案的优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要耦合 sharding-jdbc 的依赖;
mycat 这种 proxy 层方案的缺点在于需要部署,自己运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果遇到升级之类的都是自己中间件那里搞就行了。
通常来说,这两个方案其实都可以选用,但是我个人建议中小型公司选用 sharding-jdbc,client 层方案轻便,而且维护成本低,不需要额外增派人手,而且中小型公司系统复杂度会低一些,项目也没那么多;但是中大型公司最好还是选用 mycat 这类 proxy 层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护 mycat,然后大量项目直接透明使用即可。

==========================================
zk提供了:1、文件系统  2、通知机制

zk每个znode节点存放数据上限为1M

Zookeeper的ZAB协议包括两种基本的模式:崩溃恢复(启动/异常时)、消息广播(正常时)。

分布式协调zookeeper是如何进行领导选举的?
ZAB算法:
1)发起选主投票:每个Server启动以后都询问其它的Server它要投票给谁。
2)一次上报已有/自己:对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己)
3)接上报,计算zxid最大:收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
4)二次上报,过半数当选:这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则该server被选为leader。否则,继续这个过程,直到leader被选举出来。

一致性算法paxos、multi-paxos、raft、ZAB相同点,两阶段提交:
1、第一次提议(提交自己,或原leader)
2、根据询问的结果(判断id最大) ,提出二次决议 
3、接收决议、判断id最大、半数通过,决议产生。

不同点:
1、raft协议日志连续性,日志从leader向follower同步
2、multi-paxos、raft、ZAB都有Leader,降低多个proposer冲突

Zookeeper集群管理
1、判断节点退出/加入:所有节点在父目录下创建临时目录节点,然后watch父目录节点的子节点变化消息。一旦有机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知。
2、选举master:所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好(master先注册)。

zookeeper锁服务分为两类,独占锁,控制时序锁。
独占锁:所有客户端都去创建 /distribute_lock 节点,成功创建的拥有锁。
控制时序锁, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁。

==========================================
MongoDB,源码语言是C++,索引也是B+tree
应用不需要事务、复杂 join 支持
应用需要大量的地理位置查询、文本查询

==========================================
ES,索引原理,调优,index量,用于场景
DB ⇒ 数据库         ⇒ 表               ⇒ 行                         ⇒ 列(Columns)
ES  ⇒ 索引(Index) ⇒ 类型(type) ⇒ 文档(Docments) ⇒ 字段(Fields)

索引:
1、有序的倒排索引(posting List,采用Roaring bitmaps结构压缩), 形成索引term index
2.索引term为b-tree结构,且经过FST(Finite State Transducers)的压缩后,放入内存。
    a、Roaring bitmaps结构压缩:有序、存差值(大数变小数,节省空间)、转bitmap存储
    b、FST(Finite State Transducers)的压缩:将字符串转为了字母+数字:重复字符减少存储,通过数字标识字符联系,形成字符串。

优化:
不需要索引的字段,一定要明确定义出来,因为默认是自动建索引的
对于String类型的字段,不需要analysis的也需要明确定义出来,因为默认也是会analysis的
选择有规律的ID很重要,随机性太大的ID(比如java的UUID)不利于查询,压缩比也低,且ES是分Segment存储的,根据ID这个大范围的Term定位到Segment的效率很重要。


1、ulimit -a 命令,查看文件句柄(open files)的个数为1024,改为:ulimit -n 655360
2、jvm老年代和新生代的比例为2:1是比较合适的
3、内存分配,官方给出了解决方案,把一半的内存分配给Luence,另外的内存分配给ElasticSearch。内存消耗大户 :Lucene, 非堆内存 (off-heap)。
4、JVM不要 超过 32 GB,会让GC 应对大内存。且,Java 使用内存指针压缩(compressed oops)技术,指针表示 偏移量(内存占用小),一旦越过 ~32 GB,指针就会切回普通对象的指针
5、关掉swap:swapoff -a,如果内存交换到磁盘上,一个 100 微秒的操作可能变成 10 毫秒。
6、Elasticsearch 默认的线程设置已经很合理了,搜索线程池可以设置的大一点,配置为 int(( 核心数 * 3 )/ 2 )+ 1 
7、GC,Elasticsearch 默认的垃圾回收器( GC )是 CMS,低延迟需求,官方建议使用。
8、最小主节点数:minimum_master_nodes 设置及其重要,为了防止集群脑裂,这个参数应该设置为法定个数就是 ( master 候选节点个数 / 2) + 1
9、集群分片数:一个分片实际上对应一个lucene 索引,而lucene索引的读写会占用很多的系统资源,因此,分片数不能设置过大。控制每个分片占用的硬盘容量不超过ES的最大JVM的堆空间设置(一般设置不超过32G)。
如果分片数过多,大大超过了节点数,很可能会导致一个节点上存在多个分片,一旦该节点故障,有可能会导致数据丢失。所以, 一般都设置分片数不超过节点数的3倍。
10、索引优化
a.修改index_buffer_size 的设置,可以设置成百分数,也可设置成具体的大小,大小可根据集群的规模做不同的设置测试。
indices.memory.index_buffer_size:10%(默认)
indices.memory.min_index_buffer_size:48mb(默认)
indices.memory.max_index_buffer_size: 32GB
b. _id字段的使用,应尽可能避免自定义_id, 以避免针对ID的版本管理;建议使用ES的默认ID生成策略或使用数字类型ID做为主键。
c. _all字段及_source字段的使用,应该注意场景和需要,_all字段包含了所有的索引字段,方便做全文检索,如果无此需求,可以禁用;
   _source存储了原始的document内容,如果没有获取原始文档数据的需求,可通过设置includes、excludes 属性来定义放入_source的字段。、
d. 合理的配置使用index属性,analyzed 和not_analyzed(分词),根据业务需求来控制字段是否分词或不分词。只有 groupby需求的字段,配置时就设置成not_analyzed, 以提高查询或聚类的效率。
11、查询优化
a. 调整filter过滤顺序,把过滤效果明显的条件提前,按照过滤效果把过滤条件排序
b. 索引时间精度优化,时间粒度越大,搜索时间越短,所以时间精度越低越好;或者,增加冗余的时间字段,精确到天,带有时间范围的查询使用该字段进行查询。
c. 查询Fetch Source优化,不取非必须的字段。举例:只需要从es中查询id这一个字段,却把所有字段查询了出来


==========================================
kafka,消费顺序保证,一致性机制,死信队列怎么用,内部实现比较
不同partition之间不能保证顺序。同一个partition可以保证顺序,所以保证业务id发至同一个partition即可。

==========================================
服务治理,策略,原则,监控,devops,弹性扩容,服务发现,服务变更管理,服务监控,服务扩容缩容,服务自我保护,服务降级,服务授权防攻击,服务上线验证和灰度发布,服务问题定位和跟踪,服务负载,服务实例的调度
关于限流熔断降级,目前开源框架有hystrix和sentinel、Guava RateLimiter。比如Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性,限流:令牌桶、漏斗、冷启动限流、 滑动窗口统计机制
==========================================
springboot、springcloud
什么是SpringBoot?
1、用来简化spring初始搭建和开发过程使用特定的方式进行配置(properties或者yml文件)
2、创建独立的spring引用程序main方法运行
3、嵌入Tomcat无需部署war包,直接打成jar包nohup java -jar – & 启动就好
4、简化了maven的配置
4、自动配置spring添加对应的starter自动化配置
SpringBoot常用的starter:
1、spring-boot-starter-web(嵌入Tomcat和web开发需要的servlet和jsp支持)
2、spring-boot-starter-data-jpa(数据库支持)
3、spring-boot-starter-data-Redis(Redis支持)
4、spring-boot-starter-data-solr(solr搜索应用框架支持)
5、mybatis-spring-boot-starter(第三方mybatis集成starter)
SpringBoot自动配置原理:
1、@EnableAutoConfiguration这个注解会"猜"你将如何配置spring,前提是你已经添加了jar依赖项,如果spring-boot-starter-web已经添加Tomcat和SpringMVC,这个注释就会自动假设您在开发一个web应用程序并添加相应的spring配置,会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建spring容器中bean
2、在main方法中加上@SpringBootApplication和@EnableAutoConfiguration
SpringBoot starter工作原理:
1、SpringBoot在启动时扫描项目依赖的jar包,寻找包含spring.factories文件的jar
2、根据spring.factories配置加载AutoConfigure
3、根据@Conditional注解的条件,进行自动配置并将bean注入到Spring Context
SpringBoot的优点:
1、减少开发、测试时间和努力
2、使用JavaConfig有助于避免使用XML
3、避免大量的maven导入和各种版本冲突
4、提供意见发展方法
5、通过提供默认值快速开始开发
6、没有单独的web服务器需要,这就意味着不再需要启动Tomcat、Glassfish或其他任何东西
7、需要更少的配置,因为没有web.xml文件。只需添加用@Configuration注释的类,然后添加用@Bean注释的方法,Spring将自动加载对象并像以前一样对其进行管理。甚至可以将@Autowired添加到bean方法中,以使用Spring自动装入需要的依赖关系中
Springcloud解决那些问题:
配置管理、(注册中心eureka、zk)、服务发现、服务注册、断路器、路由策略、全局锁、分布式会话、客户端调用、接口网关(zuul)、服务管理系统
SpringBoot与Springcloud:
1>、SpringBoot简化了xml配置,快速整合框架
2>、Springcloud是一套微服务解决方案—RPC远程调用
3>、关系Springcloud依赖与SpringBoot(web组件用的SpringMVC),为什么Springcloud会依赖与SpringBoot?因为Springcloud写接口就是SpringMVC接口
4>、SpringBootproperties和yml中可以使用${random}设置一些随机值
服务的调用:
rest、feign(均使用httpclient技术),负载均衡ribbon
服务调用的原理:
服务首先注册到注册中心eureka中(注册一个名字通过名字调用)
负载均衡
ribbon,先去注册中心取到对应的服务,然后交给我ribbon
配置详解:
1>、eureka.client.register-with-eureka:是否向注册中心注册自己,注册为true反之为false
2>、eureka.client.fetch-registry: 是否需要去检索服务,检索为true反之为false
3>、eureka.client.serviceUrl.defaultZone : 指定服务注册中心的地址
Eureka:
1>、eureka可分为三个角色:服务发现者、服务注册者、注册发现中心,但是这三个角色并不和实际部署的模型是一对一的关系
2>、所有的网络通信都是基于http(s)协议的
3>、Eureka和AWS是紧密结合的,无论是配置还是源码,比如Region、zone…,Region可以通过配置文件进行配置,如果不配置默认使用us-east-1。同样Zone也可以配置,若不配置默认使用defaultZone
高可用配置:
Eureka server 的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用效果。
微服务:
以前所有的代码都放在同一个工程中、部署在同一个服务器、同一项目的不同模块不同功能互相抢占资源,微服务就是将工程根据不同的业务规则拆分成微服务,部署在不同的服务器上,服务之间相互调用,java中有的微服务有dubbo(只能用来做微服务)、springcloud( 提供了服务的发现、断路器等)。
微服务的特点:
按业务划分为一个独立运行的程序,即服务单元
服务之间通过HTTP协议相互通信
自动化部署
可以用不同的编程语言
可以用不同的存储技术
服务集中化管理
微服务是一个分布式系统
微服务的优势:
1、将一个复杂的业务拆分为若干小的业务,将复杂的业务简单化,新人只需要了解他所接管的服务的代码,减少了新人的学习成本。
2、由于微服务是分布式服务,服务于服务之间没有任何耦合。微服务系统的微服务单元具有很强的横向拓展能力。
3、服务于服务之间采用HTTP网络通信协议来通信,单个服务内部高度耦合,服务与服务之间完全独立,无耦合。这使得微服务可以采用任何的开发语言和技术来实现,提高开发效率、降低开发成本。
4、微服务是按照业务进行拆分的,并有坚实的服务边界,若要重写某一业务代码,不需了解所以业务,重写简单。
5、微服务的每个服务单元是独立部署的,即独立运行在某个进程中,微服务的修改和部署对其他服务没有影响。
6、微服务在CAP理论中采用的AP架构,具有高可用分区容错特点。高可用主要体现在系统7x24不间断服务,他要求系统有大量的服务器集群,从而提高系统的负载能力。分区容错也使得系统更加健壮。
微服务的不足:
1、微服务的复杂度:构建一个微服务比较复杂,服务与服务之间通过HTTP协议或其他消息传递机制通信,开发者要选出最佳的通信机制,并解决网络服务差时带来的风险。
2、分布式事物:将事物分成多阶段提交,如果一阶段某一节点失败仍会导致数据不正确。如果事物涉及的节点很多,某一节点的网络出现异常会导致整个事务处于阻塞状态,大大降低数据库的性能。
3、服务划分:将一个完整的系统拆分成很多个服务,是一件非常困难的事,因为这涉及了具体的业务场景
4、服务部署:最佳部署容器Docker
微服务和SOA的关系:
微服务相对于和ESB联系在一起的SOA轻便敏捷的多,微服务将复杂的业务组件化,也是一种面向服务思想的体现。对于微服务来说,它是SOA的一种体现,但是它比ESB实现的SOA更加轻便、敏捷和简单。
springcloud如何实现服务注册与发现?
服务发布时指定对应的服务名(IP地址和端口号),将服务注册到注册中心(eureka和zookeeper),但是这一切是Springcloud自动实现的,只需要在SpringBoot的启动类上加上@EnableDisscoveryClient注解,同一服务修改端口就可以启动多个实例调用方法:传递服务名称通过注册中心获取所有的可用实例,通过负载均衡策略(Ribbon和Feign)调用对应的服务
Ribbon和Feign的区别:
Ribbon添加的maven依赖是spring-starter-ribbon,使用@RibbonClient(value=“服务名称”)使用RestTemplate调用远程服务对应的方法,
Feign添加的maven依赖是spring-starter-feign,服务提供方提供对外接口,调用方使用,在接口上使用FeignClient(“指定服务名”),
具体区别:
1、启动类使用的注解不同,Ribbon使用的是@RibbonClient,Feign使用的是@EnableFeignClients
2、服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明
3、调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤比较繁琐。Feign则是在Ribbon的基础上进行了一次改进,采用接口调用的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求,不过要注意的是抽象方法的注解、方法签名要和提供方的完全一致。
雪崩效应:
分布式系统中的服务通信依赖于网络,网络不好,必然会对分布式系统带来很大的影响。在分布式系统中,服务之间相互依赖,如果一个服务之间出现了故障或者网络延迟,在高并发的情况下,会导致线程阻塞,在很短的时间内该服务的线程资源会消耗殆尽,最终使得该服务不可用。由于服务的相互依赖,可能会导致整个系统的不可用,这就是“雪崩效应”。为了防止此类事件的发生,分布式系统必然要采取相应的措施,如熔断机制(Springcloud采用的是Hystrix)
熔断机制:
1、当一个服务出现故障时,请求失败次数超过设定的阀值(默认50)之后,该服务就会开启熔断器,之后该服务就不进行任何业务逻辑操作,执行快速失败,直接返回请求失败的信息。其他依赖于该服务的服务就不会因为得不到响应而造成线程阻塞,这是除了该服务和依赖于该服务的部分功能不可用外,其他功能正常。
2、熔断器还有一个自我修复机制,当一个服务熔断后,经过一段时间(5s)半打开熔断器。半打开的熔断器会检查一部分请求(只能有一个请求)是否正常,其他请求执行快速失败,检查的请求如果响应成功,则可判断该服务正常了,就可关闭该服务的熔断器,反之则继续打开熔断器。这种自我熔断机制和自我修复机制可以使程序更加健壮、也可以为开发和运维减少很多不必要的工作。
3、熔断组件往往会提供一系列的监控,如:服务可用与否、熔断器是否被打开、目前的吞吐量、网络延迟状态的监控等,从而可以让开发人员和运维人员的了解服务的状况。
Eureka基础架构:
1>、服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能
1>>、失效剔除:对于那些非正常下线的服务实例(内存溢出、网络故障导致的),服务注册中心不能收到“服务下线”的请求,为了将这些无法提供服务的实例从服务列表中剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间(默认60s)将当前清单中超时(默认90s)没有续约的服务剔除出去。
2>>、自我保护:Eureka Server 在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(生产环境由于网络不稳定会导致),Eureka Server会降当前的实例注册信息保护起来,让这些实例不过期,尽可能保护这些注册信息,但是在这保护期间内实例出现问题,那么客户端就很容易拿到实际上已经不存在的服务实例,会出现调用失败的情况,所以客户端必须有容错机制,比如可以使用请求重试、断路器等机制。
在本地进行开发时可以使用 eureka.server.enable-self-preseervation=false参数来关闭保护机制,以确保注册中心可以将不可用的实例剔除。
2>、服务提供者:提供服务的应用,可以是SpringBoot应用也可以是其他的技术平台且遵循Eureka通信机制的应用。他将自己提供的服务注册到Eureka,以供其他应用发现,(如:service层)
1>>、服务注册:服务提供者在启动的时候会通过发送Rest请求的方式将自己注册到Eureka Server(服务注册中心)中,同时带上自身服务的一些元数据,Eureka Server 接收到这个Rest请求后,将元数据存储在一个双层结构Map中,第一层的key是服务名,第二层key是具体服务的实例名
2>>、服务同步:若有两个或两个以上的Eureka Server(服务注册中心)时,他们之间是互相注册的,当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发到集群中相连的其他注册中心,从而实现注册中心间的服务同步,这样服务提供者的服务信息可以通过任意一台服务中心获取到
3>>、服务续约:在注册完服务之后,服务提供者会维护一个心跳来持续告诉Eureka Server:“我还活着”,以防止Eureka Server的“剔除任务”将该服务实例从服务列表中排除出去。配置:eureka.instance.lease-renewal-in-seconds=30(续约任务的调用间隔时间,默认30秒,也就是每隔30秒向服务端发送一次心跳,证明自己依然存活),eureka.instance.lease-expiration-duration-in-seconds=90(服务失效时间,默认90秒,也就是告诉服务端,如果90秒之内没有给你发送心跳就证明我“死”了,将我剔除)
3>、服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其所需要的服务,如:Ribbon实现消费方式、Feign实现消费方式
1>>、获取服务:当启动服务消费者的时候,它会发送一个Rest请求给注册中心,获取上面注册的服务清单,Eureka Server会维护一份只读的服务清单来返回给客户端,并且每三十秒更新一次
2>>、服务调用:在服务消费者获取到服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元信息,采用Ribbon实现负载均衡
3>>、服务下线:当服务实例进行正常的关闭操作时,它会触发一个服务下线的Rest请求给Eureka Server,告诉服务注册中心“我要下线了”。服务端接收到请求之后,将该服务状态设置为下线,并把下线时间传播出去。
Eureka和zookeeper都可以提供服务注册与发现的功能,两者的区别:
Zookeeper保证了CP(C:一致性,P:分区容错性),Eureka保证了AP(A:高可用,P:分区容错)
1、Zookeeper-----当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接down掉不可用的。也就是说服务注册功能对高可用性要求比较高,但是zk会出现这样的一种情况,当master节点因为网络故障与其他节点失去联系时,剩余的节点会重新选leader。问题在于,选取leader的时间过长(30~120s),且选取期间zk集群都不可用,这样就会导致选取期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务最终恢复,但是漫长的选择时间导致的注册长期不可用是不能容忍的
2、Eureka则看明白这一点,因此再设计的优先保证了高可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响到正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端再向某个Eureka注册时如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保证注册服务的可用(保证可用性),只不过查到的信息可能不是最新的(不保证一致性)。除此之外Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时就会出现以下几种情况:
1>、Eureka不再从注册列表移除因为长时间没收到心跳而应该过期的服务
2>、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(保证当前节点可用)
3>、当网络稳定时,当前实例新的注册信息会被同步到其它节点中
Eureka还有客户端缓存功能(Eureka分为客户端程序和服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。所以即便Eureka集群中所有节点都失效,或者发生网络分隔故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者任然可以通过Eureka客户端缓存来获取所有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生响应也没有更好的服务器解决方案来解决这种问题时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息,这点很重要,因此Eureka可以很好的应对网络故障导致部分节点失去联系的情况,而不像Zookeeper那样使整个注册服务瘫痪。
CAP理论:
1、Consistency:指数据的强一致性。如果写入某个数据成功,之后读取,读到的都是新写入的数据;如果写入失败,读到的都不是写入失败的数据。
2、Availability:指服务的可用性
3、Partition-tolerance:指分区容错
Ribbon和Nginx的区别:
Nginx性能好,但Ribbon可以剔除不健康节点,Nginx剔除比较麻烦,Ribbon是客户端负载均衡,Nginx是服务端负载均衡
服务注册与发现:
服务注册就是向服务注册中心注册一个服务实例,服务提供者将自己的服务信息(服务名、IP地址等)告知注册中心。服务发现是服务消费另一个服务时,注册中心将服务的实例返回给服务消费者,一个服务既是服务提供者又是服务消费者。
服务注册中心健康检查机制,当一个服务实例注册成功以后,会定时向注册中心发送一个心跳证明自己可用,若停止发送心跳证明服务不可用将会别剔除。若过段时间继续想注册中心提供心跳,将会重新加入服务注册中心列表中。
服务的负载均衡:
为什么要用:微服务是将业务代码拆分为很多小的服务单元,服务之间的相互调用通过HTTP协议来调用,为了保证服务的高可用,服务单元往往都是集群化部署的,那么消费者该调用那个服务提供者的实例呢?
介绍:服务消费者集成负载均衡组件,该组件会向服务消费者获取服务注册列表信息,并隔一段时间重新刷新获取列表。当服务消费者消费服务时,负载均衡组件获取服务提供者所有实例的注册信息,并通过一定的负载均衡策略(可以自己配置)选择一个服务提供者实例,向该实例进行服务消费,这样就实现了负载均衡。

==========================================
DDD 领域驱动开发

失⾎模型
Domain Object 只有属性的 getter/setter ⽅法的纯数据类,所有的业务逻辑完全由 busi‐ness object 来完成。

贫⾎模型
简单来说,就是 Domain Object 包含了不依赖于持久化的领域逻辑,⽽那些依赖持久化的领域逻辑被分离到 Service 层。
优点:各层单向依赖,结构清晰。
缺点:Domain Object 的部分⽐较紧密依赖的持久化 Domain Logic 被分离到 Service 层,显得不够 OO,Service 层过于厚重


充⾎模型
和第⼆种模型差不多,区别在于业务逻辑划分,将绝⼤多数业务逻辑放到 Domain中
优点:
更加符合 OO 的原则;Service 层很薄,只充当 Facade 的⻆⾊,不和 DAO 打交道。
缺点:
DAO 和 Domain Object 形成了双向依赖,复杂的双向依赖会导致很多潜在的问题。
如何划分 Service 层逻辑和 Domain 层逻辑是⾮常含混的,在实际项⽬中,由于设计
和开发⼈员的⽔平差异,可能 导致整个结构的混乱⽆序。

胀⾎模型
基于充⾎模型的第三个缺点,有同学提出,⼲脆取消 Service 层,只剩下 Domain Object和 DAO 两层,在 Domain Object 的 Domain Logic 上⾯封装事务。
优点:简化了分层,也算符合 OO
该模型缺点:很多不是 Domain Logic 的 Service 逻辑也被强⾏放⼊ Domain Object ,引起了Domain Object 模型的不稳定;Domain Object 暴露给 Web 层过多的信息,可能引起意想不到的副作⽤。


防腐层
亦称适配层。在⼀个上下⽂中,有时需要对外部上下⽂进⾏访问,通常会引⼊防腐层的概念来对外部上下⽂的访问进⾏⼀次转义。
有以下⼏种情况会考虑引⼊防腐层:
1、需要将外部上下⽂中的模型翻译成本上下⽂理解的模型。
2、不同上下⽂之间的团队协作关系,如果是供奉者关系,建议引⼊防腐层,避免外部上下⽂变化对本上下⽂的侵蚀。
3、该访问本上下⽂使⽤⼴泛,为了避免改动影响范围过⼤。

==========================================
分布式事务,seata
1、AT 模式,两阶段:预留+执行/回滚
好处:业务开发无侵入,框架完成sql拦截、生成数据的before image、after image(回滚时验证脏写)、行锁、回滚删除img。
缺点:全局行锁,效率低

2、TCC模式,两阶段:预留+执行/回滚
好处:无行锁,性能高
缺点:业务需拆分为两阶段业务,侵入性高
三种异常:
Try未执行,Cancel执行了,解决方案:cancel空回滚
多次Confirm,解决方案:业务幂等
Cancel接口 比 Try接口先执行,解决方案:cancel悬挂

3、Saga: 是长事务解决方案,事务驱动
好处:参与者包含其他公司或遗留系统服务,无法提供 TCC 模式要求的三个接口,典型金融机构业务
一阶段提交本地数据库事务,无锁,高薪能
补偿服务即正向服务的 “反向”,高吞吐
参与者可异步执行,高吞吐

4、XA无侵入,将快照数据和行锁等通过 XA 指令委托给了数据库来完成。
分布式强一致性的解决方案,但性能低而使用较少。

==========================================
大数据,hadoop spark flink jstorm hbase akka比较


==========================================
列式存储,clickhouse
ClickHouse从OLAP场景需求出发,定制开发了一套全新的高效列式存储引擎,并且实现了数据有序存储、主键索引、稀疏索引、数据Sharding(自定义)、数据Partitioning(自定义)、TTL、主备复制等丰富功能

适用于:
1、读多于写。
2、大宽表,读大量行但是少量列。
3、数据批量写入。
4、无需事务,数据一致性要求低。
5、灵活多变,不适合预先建模。

列式存储好处:
1、宽表少量列读出时,降低磁盘io。
2、高压缩比 --> 高缓存率。

特性:
1、支持在建表时,指定将数据按照某些列进行sort by,等值、范围查询时,降低IO,也能充分利用操作系统page cache。
2、主键索引,按照index granularity(默认8192行)进行划分,主键索引存储每个index granularity的第一行的pk值,不排重。
3、稀疏索引,minmax、set(max_rows)、ngrambf_v1、tokenbf_v1、bloom_filter等
4、数据Sharding,解决分片间数据倾斜问题:random随机分片、constant固定分片、column value分片、自定义表达式分片。
5、数据Partitioning,在建表时可以指定合法表达式做数据分区,toYYYYMM()按月分区、toMonday()按照周几分区、Enum类型每值一个分区。
6、数据TTL,保留热数据:列级别、行级别、分区级别。
7、高吞吐写入:LSM Tree的结构(磁盘批量的顺序写),定期在后台Compaction。50MB-200MB/s。
8、多核并行、分布式计算、主备同步。

==========================================
脑裂,从与主失去心跳,变成主。
1,从延迟选主动作
2,增加心跳
3,主增加分布式锁,锁资源,只有一个节点能编辑资源
4,从增加自我ping判断,是否自己断网

==========================================
PoW,Pos,DPoS,分布式一致性算法优劣分析

PoW:矿工通过把网络尚未记录的现有交易打包到一个区块,然后不断遍历尝试来寻找一个随机数,使的新区块加上随机数的哈希值满足一定难度的条件,找到满足条件的随机数就相当于确定了区块链最新的一个区块,也相当于获得了区块链的本轮记账权。矿工吧满足条件的区块在网络中广播出去,全网其他节点在验证该区块满足挖矿难度条件,同时区块里的交易数据符合协议规范后,将各自把该区块链接到自己版本的区块链上从而在全网行程对当前网络状态的共识。
优点:完全去中心化,节点*进出,避免了建立和维护中心化信用机构的成本,只要网络破坏者的算力不超过全网50%,网络的交易状态就能达成一致
缺点:挖矿造成大量的资源浪费,同时挖矿的奖励机制也造成矿池算力的高度集中,背离了当初去中心化设计的初衷,更大的问题是PoW机制达成共识的周期比较长,每秒钟最多只能做七笔交易,不适合商业应用

PoS:要求节点提供拥有一定数量的代币证明来获取竞争区块链记账权的一种分布式共识机制,如果单纯依靠代币余额来决定记账者必然使得富有者胜出,导致记账权的中心化,降低共识的公正性,因此不同的PoS机制在权益证明的基础上,采用不同方式来增加记账权的随机性来避免中心化,例如点点币(PeerCoin)PoS机制中拥有最多链龄长的比特币获得记账权的几率就越大,NXT和Blackcoin则采用一个公式来预测下一个记账的节点,拥有多的代币被选为记账节点的概率就越大;
优点:在一定 程度上缩短了共识达成的时间,降低了资源浪费
缺点:破坏者对网络攻击的成本低,网络安全性有待验证,另外拥有代币数量大的节点获得记账权的几率更大,会使得网络的共识受少数富裕账户支配从而失去公正性。

DPoS :类似于董事会投票,比特股(bitshares)采用的PoS机制是持股者投票选出一定数量的见证人,每个见证人按顺序有两秒钟的权限时间生成区块,若见证人在指定的时间片不能生成区块,区块生成权限交给下一个时间片对应的见证人。持股人可以随时通过投票更换这些见证人,DPoS的这种设计使得区块的生成更快捷更节能。
优点:大幅度缩小参与验证和记账节点的数量,可以达到秒级共识验证
缺点:选举固定数量的见证人作为记账候选人有可能不适合完全去中心化的场景,另外在网络节点数量少的场景,选举的见证人代表性也不强。

分布式一致性算法:基于传统的分布式一致性技术,其中有分为拜占庭将军问题的拜占庭容错算法,如PBFT,另外解决非拜占庭将军问题的分布式一致性算法(Pasox,Raft)
优点:实现秒级的快速共识机制,保证一致性
缺点:去中心化程度不如共有连上的共识机制,更适合多方参与的多中心商业模式

==========================================
Linux下TCP最大连接数限制修改:
1、修改
打开文件 /etc/sysctl.conf,增加以下设置
该参数设置系统的TIME_WAIT的数量,如果超过默认值则会被立即清除
net.ipv4.tcp_max_tw_buckets = 20000
定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数
net.core.somaxconn = 65535
对于还未获得对方确认的连接请求,可保存在队列中的最大数目
net.ipv4.tcp_max_syn_backlog = 262144
在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 30000
能够更快地回收TIME-WAIT套接字。此选项会导致处于NAT网络的客户端超时,建议为0
net.ipv4.tcp_tw_recycle = 0
系统所有进程一共可以打开的文件数量
fs.file-max = 6815744
防火墙跟踪表的大小。注意:如果防火墙没开则会提示error: "net.netfilter.nf_conntrack_max" is an unknown key,忽略即可
net.netfilter.nf_conntrack_max = 2621440

运行 sysctl -p即可生效。

2、修改打开文件限制
(1)ulimit -HSn 102400 这只是在当前终端有效,退出之后,open files 又变为默认值。
(2)将ulimit -HSn 102400写到/etc/profile中,这样每次登录终端时,都会自动执行/etc/profile。
(3)令修改open files的数值永久生效,则必须修改配置文件:/etc/security/limits.conf. 在这个文件后加上:
soft nofile 1024000
hard nofile 1024000
root soft nofile 1024000
root hard nofile 10240

==========================================
设计模式五大原则:SOLID原则
开闭原则:当需求改变时,在不修改源代码的前提下,可以扩展模块的功能,满足新的需求。例如:Windows 的桌面主题设计
里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能。例如:企鹅属于鸟类;但它不飞,所以不能定义成“鸟”的子类。
依赖倒置原则:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;其核心思想是:要面向接口编程。例如:顾客从多家商店购物,可将商店参数设置为interface Shop,代码中用Shop的实现类做参数。
单一职责原则:的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。一个类只负责一项职责,提高类的可读性,提高系统的可维护性。例如:学校系统中,老师、学生的角色。
接口隔离原则:将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。例如:学生成绩管理程序,将诸多功能分类放在输入、统计、打印 3 个接口中。
 

上一篇:Python如何支持读入gz压缩或未压缩文件?


下一篇:2.3.3 `do while`循环