sowhat1412 后端总结
2021-05-21 09:24:31 140 举报
AI智能生成
sowhat1412 个人知识思维导图分享,有很多别人的文章吸收跟汇总,只是单纯的希望可以帮助到想提升自己的朋友。
作者其他创作
大纲/内容
Java基础
参考高淇300集,相当不错
1. JVM、JRE、JDK。环境搭建。
数据类型
整数型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点型:float(4字节)、double(8字节)
布尔型:boolean(1字节)
字符型:char(2字节)
浮点型:float(4字节)、double(8字节)
布尔型:boolean(1字节)
字符型:char(2字节)
Unicode 编码
强制转换,自动转换,转换注意
变量命名,运算符,顺序结构,选择结构,循环结构,while,for,break,continue,package,import,面向对象思维,this,static,volatile,Object类方法,
抽象,封装,继承,多态,Interface,abstract,数组,自动拆箱装箱
抽象,封装,继承,多态,Interface,abstract,数组,自动拆箱装箱
String
底层实现不可变 final char[],联想到8大基本类型
StringBuilder:单线程操作字符串
StringBuffer:多线程操作字符串,底层用了 synchronized
Java函数是值传递
深拷贝跟浅拷贝:基本类型跟String是深拷贝,其余默认浅拷贝
枚举类型
策略枚举
异常
异常继承图
IO
字节流和字符流
输出流和输入流
节点流和处理流
IO装饰者模式
反射
通过反射 reflact 拿到类的一切
1、Class cls = String.class;
2、Class cls = "str".getClass();
3、Class cls = Class.forName("java.lang.String");
2、Class cls = "str".getClass();
3、Class cls = Class.forName("java.lang.String");
可以破坏私有性实现一切
按需加载 *.class文件到内存生成Class 目标对象
注解
给Java代码看的注释,底层还是用的反射
作用一、让编译器检查代码
作用二、将数据注入到方法、成员变量、类上
作用二、将数据注入到方法、成员变量、类上
Annotation
@Override
@Deprecated
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
装饰注解的基本注解,自定义注解
@Deprecated
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
装饰注解的基本注解,自定义注解
泛型
通俗理解
Java在1.5之后加入了泛型的概念:
泛型的本质是为了参数化类型(将类型参数化传递)(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)
泛型的本质是为了参数化类型(将类型参数化传递)(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)
泛型类、泛型方法、泛型接口
PECS 原则 上界 下界
extends
super
泛型擦除
工具类
Object
hashCode()
equals(Object obj)
clone()
toString()
notify()
notifyAll()
wait(long timeout) (还有重载了两个)
finalize()
equals(Object obj)
clone()
toString()
notify()
notifyAll()
wait(long timeout) (还有重载了两个)
finalize()
wait、notify还是notifyAll()都是在同步代码块中调用的
调用wait()的线程会释放掉锁,Thread.sleep不会释放锁
wait被唤醒:该线程被中断,wait()时间到了,被notify()唤醒,被notifyAll()唤醒
调用wait()的线程会释放掉锁,Thread.sleep不会释放锁
wait被唤醒:该线程被中断,wait()时间到了,被notify()唤醒,被notifyAll()唤醒
线程安全的DateTimeFormatter ,线程不安全的 SimpleDateFormat
java.utils
Random
Date
Calendar
Arrays
Timer TimerTask
Collections
并发包 java.util.concurrent : 在并发专题会着重讲解
常见容器
HashMap
数组 + 链表 + 红黑树
增删改查,同时维护这红黑树跟双向链表结构,若干常见问题比如扩容等
JDK7中环的产生
transfer 函数, 多线程扩容时会产生环
LinkedHashMap
继承自HashMap,保存了数据插入的顺序。方便访问。 HashMap + 双向链表 = LinkedHashMap
LinkedHashMap和HashMap不同的是LinkedHashMap通过继承hashMap中的Entry<K,V>,并添加两个属性Entry<K,V> before,after,和header结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序
基于LinkedHashMap实现LRU
ArrayList
是Array的复杂版本,动态数组,用来存储对象,基本数据类型需包装,JDK7初始化为10,JDK8初始化为0,扩容为1.5倍
arraylist原理 transient Object[] elementData,为什么数组加 transient
底层是writeObject,readObject。实现按需序列化
小bug,ArrayList(int initialCapacity) 虽然指定初始化值,但是size仍然为0 !
线程不安全,可用 Collections.synchronizedList 包装下变为安全的
LinkedList
双向链表 方便增删
Vector
初始值为10,扩容为2倍,底层用synchronized实现的!
容器图
Java8新特性
@FunctionalInterface
一个接口 只有一个未实现方法
Function 函数型接口:有一个输入参数,有一个输出
Predicate 断定型接口:有一个输入参数,返回值只能是 布尔值!
Consumer 消费型接口: 只有输入,没有返回值
Supplier 供给型接口 没有参数,只有返回值
lambda表达式
从 Scala作者 马丁·奥德斯基 借鉴
() -> {}
方法引用
Option
防止出现空类型,一切皆对象的思想,跟Scala中类似
Stream
Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身
常用操作:foreach、filter、map、flatMap、sorted、distinct、count、min、max、skip、limit、collect、anyMatch、allMatch、reduce、findFirst、findAny
JUC并发
拔高必备:夺命连问
1、synchronized跟ReentrantLock使用区别跟底层实现以及重入底层原理
2、描述下锁的四种状态跟升级过程
3、CAS是什么?CAS的ABA问题如何解决,聊一聊AQS,为什么AQS底层是CAS + Volatile
4、你对volatile的理解,可见性跟指令重排咋实现的
5、一个对象创建过程是怎么样的。对象在内存中如何分布的。
6、聊一聊单例模式,为什么DCL要用volatile
7、Object 0 = new Object() 在内存中占据几个字节
8、你对as-if-serial跟happpends-before的理解
9、ThreadLocal说一说,咋解决内存泄露
10、自旋锁一定比重量级锁效率高吗?偏向锁是否效率一定提高。
11、线程池聊一聊如何用 注意细节,如何实现。
12、你对JMM理解?
13、Synchronized 无法禁止指令重排,却能保证有序性
2、描述下锁的四种状态跟升级过程
3、CAS是什么?CAS的ABA问题如何解决,聊一聊AQS,为什么AQS底层是CAS + Volatile
4、你对volatile的理解,可见性跟指令重排咋实现的
5、一个对象创建过程是怎么样的。对象在内存中如何分布的。
6、聊一聊单例模式,为什么DCL要用volatile
7、Object 0 = new Object() 在内存中占据几个字节
8、你对as-if-serial跟happpends-before的理解
9、ThreadLocal说一说,咋解决内存泄露
10、自旋锁一定比重量级锁效率高吗?偏向锁是否效率一定提高。
11、线程池聊一聊如何用 注意细节,如何实现。
12、你对JMM理解?
13、Synchronized 无法禁止指令重排,却能保证有序性
周志明:《深入理解Java虚拟机》
方腾飞:《Java并发编程的艺术》
方腾飞:《Java并发编程的艺术》
基础知识
进程跟线程
进程是操作系统调用的最小单位,线程是CPU调度的最小单位,Java默认有两个线程 main 跟GC
Java是没有权限开线程的,无法操作硬件,都是调用的native方法 由C++实现
并发跟并行
并发concurrency : 多线程操作同一个资源,单核CPU极速的切换运行多个任务
并行parallelism :多个CPU同时使用,CPU多核 真正的同时执行
集群跟分布式
集群:多个人做同一件事情(3 个厨师同时炒菜)
分布式:把一件复杂的事情,拆分成几个简单的步骤,分别找不同的人去完成 (1 洗菜、2 切菜、3 炒菜)
并发编程的出发点:充分利用CPU计算资源
线程几个状态
1、New 新生
2、Runnable 可运行
3、running
4、Blocked 阻塞
5、Waiting 等待 Time_Waiting 超时等待
6、Terminated 终止
2、Runnable 可运行
3、running
4、Blocked 阻塞
5、Waiting 等待 Time_Waiting 超时等待
6、Terminated 终止
线程状态
转换图:IO阻塞还是Runnable
yield 跟sleep区别
1、yield, sleep 都能暂停当前线程 同样不会释放锁,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。
2、yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。
3、yield 不能被中断,而 sleep 则可以接受中断。
2、yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。
3、yield 不能被中断,而 sleep 则可以接受中断。
Java线程
早期OS没有线程概念,只有进程。操作系统按照进程调度,应用层进行线程的创建跟维护还有调度。
优点:OS不支持线程也可通过库函数支持线程。不会引发用户态到内核态的切换,为啥go流行,它的协程借鉴了这点
缺点: OS不知道线程存在,进程中一个线程耗时过久会阻塞整个进程。某个线程进行网络IO导致线程中断也会引发阻塞整个进程。
优点:OS不支持线程也可通过库函数支持线程。不会引发用户态到内核态的切换,为啥go流行,它的协程借鉴了这点
缺点: OS不知道线程存在,进程中一个线程耗时过久会阻塞整个进程。某个线程进行网络IO导致线程中断也会引发阻塞整个进程。
OS线程
Linux中JVM基于pthread实现,Java线程本质是OS线程。
为避免线程消耗资源过大引入了协程,协程是按需使用栈内存的,即是在用户态做线程资源切换,也让操作系统在内核层做线程调度。
为避免线程消耗资源过大引入了协程,协程是按需使用栈内存的,即是在用户态做线程资源切换,也让操作系统在内核层做线程调度。
wait 跟sleep区别
1、来源不同
wait 来自Object
sleep 来自Thread
2、是否释放锁
wait释放锁
sleep不释放
3、使用范围
wait必须在同步代码块中
sleep可以任意使用
4、捕捉异常
wait不需要捕获异常
sleep需捕获异常
多线程实现方式
1、继承 Thread,实现run方法
2、实现 Runnable接口中的run方法,然后用Thread包装下
Thread 是线程对象,Runnable 是任务,一线程启动的时候一定是对象
3、实现 Callable接口,FutureTask包装实现接口,Thread包装FutureTask
Callable 与 Runnable 的区别
1、Callable的call方法有返回值,可以抛出异常,callable有缓存。
4、通过线程池 调用实现
5、@Async Spring注解
死锁
产生条件
1、互斥条件
2、不可抢占条件
3、占有且申请条件
4、循环等待条件
2、不可抢占条件
3、占有且申请条件
4、循环等待条件
检查
1、jps -l 定位进程号
2、jstack 进程号找到死锁问题
2、jstack 进程号找到死锁问题
避免
破坏产生条件
synchronized 讲解
并发编程中关键字级别加锁,悲观锁。
作用
互斥性:确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须排队获得锁,
可见性:保证共享变量的修改能够及时可见,获得锁的线程操作完毕后会将所数据刷新到共享内存区
有序性:不解决重排序,但保证有序性
用法
修饰实例方法:synchronized关键词作用在方法的前面,用来锁定方法,其实默认锁定的是this对象。
修饰静态方法:synchronized还是修饰在方法上,不过修饰的是静态方法,等价于锁定的是Class对象
修饰代码块:在函数体内部对于要修改的参数区间用synchronized来修饰,相比与锁定函数这个范围更小,可以指定锁定什么对象。
if 导致的 虚假唤醒
如何优雅关闭线程
Thread.join 其实底层是通过 wait/notifyall 来实现线程的通信达到线程阻塞的目的;当线程执行结束以后,会触发两个事情,第一个是设native 线程对象为null、第二个是通过notifyall方法 让所有wait的线程可以有机会执行下去
Monitors 管程:可以认为是专门管理 共享变量和对这些共享变量操作的封装。
底层原理
堆中创建的对象有三部分
实例对象:类的属性信息跟父类属性
填充数据:仅仅是为了字节对齐,8字节倍数
对象头
类型指针:指向它的类元数据的指针,是那个Class的实例
标记字段:HashCode,GC分代年龄,会根据对象的状态复用自己的存储空间,锁状态标准跟指针(指向monitor,也称管程或监视锁)。
monitor
monitor依赖操作系统底层Mutex Lock实现,导致操作系统线程切换会从用户态切换到核心态,在Java6之前比较耗时。
monitor大致组成部分
JDK6后锁升级
synchronized锁有四种状态,无锁、偏向锁、轻量级锁、重量级锁。这几个状态会随着竞争状态逐渐升级,锁可以升级但不能降级,但是偏向锁状态可以被重置为无锁状态。
若干名词解释
1、偏向锁:大部分时候没有锁竞争 都是同一个线程多次获得锁。偏向锁不需要做申请操作。
2、轻量级锁:竞争锁对象的线程不多且持有锁的时间不长,避免到内核态的切换,直接在用户态自选尝试获取。
3、锁消除:JVM在JIT编译时可以上下文扫描去除不必要的加锁,比如函数内部调用StringBuffer。
2、轻量级锁:竞争锁对象的线程不多且持有锁的时间不长,避免到内核态的切换,直接在用户态自选尝试获取。
3、锁消除:JVM在JIT编译时可以上下文扫描去除不必要的加锁,比如函数内部调用StringBuffer。
synchronized跟Lock区别
synchronized是关键字,JVM层面的底层啥都帮我们做了,而Lock是一个接口,是JDK层面的有丰富的API。
synchronized会自动释放锁,而Lock必须手动释放锁。
synchronized是不可中断的,Lock可以中断也可以不中断。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
synchronized能锁住方法和代码块,而Lock只能锁住代码块。
Lock可以使用读锁提高多线程读效率。
synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。
synchronized会自动释放锁,而Lock必须手动释放锁。
synchronized是不可中断的,Lock可以中断也可以不中断。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
synchronized能锁住方法和代码块,而Lock只能锁住代码块。
Lock可以使用读锁提高多线程读效率。
synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。
syn锁升级图
关于syn锁降级
synchronized 的锁升级之后不会降级,所以认为当一个锁升级为重量级锁之后,任何线程再来争抢之后会走重量级锁的逻辑。不会再从无锁到偏向锁到轻量级锁再到重量级锁。
真实结果是 抢锁称为重量级锁后,如果锁对象变为无锁后,再有个线程强锁,锁会从无锁变为轻量级锁。
真实结果是 抢锁称为重量级锁后,如果锁对象变为无锁后,再有个线程强锁,锁会从无锁变为轻量级锁。
CAS、Lock、读写锁
CAS
CAS = Conmpare And Swap,从CPU指令级别实现一个原子性指令操作。
整个J.U.C都是建立在CAS之上的
Java无法像C++一样直接操作内存都是通过native方法调用,Java中的Unsafe类给Java用户提供了了类似C++直接操作管理内存的能力。UnSafe类内部方法均是native修饰的。
CAS的弊端
ABA:解决方法是引入版本号 AtomicStampedReference
开销问题:长期不成功那就会进入自旋,JVM提供了pause指令可以有效减少自旋导致的资源浪费。
只能保证一个共享变量之间的原则性操作,JDK5后可以将多个变量放到一个对象中AtomicReference操作。
JDK中相关原子操作类的使用
CAS导致只有一个线程获得锁成功,太多线程进入自旋,JDK8 节点ConcurrentHashMap的分段锁思维引入了LongAdder。
Lock
Synchronized 跟Lock 区别
1、Synchronized 是Java内置关键字,Lock是Java接口
2、Synchronized 无法判断获取锁状态,Lock可以判断是否获取到锁了
3、Synchronized 会自动释放锁,Lock必须手动释放锁。
4、Synchronized 后来线程会傻傻等待锁,Lock锁可能存在加塞 不会苦等。
5、Synchronized 是可重入锁,不可以中断。Lock 可重入锁,可以设置锁是否公平。
6、Synchronized 适合少量代码块同步,Lock适合大量代码块。
7、Synchronized 不可timeout,Lock可以timeout
8、Synchronized 可能涉及到monitor 用户态到CPU态切换,Lock不会涉及切换,效率更好些。
2、Synchronized 无法判断获取锁状态,Lock可以判断是否获取到锁了
3、Synchronized 会自动释放锁,Lock必须手动释放锁。
4、Synchronized 后来线程会傻傻等待锁,Lock锁可能存在加塞 不会苦等。
5、Synchronized 是可重入锁,不可以中断。Lock 可重入锁,可以设置锁是否公平。
6、Synchronized 适合少量代码块同步,Lock适合大量代码块。
7、Synchronized 不可timeout,Lock可以timeout
8、Synchronized 可能涉及到monitor 用户态到CPU态切换,Lock不会涉及切换,效率更好些。
日常经常用Lock的实现类ReentrantLock
基本用法lock()、unlock()
Sychronized -> wait -> notify
Lock -> Condition.await -> Condition.signal
Lock -> Condition.await -> Condition.signal
Lock 相比Syn高明地方:指定唤醒线程
配套Condition 来阻塞跟唤醒线程
Lock可以实现公平锁跟非公平锁
公平锁:以此获取,但是获取不到锁的线程由挂起状态到CPU可运行状态相当耗时
非公平锁:Lock默认为此,来了以后都要跟尝试跟当前线程抢下锁,抢不到再走公平锁那套流程
读写锁 ReetrantReadWriteLock
锁降级:从写锁变成读锁
锁升级:从读锁变成写锁
锁升级:从读锁变成写锁
1、锁降级:ReentrantReadWriteLock 支持锁降级。
2、锁升级:ReentrantReadWriteLock 不支持锁升级。
2、锁升级:ReentrantReadWriteLock 不支持锁升级。
总结:
1、JUC中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性
2、ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字,引入如果存在多度少写情况尽量用读写锁。
3、ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,读锁跟写锁之间锁互斥模式。
4、ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁。
2、ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字,引入如果存在多度少写情况尽量用读写锁。
3、ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,读锁跟写锁之间锁互斥模式。
4、ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁。
常用若干工具类一网打尽
CountDownLatch 递减计数器
CountDownLatch(int count) //实例化一个倒计数器,count指定计数个数
countDown() // 计数减一
await() //等待,当计数减到0时,所有线程并行执行
countDown() // 计数减一
await() //等待,当计数减到0时,所有线程并行执行
一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。
CyclicBarrier:可循环栅栏
集齐多少 就开始运行Runnable方法
N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
Semaphore
控制同时访问某个特定资源的线程数据,用来流量控制。
ForkJoin
在1.7中引入JDK中 是一种分治的思想,对应大数据的MapReduce
主要思路是需要自己设置一个小job的工作范围,然后规划好切分任务后compute
工作窃取:双端队列 充分利用线程进行并行计算
JDK8底层级别优化
LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
Callable、Future、FutureTask
理解组合关系 跟使用
常见并发工具包
ConcurrentSkipListMap:基于跳表 线程安全
ConcurrentSkipListSet: 基于跳表 线程安全
ConcurrentLinkedQueue:LinkedList的多线程安全升级版
CopyOnWriteArrayList:CopyOnWrite 写时复制的容器,底层用的ReentrantLock()来实现加锁
CopyOnWriteArraySet:比上面多加了个addIfAbsent(e)
1、List -> Collections.synchronizedList() -> CopyOnWriteArrayList<>()
2、Set -> Collections.synchronizedSet() -> CopyOnWriteArraySet<>()
2、Set -> Collections.synchronizedSet() -> CopyOnWriteArraySet<>()
阻塞队列 BlockingQueue
1、队列FIFO
2、写入:如果队列满了就必须阻塞等待。
3、取值:如果队列是空的必须阻塞等待生产。
4、阻塞队列使用:多线程并发处理线程池。
2、写入:如果队列满了就必须阻塞等待。
3、取值:如果队列是空的必须阻塞等待生产。
4、阻塞队列使用:多线程并发处理线程池。
添加跟移除数据
ThreadLocal讲解
问题:多个线程同时对同一共享变量里一些属性赋值会产生不同步跟数据混乱,synchronized跟ThreadLocal均可解决此问题
ThreadLocal作用:提供线程内部的局部变量,不同线程之间不会被相互干扰
1、线程并发:在多线程并发环境下用
2、传递数据:通过ThrealLocal在同一个线程,不同组件中传递公共变量。
3、线程隔离:每个线程内变量是独立的,不会相互影响。
2、传递数据:通过ThrealLocal在同一个线程,不同组件中传递公共变量。
3、线程隔离:每个线程内变量是独立的,不会相互影响。
用时功能如上
JDK早版本设计
1、service层开启事务的connection需要跟dao层访问数据库的connection保持一致
2、线程并发的情况下,每个线程只能操作各自的connection。
JDK8设计
每一个Thread维护一个ThreadLocal.ThreadLocalMap,这个Map的key是ThreadLocal对象,value才是真正要存储的object
1、每个Map存储的KV数据变小了,以前是线程个数多则ThreadLocal存储的KV数就变多。现在的K是用ThreadLocal实例化对象来当key的,多线程情况下ThreadLocal实例化个数一般都比线程数少!
2、以前线程销毁后ThreadLocal这个Map还是存在的,现在当Thread销毁时候,ThreadLocalMap也会随之销毁,减少内存使用。
2、以前线程销毁后ThreadLocal这个Map还是存在的,现在当Thread销毁时候,ThreadLocalMap也会随之销毁,减少内存使用。
斐波那契(Fibonacci)散列法 寻址
重要三个组成:ThreadLocal、ThreadLocalMap、Entry
如果想共享线程的ThreadLocal数据
InheritableThreadLocal
为什么一般用ThreadLocal都要用Static
阿里编程规范跟源码建议:ThreadLocal对象建议使用 static修饰。这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。
内存泄露
1、ThreadLocalRef用完后Entry没有手动删除。
2、ThreadLocalRef用完后CurrentThread依然在运行ing。
2、ThreadLocalRef用完后CurrentThread依然在运行ing。
弱引用跟强引用都会导致内存泄露,不过弱引用会多一层保障,下次GC时候会回收导致key=null,ThreadLocal也会在调用set/get/remove方法适合清空无用数据。
十分钟学线程池 ThreadPool
线程池、连接池、内存池、对象池创建销毁耗时,引入池化技术
好处:线程复用、可以控制最大并发数、管理线程
好处:线程复用、可以控制最大并发数、管理线程
自定义实现简单线程池
有若干个初始化好的线程数组来充当线程池。
线程池要去一个 等待的任务队列 中去拿任务。
有若干个初始化好的线程数组来充当线程池。
线程池要去一个 等待的任务队列 中去拿任务。
三大方法、七大参参数、四大拒绝策略
阿里编程建议:
线程池的使用不要用 Executors(底层调用也是ThreadPoolExecutor),要通过ThreadPoolExecutor创建,这样可以明确线程池运行规则,Executors创建的线程池允许的请求队列最大值是int最大值,容易导致OOM
三大方法
1、 单个线程池 Executors.newSingleThreadExecutor();
2、 固定大小的线程池 Executors.newFixedThreadPool(5);
3、 弹性线程池 Executors.newCachedThreadPool();
2、 固定大小的线程池 Executors.newFixedThreadPool(5);
3、 弹性线程池 Executors.newCachedThreadPool();
7大参数
1、corePoolSize:核心线程池大小。
2、maximumPoolSize:表示允许的最大线程数 = (非核心线程数+核心线程数),当BlockingQueue也满了,但线程池中总线程数 < maximumPoolSize时候就会再次创建新的线程。
3、keepAliveTime:非核心线程 =(maximumPoolSize - corePoolSize ) ,非核心线程闲置下来不干活最多存活时间。
4、unit:线程池中非核心线程保持存活的时间,比如 TimeUnit.MINUTES
5、workQueue:线程池 等待队列,维护着等待执行的Runnable对象。当运行当线程数= corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务,另外等待队列尽量用有界的哦!!
6、threadFactory:创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
7、handler:corePoolSize、workQueue、maximumPoolSize都不可用的时候执行的 饱和策略。
2、maximumPoolSize:表示允许的最大线程数 = (非核心线程数+核心线程数),当BlockingQueue也满了,但线程池中总线程数 < maximumPoolSize时候就会再次创建新的线程。
3、keepAliveTime:非核心线程 =(maximumPoolSize - corePoolSize ) ,非核心线程闲置下来不干活最多存活时间。
4、unit:线程池中非核心线程保持存活的时间,比如 TimeUnit.MINUTES
5、workQueue:线程池 等待队列,维护着等待执行的Runnable对象。当运行当线程数= corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务,另外等待队列尽量用有界的哦!!
6、threadFactory:创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
7、handler:corePoolSize、workQueue、maximumPoolSize都不可用的时候执行的 饱和策略。
4大拒绝策略
1、AbortPolicy :直接抛出异常,默认用此
2、CallerRunsPolicy:用调用者所在的线程来执行任务
3、DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
4、DiscardPolicy :当前任务直接丢弃
5、想实现自己的饱和策略,实现RejectedExecutionHandler接口即可
2、CallerRunsPolicy:用调用者所在的线程来执行任务
3、DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
4、DiscardPolicy :当前任务直接丢弃
5、想实现自己的饱和策略,实现RejectedExecutionHandler接口即可
提交
execute
无返回值,会抛出异常,入参为Runnable
submit
返回Future<T>类型值,入参可以是Runnable或Callable,不会抛异常除非调用返回Future.get()
ThreadPoolExecutor中一个线程就是一个Worker对象,它与一个线程绑定,当Worker执行完毕就是线程执行完毕。而Worker带了锁AQS
关闭线程池
注意线程之间是协作式的哦,所以的关闭只是发出关闭指令。
优雅的关闭,用shutdown()
想立马关闭,并得到未执行任务列表,用shutdownNow()
优雅的关闭,发出关闭指令后看下是否真的关闭了用awaitTermination()。
想立马关闭,并得到未执行任务列表,用shutdownNow()
优雅的关闭,发出关闭指令后看下是否真的关闭了用awaitTermination()。
合理配置线程池
计算密集型:大部分都在用CPU跟内存,加密,逻辑操作业务处理等
Runtime.getRuntime().availableProcessors() + 1 ,+1是因为可能存在页缺失(就是可能存在有些数据在硬盘中需要多来一个线程将数据读入内存)。如果线程池数太大,可能会频繁的 进行线程上下文切换跟任务调度
IO密集型:数据库链接,网络通讯传输等。
线程数适当大一点,机器的Cpu核心数*2。
demo使用
JDK8 ExecutorCompletionService
内部维护了一个阻塞队列,可以让先执行的任务放到结果集。 = Executor + BlockingQueue
设定一个线程池优先级队列
Runable类要实现可对比功能,任务队列使用优先级队列
单机线程池停电应对方案:正在处理的实现事务功能,下次自动回滚,队列实现持久化储存,下次启动自动载入。
提高逼格的JMM(Java Memory Model)
并发编程时经常会涉及到线程之间的通信跟同步问题,一般抽象说是可见性、原子性、有序性。对应带实际中就是缓存一致性、处理器优化、指令重排问题。
原子性:指在一个操作cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
线程之间通信机制
共享内存:线程之间共享程序的公共数据状态
Java中主要依靠 共享内存 跟 wait notify
消息传递:管道Pipe、消息队列MQ、套接字Socket、信号量、信号
了解现代计算机物理上的内存模型
数据存储容量依次增加,访问速度依次降低:CPU => 寄存器 => 高速缓存区 => 内存 => 硬盘
为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。
内存模型解决并发问题方式:限制处理器优化和使用内存屏障。
JMM是约定概念,来保证效果一致的机制跟规范
1、线程解锁前,必须把共享变量立刻刷回主存。
2、线程加锁前,必须读取主存中的最新值到工作内存中!
3、加锁和解锁是同一把锁
2、线程加锁前,必须读取主存中的最新值到工作内存中!
3、加锁和解锁是同一把锁
JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步
指令重排
1、编译器优化的重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2、指令级并行的重排序:现代处理器采用了指令级并行技术在不影响数据依赖性前提下重排。
3、内存系统的重排序:处理器使用缓存和读/写缓冲区 进程重排。
2、指令级并行的重排序:现代处理器采用了指令级并行技术在不影响数据依赖性前提下重排。
3、内存系统的重排序:处理器使用缓存和读/写缓冲区 进程重排。
Java中提出 as-if-serial:不管如何重排序 都必须保证单线程代码执行的准确性跟幂等性
Java中提出 happens-before:用来阐述操作之间的内存可见性,及两个操作直接前后依赖性
内存屏障(Memory Barrier):是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。
LoadLoad 屏障
StoreStore 屏障
LoadStore 屏障
StoreLoad 屏障
StoreStore 屏障
LoadStore 屏障
StoreLoad 屏障
第一个为读操作时,第二个任何操作不可重排序到第一个前面。
第二个为写操作时,第一个任何操作不可重排序到第二个后面。
第一个为写操作时,第二个的读写操作也不运行重排序。
第二个为写操作时,第一个任何操作不可重排序到第二个后面。
第一个为写操作时,第二个的读写操作也不运行重排序。
整体意思就是你看到的结果跟你写的代码 JVM底层只是保证最终逻辑一致性,中间如何执行的JVM加速、重排、一致性 都是内部解决的事
深入理解Java虚拟机 :java并发内存模型以及内存操作规则8种原子操作
volatile由浅入深讲解
volatile 是 Java 虚拟机提供轻量级的同步机制
1、保证可见性
对一个volatile变量的读,总是从共享内存中读取最新的数据来用
2、不保证原子性
对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
应该用JUC下的 Atomic*
AtomicInteger
AtomicLong
JDK8 使用LongAdder替换AtomicLong,一种原子性阶段性锁
3、禁止指令重排
指令重排定义:你写的程序,计算机并不是按照你写的那样去执行的。处理器在进行指令重排的时候,考虑:数据之间的依赖性!
读写理解
写理解:当写一个volatile变量时,JMM会把该线程对应的本地中的共享变量值刷新到主内存。
写底层实现:在每个volatile写操作的前面插入一个StoreStore屏障。在每个volatile写操作的后面插入一个StoreLoad屏障。
读理解:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
读底层:在每个volatile读操作的后面插入一个LoadLoad屏障。在每个volatile读操作的后面插入一个LoadStore屏障。
volatile的实现原理
volatile修饰的变量进行写操作的时候会使用CPU提供的Lock前缀指令 来实现上面说的读写理解功能
请讲述下java的volatile会在寄存器发一个22伏的低电平的量子运动过程?
JUC的核心AQS(AbstractQueuedSynchronizer 抽象队列同步器)
重要性:CountDownLatch、ThreadPoolExecutor、ReentrantLock、读写锁等,几乎JUC中显示锁底层都是用的这个。AQS是模板设计模式哦
前置知识点
模板方法
LockSupport 线程阻塞工具包
cas操作
独占锁
尝试获取锁
1、调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
2、没成功,则addWaiter()将该线程以CAS方式加入等待队列的尾部,并标记为独占模式;
3、acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark(),用到线程阻塞工具类LockSupport)会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
4、如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
5、每个等待线程都进入到自旋状态了 这点需明白。
2、没成功,则addWaiter()将该线程以CAS方式加入等待队列的尾部,并标记为独占模式;
3、acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark(),用到线程阻塞工具类LockSupport)会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
4、如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
5、每个等待线程都进入到自旋状态了 这点需明白。
释放锁
release()是独占模式下线程释放共享资源的顶层入口。它会释放指定量的资源tryRelease,如果彻底释放了(即state=0),它会唤醒等待队列里的后面最近能用的线程来获取资源。
非独占锁
尝试获取锁
tryAcquireShared()需自己实现不过返回值中负值代表获取失败;0代表获取成功,但没有剩余资源;正数表示获取成功,还有剩余资源,其他线程还可以去获取
1、tryAcquireShared()尝试获取资源,成功则直接返回;
2、失败则通过doAcquireShared()进入等待队列park(),直到被unpark()/interrupt()并成功获取到资源才返回。整个等待过程也是忽略中断的。
3、其实跟acquire()的流程大同小异,只不过多了个自己拿到资源后,还会去唤醒后继队友的操作(这才是共享嘛)。
2、失败则通过doAcquireShared()进入等待队列park(),直到被unpark()/interrupt()并成功获取到资源才返回。整个等待过程也是忽略中断的。
3、其实跟acquire()的流程大同小异,只不过多了个自己拿到资源后,还会去唤醒后继队友的操作(这才是共享嘛)。
释放锁
releaseShared() :释放掉资源后,唤醒后继。后面的需要5个如果只有空闲4个还是会继续等待资源。
AQS底层
AQS底层其实是一个 CLH 双向链表,抢锁的排队获取而已
总结:
同步类在实现时一般都将自定义同步器(sync)定义为内部类,供自己使用;而同步类自己(Mutex)则实现某个接口,对外服务。当然,接口的实现要直接依赖sync,它们在语义上也存在某种对应关系!!而sync只用实现资源state的获取-释放方式tryAcquire-tryRelelase,至于线程的排队、等待、唤醒等,上层的AQS都已经实现好了,我们不用关心。
除了Mutex,ReentrantLock/CountDownLatch/Semphore这些同步类的实现方式都差不多,不同的地方就在获取-释放资源的方式tryAcquire-tryRelelase。
除了Mutex,ReentrantLock/CountDownLatch/Semphore这些同步类的实现方式都差不多,不同的地方就在获取-释放资源的方式tryAcquire-tryRelelase。
1. 在AQS 同步队列中 -1 表示线程在睡眠状态
2. 当前Node节点线程会把前一个Node.ws = -1。当前节点把前面节点ws设置为-1,你可以理解为:你自己能知道自己睡着了吗? 只能是别人看到了发现你睡眠了!
3. 持有锁的线程永远不在队列中。
4. 在AQS队列中第二个才是最先排队的线程。
5. 如果是交替型任务或者单线程任务,即使用了Lock也不会涉及到AQS 队列。
6. 不到万不得已不要轻易park线程,很耗时的!所以排队的头线程会自旋的尝试几个获取锁。
2. 当前Node节点线程会把前一个Node.ws = -1。当前节点把前面节点ws设置为-1,你可以理解为:你自己能知道自己睡着了吗? 只能是别人看到了发现你睡眠了!
3. 持有锁的线程永远不在队列中。
4. 在AQS队列中第二个才是最先排队的线程。
5. 如果是交替型任务或者单线程任务,即使用了Lock也不会涉及到AQS 队列。
6. 不到万不得已不要轻易park线程,很耗时的!所以排队的头线程会自旋的尝试几个获取锁。
Condition
用来替换wait nitify, wait()/notify()这些都更倾向于底层的实现开发,而Condition接口更倾向于代码实现的等待通知效果
一个Condition对象就有一个单项的等待任务队列
多线程任务中我们可以new出多个等待任务队列,所以真正的AQS任务中一般是一个任务队列N个等待队列的。
调用await方法时候。说明当前任务队列的头节点拿着锁呢,此时要把该Thread从任务对列挪到等待队列里
调用signal方法的时候,将等待队列中的头节点移出来,让其去抢锁,不过如果是抢的公平对列就是排队了
Condition已经实现了多个实例化的等待队列,因此我们尽量调用signal而少用signalAll
ReentrantLock
锁的可重入性
ReentrantLock的锁是可重入的不管是公平锁还是非公平锁,我们以默认的非公平锁为例看下获取的状态。可以发现当进行可重入的时候锁的数据是不断增加的,同时当进行释放的时候锁的个数是不断减少的。
ReentrantLock源码
公平跟非公平
非公平:当前进程尝试获取锁的时候,如果发现锁的状态位是0的话就直接尝试将锁拿过来,然后setExclusiveOwnerThread,根本不管其他原因。
公平锁:当前进程来来后先看一看前面是否有前驱节点。
ReentrantReadWriteLock
只有一个32位的int state来表示读锁跟写锁
ConcurrentHashMap
HashTable:synchronized关键字来修饰方法,Key不可为null。过于重量级的锁。
提速
JDK7
Segment数组 + HashEntry数组 :可以理解为每个segment都是实现了 ReentrantLock 功能的HashMap。如果我们同时有多个segment形成了segment数组那我们就可以实现并发咯。
size() :先无锁的统计下所有的数据量看下前后两次是否数据一样,如果一样则返回数据,如果不一样则要把全部的segment进行加锁,统计
缺点:并发程度是有segment数组来决定的,并发度一旦初始化无法扩容。rehash只能是table变化
弱一致性体现在迭代器,clear和get方法,原因在于没有加锁。
JDK8
1、取消了segment数组,直接用table保存数据,锁的粒度更小,减少并发冲突的概率。采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率,并发控制使用 Synchronized 和 CAS 来操作。其中syn锁数组节点,cas变更数据
2、存储数据时采用了数组+ 链表+红黑树的形式。
2、存储数据时采用了数组+ 链表+红黑树的形式。
helpTransfer 插入时找到的节点如果发现正在扩容还要去帮忙扩容,骚操作
Contended避免伪共享:缓存系统一般以64字节为一行单位存储数据,如果多线程修改的变量都在同一行则会影响彼此性能。
使用一个volatile类型的变量baseCount记录元素的个数
sizeCtl
负数:表示进行初始化或者扩容,-1:表示正在初始化,-N:表示有 N-1 个线程正在进行扩容
正数:0 表示还没有被初始化,> 0的数:初始化或者是下一次进行扩容的阈值,有点类似HashMap中的threshold,不过功能更强大。
正数:0 表示还没有被初始化,> 0的数:初始化或者是下一次进行扩容的阈值,有点类似HashMap中的threshold,不过功能更强大。
5个构造函数
CAS + synchronized + volatile
JUC 官网
JVM
懂原理的码农才是好coder
推荐 周志明:深入理解Java虚拟机
JVM整体来说知识点比较死相对来说 比JUC简单
01 Java内存区域
引言:不同公司会自研不同的JVM,目前开源主流是 HotSpot,国内阿里自研的TaobaoJVM也很优秀
JVM发展趋势
JVM8 内存区
私有区:编译时需确定下大小
程序计数器:记录当前线程执行的字节码的行号指示器
Java栈(stack):线程私有,生命周期跟线程,每个方法都会在执行的时候创建一个栈桢用来存储局部变量
本地方法栈:本地方法栈保存的是native方法信息,当一个JVM创建一个线程调用native方法后
共有区
堆(heap):涉及到内存的分配 (new关键字,反射等)与回收(回收算法,收集器等)
方法区:也叫永久区,用于存储已经被虚拟机加载的类信息,常量 (“zdy”,"123"等),静态变量(static变量)等数据
运行时常量池:方法区的一部分,用于存放编译期生成 的各种字面量(“zdy”,"123"等)和符号引用
元空间
在Java中用方法区(永久代)来存储类信息,常量,静态变量等数据不是好注意,因为这样很容易造成内存溢出。同时对永久代的性能调优也很困难,因此1.8以后引入了元空间来存储这些本来在方法区的东西,主要优势就是不在JVM机中占有空间了
Java中的对象都是在堆中分配吗?
不一定看对象经过了逃逸分析跟标亮替换后发现该变量只是用到方法区呢则JVM会自动优化,在栈上创建该对象
逃逸分析(Escape Analysis):简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存
标量替换:JVM通过逃逸分析确定该对象不会被外部访问。那就通过将该对象标量替换分解在栈上分配内存,这样该对象所占用的内存空间就可以随栈帧出栈而销毁,就减轻了垃圾回收的压力。
虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。如果对象经过层层分析后发现 无法进行逃逸分析优化则反而耗时了,因此慎用。
对象内存布局
对象头(Header)
1、第一部分Mark word 8个字节 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
2、另外一部分是class pointer 类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,默认压缩4字节,不压缩则8字节
2、另外一部分是class pointer 类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,默认压缩4字节,不压缩则8字节
实例数据(Instance Data):new 方法中赋值数据
对齐填充(Padding)
JVM要求对象大小为8字节倍数,如果不满足则填充
Object o = new Object 占据几个字节
对象访问
使用句柄
使用句柄访问的话,那么Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。类似一个中间件
直接指针(HotSpot默认方式)
使用直接指针访问, reference中存储的直接就是对象地址。
02 垃圾回收器和内存分配策略
内存回收
引用计数法:无法解决 相互引用问题
可达性分析:主流商用语言采用的方法
1、当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达。只是不可达还未宣判死亡
2、第一次标记和筛选 将需要执行finalize() 的对象放到一个 待处理队列中 让第二次标记筛选处理
3、当对象经过了第一次的标记 & 筛选,会被进行第二次标记 & 准备被进行 筛选
流程图
可作为GC Roots 的对象
1、 在堆上正常创建的对象
2、 用static修饰的对象
3、 用static final修饰的常量池中的对象
4、 引用Native的对象
2、 用static修饰的对象
3、 用static final修饰的常量池中的对象
4、 引用Native的对象
引用:GC Root分析到到各种引用对象也有不同到引用级别
强引用:一般的Object obj = new Object() ,就属于强引用
软引用:SoftReference 系统将要发生OOM之前,这些对象就会被回收
弱引用:WeakReference :只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
弱引用跟强引用 应用到 ThreadLocal底层内存泄露
虚引用:PhantomReference:幽灵引用,最弱,object.fun() 都无法执行 。 被垃圾回收的时候收到一个通知
GC 算法理论
标记-清除算法(Mark-Sweep):算法分为标记和清除两个阶段,标记清除之后会产生大量不连续的内存碎片
复制算法(Copying):可用内存按容量划分为大小相等的两块,每次只用一块
标记-整理算法(Mark-Compact):首先标记出所有需要回收的对象,后续步骤让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
GC处理的是heap区
年轻代 Minor GC:复制算法
Eden Space(伊甸园) 跟 Survivor Space(分别叫from和to) 默认比例为8:1:1
总会保证名为To的Survivor区域是空的
默认16次存活 才放到年老代,
年老代 Major GC:标记—清理 或 者标记—整理
新生代 跟老年代大小占比 1:2
元数据区:不会被GC 用来存储 Class和Meta的信息
Java代码GC的时候 会STW(Stop the World)。也就是停止所有的工作线程
垃圾回收器
吞吐量:运行用户代码时间/(运行用户代码时间+ 垃圾收集时间)
垃圾收集时间:垃圾回收频率 * 单次垃圾回收时间
垃圾收集时间:垃圾回收频率 * 单次垃圾回收时间
各种GC器
各种GC概述
新生代
别的大部分关注 STW间隔时间
Parallel Scavenge关注的是 吞吐量
Parallel Scavenge关注的是 吞吐量
老年代
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,具有自适应调整策略,适合互联网站 跟B/S 服务应用
CMS收集器的优点:并发收集、低停顿,但是CMS还远远达不到完美,主要有三个显著缺点:cpu敏感,浮动垃圾,空间碎片
CMS收集器的优点:并发收集、低停顿,但是CMS还远远达不到完美,主要有三个显著缺点:cpu敏感,浮动垃圾,空间碎片
划时代的 CMS
优点:并发收集、低停顿
缺点:cpu敏感,浮动垃圾,空间碎片
标记-清除算法
1、初始标记(CMS initial mark) : 引发STW 仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快
2、并发标记(CMS concurrenr mark):正常运行 所有Old 对象是否可链到GC Roots
3、重新标记(CMS remark):引发STW 为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,
这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。还会产生浮动垃圾
4、并发清除(CMS concurrent sweep):正常运行,标记清除算法,内存碎片。
2、并发标记(CMS concurrenr mark):正常运行 所有Old 对象是否可链到GC Roots
3、重新标记(CMS remark):引发STW 为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,
这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。还会产生浮动垃圾
4、并发清除(CMS concurrent sweep):正常运行,标记清除算法,内存碎片。
为何采用清除算法?CMS主要关注低延迟,因而采用并发方式,清理垃圾时,应用程序还在运行,如何采用压缩算法,则涉及到要移动应用程序的存活对象,此时不停顿,是很难处理的,一般需要停顿下,移动存活对象,再让应用程序继续运行,但这样停顿时间变长,延迟变大,所以CMS采用清除算法。
优点:并发收集、低停顿
缺点:cpu敏感,浮动垃圾,空间碎片
老年代引用新生代 用到了 card table来加速GC
是一种points-out(我引用了谁的对象)的结构
整个过程耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以整体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
CMS 如何治理 内存碎片
-XX:+UseCMSCompactAtFullCollection 开启CMS期间内存碎片整合 耗时性会增大
-XX:CMSFullGCsBefore-Compaction 作用是要求CMS收集器在执行过若干次(数量由参数值决定)不整理空间的Full GC之后 下一次进入Full GC前会先进行碎片整理(默认值为0,表示每次进入Full GC时都进行碎片整理)。
jdk1.7跟1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1
jdk1.9 默认垃圾收集器G1
常见组合就是 ParNew + CMS
G1
STW
存在的原因:默认用ParNew+CMS组合垃圾回收期,进行新生代和老年代的垃圾回收都会导致STW现象,大大影响了系统运行的性能。为了减少STW对系统的影响,G1提供了更好的垃圾回收性能。
将堆内存分为若干Region
2000个Region,每个1~32M
2000个Region,每个1~32M
四种不同标签的Region:Eden、 Survivor 、Old、 Humongous(Old区的一种)。
<=0.5 Region <= 1 Region 存储到 H区
> 1 Region 存储到连续的H区
RememberSets: RSet 记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)
CollectionSets:Csets 是一次GC中需要被清理的regions集合,注意G1每次GC不是全部region都参与的,可能只清理少数几个,这几个就被叫做Csets。
Yang GC
1. 复制活跃对象的时间
2. 扫描card table(老年代对象引用新生代对象)的时间
2. 扫描card table(老年代对象引用新生代对象)的时间
标准的年轻代GC算法
借鉴CMS 老年代引用新生代 用到了 card table来加速GC
Mixed GC
回收内存区域:所有新生代里的Region + 在用户指定的开销目标范围内根据global concurrent marking统计得出收集收益高的若干老年代Region
全局并发标记:目的就是统计region使用情况
1、初始标记 Initial Mark :是STW事件,其完成工作是标记GC ROOTS 直接可达的对象。并将它们的字段压入扫描栈(marking stack)中等到后续扫描
2、根区域扫描 Root Region Scanning :不是STW事件,从Survior区的对象出发,标记被引用到老年代中的对象。该步骤必须在YGC开始前完成。
3、并发标记 Concurrent Marking : 同CMS并发标记 不需要STW,遍历范围减少,在此只需要遍历 第二步 被标记到引用老年代的对象 RSet。
4、Remark 最终标记 : 同 CMS 重新标记,用的SATB操作,STW 速度快
5、Cleanup 清除 :STW操作,用复制清理算法,清点出有存活对象的Region和没有存活对象的Region(Empty Region),更新Rset。把Empty Region收集起来到可分配Region队列。
2、根区域扫描 Root Region Scanning :不是STW事件,从Survior区的对象出发,标记被引用到老年代中的对象。该步骤必须在YGC开始前完成。
3、并发标记 Concurrent Marking : 同CMS并发标记 不需要STW,遍历范围减少,在此只需要遍历 第二步 被标记到引用老年代的对象 RSet。
4、Remark 最终标记 : 同 CMS 重新标记,用的SATB操作,STW 速度快
5、Cleanup 清除 :STW操作,用复制清理算法,清点出有存活对象的Region和没有存活对象的Region(Empty Region),更新Rset。把Empty Region收集起来到可分配Region队列。
回收步骤总结
1、经过global concurrent marking,collector就知道哪些Region有存活的对象。并将那些完全可回收的Region(没有存活对象)收集起来加入到可分配Region队列,实现对该部分内存的回收。对于有存活对象的Region,G1会根据统计模型找处收益最高、开销不超过用户指定的上限的若干Region进行对象回收。这些选中被回收的Region组成的集合就叫做collection set 简称Cset!
2、在MIX GC中的Cset = 所有年轻代里的region + 根据global concurrent marking统计得出收集收益高的若干old region。
3、在YGC中的Cset = 所有年轻代里的region + 通过控制年轻代的region个数来控制young GC的开销。
4、YGC 与 MIXGC 都是采用多线程复制清除,整个过程会STW。 G1的低延迟原理在于其回收的区域变得精确并且范围变小了。
2、在MIX GC中的Cset = 所有年轻代里的region + 根据global concurrent marking统计得出收集收益高的若干old region。
3、在YGC中的Cset = 所有年轻代里的region + 通过控制年轻代的region个数来控制young GC的开销。
4、YGC 与 MIXGC 都是采用多线程复制清除,整个过程会STW。 G1的低延迟原理在于其回收的区域变得精确并且范围变小了。
相比CMS的不同点
1、region化的内存结构,采用复制清理的方式,避免了内存碎片。但是这种清理也造成了STW。
2、SATB速度更快。
3、初始标记,并发标记,重新标记,清理垃圾四个阶段很像,但是G1中有很多标记region的操作,并借助Rset进行了范围的缩小,提高了并发标记的速度。
2、SATB速度更快。
3、初始标记,并发标记,重新标记,清理垃圾四个阶段很像,但是G1中有很多标记region的操作,并借助Rset进行了范围的缩小,提高了并发标记的速度。
G1提速点
1 重新标记时X区域直接删除。
2 Rset降低了扫描的范围,上题中两点。
3 重新标记阶段使用SATB速度比CMS快。
4 清理过程为选取部分存活率低的Region进行清理,不是全部,提高了清理的效率。
2 Rset降低了扫描的范围,上题中两点。
3 重新标记阶段使用SATB速度比CMS快。
4 清理过程为选取部分存活率低的Region进行清理,不是全部,提高了清理的效率。
一句话总结:每次选择性的清理大部分垃圾来保证时效性跟系统的正常运行
G1跟CMS对比
JDK11 ZGC
为大内存、多cpu而生有分区的思想,把内存区分为若干个小区域,然后每次只是打扫几个占用空间较大的区域,
新技术:彩色指针和负载障碍
内存回收跟分配策略
1、优先在Eden上分配对象,此区域垃圾回收频繁速度还快。
2、大对象直接进入老生代。
3、年长者 (长期存活对象默认15次) 跟 进入老生代。
4、在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象会群体进入老生代。
5、空间分配担保(担保minorGC),如果Minor GC后 Survivor区放不下新生代仍存活的对象,把Suvivor 无法容纳的对象直接进人老年代。
2、大对象直接进入老生代。
3、年长者 (长期存活对象默认15次) 跟 进入老生代。
4、在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象会群体进入老生代。
5、空间分配担保(担保minorGC),如果Minor GC后 Survivor区放不下新生代仍存活的对象,把Suvivor 无法容纳的对象直接进人老年代。
JVM工具
jps : 列出当前机器上正在运行的虚拟机进程
jstat : 用于监视虚拟机各种运行状态信息的命令行工具。
JConsole 跟 VisualVM 相当于前面的工具综合体,一般这两个可以更加直观查看内存信息
JVM常见面试题
关于调优
一般项目加个xms和xmx参数就够了。在没有全面监控、收集性能数据之前,调优就是瞎调
03 JVM执行子系统
*.class文件知识
1、*.class 文件是Java跨平台的基础
2、*.class并不一定以磁盘文件的,可以在内存跟数据库中
3、Class 文件是一组以8位字节为基础单位的二进制流。
4、Class文件格式类似C语言中结构体
2、*.class并不一定以磁盘文件的,可以在内存跟数据库中
3、Class 文件是一组以8位字节为基础单位的二进制流。
4、Class文件格式类似C语言中结构体
类加载机制
1、加载(Loading)
查找并加载类的二进制数据
1、通过一个类的全限定名来获取其定义的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
2、连接(Linking)
2.1、验证(Verification)
文件格式验证、元数据验证、字节码验证、符号引用验证
2.2、准备(Preparation)
给类静态变量分配内存空间,仅仅是分配空间,比如 public static int age = 14,在准备后age = 0,
在初始化阶段age=14,如果添加了final则在这个阶段直接赋值为14
在初始化阶段age=14,如果添加了final则在这个阶段直接赋值为14
2.3、解析(Resolution)
将常量池内的符号引用替换为直接引用的过程
String str = "sowhat";
System.out.println("String" + str); 变成System.out.println("String" + "sowhat");
System.out.println("String" + str); 变成System.out.println("String" + "sowhat");
3、初始化(Initialization)
前面在加载类阶段 用户应用程序可以通过自定义类加载器参与之外其余动作完全由虚拟机主导和控制。
真正开始执行类中定义的Java程序代码,执行程序员通过程序制定的主观计划去初始化类变量和其他资源 = 编译器收集 类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的
真正开始执行类中定义的Java程序代码,执行程序员通过程序制定的主观计划去初始化类变量和其他资源 = 编译器收集 类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的
4、使用(Using)
日常使用
5、卸载(Unloading)
1、执行了System.exit()方法
2、程序正常执行结束
3、程序在执行过程中遇到了异常或错误而异常终止
4、由于操作系统出现错误而导致Java虚拟机进程终止
2、程序正常执行结束
3、程序在执行过程中遇到了异常或错误而异常终止
4、由于操作系统出现错误而导致Java虚拟机进程终止
加载图
类加载器(ClassLoder)
在连接阶段我们一般是无法干预的,我们大部分干预的阶段 类加载阶段
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性
重要方法
1、loadClass() : 加载目标类的入口,它首先会查找当前 ClassLoader 以及它的双亲里面是否已经加载了目标类,找到直接返回
2、findClass() : 如果没有找到就会让双亲尝试加载,如果双亲都加载不了,就会调用 findClass() 让自定义加载器自己来加载目标类
3、defineClass() : 拿到这个字节码之后再调用 defineClass() 方法将字节码转换成 Class 对象。
2、findClass() : 如果没有找到就会让双亲尝试加载,如果双亲都加载不了,就会调用 findClass() 让自定义加载器自己来加载目标类
3、defineClass() : 拿到这个字节码之后再调用 defineClass() 方法将字节码转换成 Class 对象。
类加载机制:双亲委派机制
定义:当某个类加载器需要加载某个.class文件时,首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
作用:
1、可以防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心*.class不能被篡改,通过委托方式,不会去篡改核心.class
1、可以防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心*.class不能被篡改,通过委托方式,不会去篡改核心.class
常见加载器
1、BootstrapClassLoader(启动类加载器):c++编写,加载java核心库 java.*
2、ExtClassLoader (标准扩展类加载器):java编写,加载扩展库
3、AppClassLoader(系统类加载器):加载程序所在的目录,如user.dir所在的位置的class
4、CustomClassLoader(用户自定义类加载器):用户自定义的类加载器,可加载指定路径的class文件
2、ExtClassLoader (标准扩展类加载器):java编写,加载扩展库
3、AppClassLoader(系统类加载器):加载程序所在的目录,如user.dir所在的位置的class
4、CustomClassLoader(用户自定义类加载器):用户自定义的类加载器,可加载指定路径的class文件
双亲委派机制只是Java类加载的一种模式,但是当我们使用一些第三方框架的时候比如JDBC跟具体实现的时候,反而会引发错误,因为JDK自带的JDBC接口由启动类加载,而第三方实现接口由应用类加载。这样相互之间是不认识的,因此JDK引入了线程上下文加载器来实现用同一个加载器加载。
Tomcat 总是先尝试去加载某个类,如果找不到再用上一级的加载器,这跟一般类加载器顺序正好相反。
Tomcat 总是先尝试去加载某个类,如果找不到再用上一级的加载器,这跟一般类加载器顺序正好相反。
栈帧(Stack Frame)执行过程
用 javap -c *.class 看下底层
基于寄存器的字节指令一般由于硬件不同会有些许差异不过因为直接跟硬件打交到因此速度更快些。
基于栈的字节码解释执行指令,速度慢点但是可移植。
基于栈的字节码解释执行指令,速度慢点但是可移植。
JVM方法调用
编译期确定下来的调用对象
运行期确定下来的调用对象,类似C++中的多态:多态的实现机制就是父类跟子类各自有一个方法表,然后会选择性调用
04 深入了解性能优化
性能指标
1、响应时间:提交请求时跟请求返回之间的使用时间
2、并发数:同一时刻对服务器有实际交互的请求数
3、吞吐量:单位时间内完成对工作量的度量
2、并发数:同一时刻对服务器有实际交互的请求数
3、吞吐量:单位时间内完成对工作量的度量
Jmeter性能测试工具
前端优化
1、合并CSS/JS/图片 这些数据,一次性传送给客户端。
2、对于CSS/JS/IMG使用客户端缓冲
3、服务器端对JS/CSS等文件进行gzip压缩
4、资源文件加载顺序 一般是CSS放最前面,JS放最后面
5、给予用户友善提示
2、对于CSS/JS/IMG使用客户端缓冲
3、服务器端对JS/CSS等文件进行gzip压缩
4、资源文件加载顺序 一般是CSS放最前面,JS放最后面
5、给予用户友善提示
后端优化
1、CDN加速 或者Nginx 反向代理
2、缓存比如Redis 跟 memcached
3、集群
4、异步
5、代码级别优化
6、并发编程:JUC合理使用
7、资源复用:数据库连接跟Spring中的Bean等
2、缓存比如Redis 跟 memcached
3、集群
4、异步
5、代码级别优化
6、并发编程:JUC合理使用
7、资源复用:数据库连接跟Spring中的Bean等
CDN :联想到京东物流存储系统, 本质是将媒体资源、图片资源、HTML、CSS、JS内容缓存到距离用户更近的互联网数据中心(Internet Data Center,简称IDC),让用户进行资源共享,实现缩减响应时间请求。
JIT(just in time )
功能:将运行频率很高的字节码直接编译为机器指令执行以提高性能,所以当字节码被 JIT 编译为机器码的时候,此时可以认为是编译型语言类。
热点编译的阈值由 方法调用计数器 跟 循环回边计数器 两个和来定,一定时间段内,会有计数器的衰减的
热点编译的阈值由 方法调用计数器 跟 循环回边计数器 两个和来定,一定时间段内,会有计数器的衰减的
模式
Client 模式:当虚拟机运行在-client 模式的时候,启动快,使用的是一个代号为 C1 的轻量级编译器
Server 模式:–server 模式启动的虚拟机采用相对重量级代号为 C2 的编译器。速度较慢,但是一旦运行起来后,性能将会有很大的提升。C2 比 C1 编译器编译的相对彻底,服务起来之后,性能更高。
方法内联
把函数调用的方法直接内嵌到方法内部,减少函数调用的次数。内联默认开启,如果关闭了性能大概降速50%。
public void SetAge(int age){
this.age = age
}
-------JIT 优化后直接就是 如下,避免类方法调用。
this.age = age
this.age = age
}
-------JIT 优化后直接就是 如下,避免类方法调用。
this.age = age
GC调优
目的:
GC时间够少
GC次数够少
GC次数够少
方法:
1、选择合适的GC回收器来组合。
2、选择合适的堆大小。
3、选择合适年轻代在堆中的比例。
4、尽可能减少full GC 的发生跟耗时。
2、选择合适的堆大小。
3、选择合适年轻代在堆中的比例。
4、尽可能减少full GC 的发生跟耗时。
指标:
1、minor GC 单次小于50ms,频率10秒以上。说明年轻代OK。
2、Full GC 单次小于1秒一下,频率10分钟以上,说明年老代OK。
2、Full GC 单次小于1秒一下,频率10分钟以上,说明年老代OK。
常用参数:
-Xms5m设置JVM初始堆为5M,-Xmx5m设置JVM最大堆为5M。-Xms跟-Xmx值一样时可以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G,一般默认为整个堆区的1/3 ~ 1/4。-Xss每个线程栈空间设置。
-XX:SurvivorRatio,设置年轻代中Eden区与Survivor区的比值,默认=8,比值为8:1:1。
-XX:+HeapDumpOnOutOfMemoryError 当JVM发生OOM时,自动生成DUMP文件。
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold 设定对象在Survivor区最大年龄阈值,超过阈值转移到老年代,默认15。
开启GC日志对性能影响很小且能帮助我们定位问题,-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log
-Xmn2g:设置年轻代大小为2G,一般默认为整个堆区的1/3 ~ 1/4。-Xss每个线程栈空间设置。
-XX:SurvivorRatio,设置年轻代中Eden区与Survivor区的比值,默认=8,比值为8:1:1。
-XX:+HeapDumpOnOutOfMemoryError 当JVM发生OOM时,自动生成DUMP文件。
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold 设定对象在Survivor区最大年龄阈值,超过阈值转移到老年代,默认15。
开启GC日志对性能影响很小且能帮助我们定位问题,-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log
常用策略
1、新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC
2、日志分析新生代空间大小分配是否合理,适当通过-Xmn命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。
3、对于大对象,如果直接在年轻代分配会导致大多年轻代小对象被拥挤到年老代,此时要合理设置-XX:PretenureSizeThreshold 参数,启动空间分配担保。
4、合理设置进入老年代对象的年龄,-XX:MaxTenuringThreshold
5、设置稳定的堆大小,堆大小设置有两个参数:-Xms 初始化堆大小,-Xmx 最大堆大小。
2、日志分析新生代空间大小分配是否合理,适当通过-Xmn命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。
3、对于大对象,如果直接在年轻代分配会导致大多年轻代小对象被拥挤到年老代,此时要合理设置-XX:PretenureSizeThreshold 参数,启动空间分配担保。
4、合理设置进入老年代对象的年龄,-XX:MaxTenuringThreshold
5、设置稳定的堆大小,堆大小设置有两个参数:-Xms 初始化堆大小,-Xmx 最大堆大小。
05 Java编程良好规范
推荐阿里开发手册:密码1412
全面思维导图
数据库
MySQL
阿里丁奇 MySQL 45讲 + 高性能MySQL
SQL插入跟更新执行流程 以及日志系统原理
底层分层
Server层
连接器
完成经典的TCP握手后,连接器就要开始认证你的身份,成功建立连接后,即使你的权限被修改了想要生效的话必须重新登录
默认8小时问题
查询缓存
KV存储,key是查询的语句,value是查询的结果
分析器
语法标准性检查,一些不存在字段排错
解析器处理语法和解析查询, 生成一课对应的解析树
解析器处理语法和解析查询, 生成一课对应的解析树
优化器
用哪个索引,表如何关联,where 1=1去除
执行器
权限认证
存储引擎层
负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。
MyISAM 与 InnoDB 的区别
日志系统原理
redo log
如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程IO成本、查找成本都很高,引入了 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。
crash-safe两阶段提交:保证绝可恢复
mysql 有一个基本的技术理念,那就是 WAL,即 Write-Ahead Logging,先写日志( innodb 的 redolog),再写磁盘
两阶段提交: 写入redolog 处于prepare阶段 --> 写binlog --> 提交事务处于commit状态
可以看到,在写入 binlog 及事务提交前,innodb 先记录了 redolog,并标记为 prepare 状态,在事务提交后,innodb 会将 redolog 更新为 commit 状态,这样在异常发生时,就可以按照下面两条策略来处理:
1. 当异常情况发生时,如果第一次写入 redolog 成功,写入 binlog 失败,MySQL 会当做事务失败直接回滚,保证了后续 redolog 和 binlog 的准确性
2. 如果第一次写入 redolog 成功,binlog 也写入成功,当第二次写入 redolog 时候失败了,那数据恢复的过程中,MySQL 判断 redolog 状态为 prepare,且存在对应的 binlog 记录,则会重放事务提交,数据库中会进行相应的修改操作
记录的是物理页的修改不同
write pos :是当前记录的位置,一边写一边后移,该位置前面会认为是空的可用的
checkpoint:是当前要擦除的位置,也是往后推移并且循环的,该位置的前面到write pos是需要清除的
checkpoint:是当前要擦除的位置,也是往后推移并且循环的,该位置的前面到write pos是需要清除的
binlog(归档日志)
MySQL安装及主从搭建教程
主从同步
主备延迟
耗时统计seconds_behind_master = 备库执行完毕时间 - 主库执行完毕时间,如果主备系统时间不一致会自动校正。
主备延迟主要表现:备库消费 Relay Log 速度 < 主库生成Binlog速度。
1、 备库所在机器的性能要比主库所在的机器性能差。
2、 备库的压力大,查询>>增删。此时可以一主多从,
3、 大事务的执行尽量避免,不要一次性地用delete语句删除太多数据。
4、 大表的DDL也要谨慎,可用工具 gh-ost
5、 备库的并行复制能力sql Thread.
1、 备库所在机器的性能要比主库所在的机器性能差。
2、 备库的压力大,查询>>增删。此时可以一主多从,
3、 大事务的执行尽量避免,不要一次性地用delete语句删除太多数据。
4、 大表的DDL也要谨慎,可用工具 gh-ost
5、 备库的并行复制能力sql Thread.
主备切换原则
1、可靠性优先策略:先将主切换为从,然后将从切换为主。中间不可用时间需谨慎,建议使用。
2、可用性优先策略:直接将从切换为主,可能出现数据不一致
备库并行复制能力
备库sql_thread 开启多个worker见数据读入到从库中。
并发复制必备要求:
1、不能造成更新覆盖。这就要求更新同一行的两个事务,必须被分发到同一个worker中。
2、同一个事务不能被拆开,必须放到同一个worker中。
2、同一个事务不能被拆开,必须放到同一个worker中。
主库crash了咋办 读写分离有哪些坑 如何判断主库挂了 太DBA了
一主多从时候 主库crash了咋办
过期读:客户发送个更改请求后,马上进行主从切换,在从库上会读到数据的一个过期状态
优先考虑update系统表,然后再配合增加检测performance_schema的信息
删库跑路
恢复手段
1、使用delete语句误删数据行:用Flashback工具通过闪回把数据恢复回来
2、使用drop table或者truncate table语句误删数据表 drop databases :使用全量备份,加增量日志的方式了。这个方案要求线上有定期的全量备份,并且实时备份binlog
3、使用rm命令误删整个MySQL实例:HA数据库集群可以安全搞定。
2、使用drop table或者truncate table语句误删数据表 drop databases :使用全量备份,加增量日志的方式了。这个方案要求线上有定期的全量备份,并且实时备份binlog
3、使用rm命令误删整个MySQL实例:HA数据库集群可以安全搞定。
减少误删操作风险的建议
账号分离 DBA 控制好
制定操作规范,自动化提交平台 由DBA执行
备份脚本、执行脚本、验证脚本和回滚脚本
为什么有些执行语句任务杀不死
1、比如Linux发出kill 指令或者MySQL发出kill指令,系统都不会暴力删除,而只是设置了一个状态并且唤醒对应的线程,该线程在执行到判断状态的埋点才会进入终止逻辑阶段。
2、如果你发现一个线程处于Killed状态,减少系统压力,加速终止逻辑
2、如果你发现一个线程处于Killed状态,减少系统压力,加速终止逻辑
关于客户端的误解
库里表特别多,连接就会很慢,当使用默认参数连接的时候,MySQL客户端会提供一个本地库名和表名补全的功能,耗时因为有个获取数据库table然后构建客户端哈希表的操作。连接时候加入 -A 可提速 屏蔽自动提醒。
Master跟Slave互为主备情况下如何避免主备循环复制问题。
日志格式
statement(默认) :binlog 只会记录可能引起数据变更的 sql 语句,性能最佳,但是对于uuid这样的无法恢复。必须串行执行
row : binlog 会记录每次操作的源数据与修改后的目标数据,而不会记录 sql 语句,绝对精准的还原。可并行化。
mixed : 对上述两种模式的混合使用。MySQL自己会判断这条SQL语句是否可能引起主备不一致,如果有可能,就用row格式,否则就用statement格式
row : binlog 会记录每次操作的源数据与修改后的目标数据,而不会记录 sql 语句,绝对精准的还原。可并行化。
mixed : 对上述两种模式的混合使用。MySQL自己会判断这条SQL语句是否可能引起主备不一致,如果有可能,就用row格式,否则就用statement格式
若干指令,如何备份
依靠 binlog 是无法保证 crash safe 的,因为 binlog 是事务提交时写入的,如果在 binlog 缓存中的数据持久化到硬盘之前宕机或断电 而主数据库中实际上这部分操作已经存在,从数据库因此无法同步这部分操作,从而造成主从数据库数据不一致
redo log 跟binlog 区别
1、redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
2、redo log是物理日志,记录的是在某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一行的c字段加1。
3、redo log是循环写的,空间固定会用完;binlog是可以追加写入的。追加写是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
2、redo log是物理日志,记录的是在某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一行的c字段加1。
3、redo log是循环写的,空间固定会用完;binlog是可以追加写入的。追加写是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
还有个undo log 用于事务回滚的,记录的意思都跟用户提交的SQL 方向相反。 在MVCC中用到
SQL 事务隔离
ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
不同隔离级别出现问题
脏读(dirty read):B事务更改数据还未提交,A事务已经看到并且用了。B事务如果回滚,则A事务做错了
不可重复读(non-repeatable read):不可重复读的重点是修改: 同样的条件, 你读取过的数据, 再次读取出来发现值不一样了,只需要锁住满足条件的记录
幻读(phantom read)
幻读的重点在于新增或者删除 同样的条件, 第1次和第2次读出来的记录数不一样 ,要锁住满足条件及其相近的记录,解决方法是间隙锁(Gap Lock)
在RR 可重复读的情况下 无论锁目标行还是所有行都无法避免新行的增加,导致出现幻读,不过在RR情况下引入了间隙锁
间隙锁可以防止出现幻读,不过可能出现死锁,并且可能出现 数据和日志不一致问题
如果读提交隔离级别够用 配合 binlog_format=row可解决
锁是加在索引上
next-key lock = 行锁 + 间隙锁
重点说RR条件下加锁的规则(好好品尝)
原则1:加锁的基本单位是next-key lock,规则是前开后闭区间。
原则2:查找过程中访问到的对象才会加锁。
优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
锁是加到索引上的!
原则2:查找过程中访问到的对象才会加锁。
优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
锁是加到索引上的!
隔离级别
你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。SQL标准的事务隔离级别由低到高
上面只是理论,各大数据库厂商对理论实现具体细节均有不同,MySQL的RR级别是不会发生这种幻读的,因为会出现表锁。
快照读 跟 当前读 :RR级别下的当前读会出现不可重复读问题,而快照读不会出现不可重复读问题
读未提交:别人改数据的事务尚未提交,我在我的事务中也能读到。
读已提交(Oracle默认):别人改数据的事务已经提交,我在我的事务中才能读到。
可重复读(MySQL默认):别人改数据的事务已经提交,我在我的事务中也不去读,以此保证重复读一致性。
串行:我的事务尚未提交,别人就别想改数据。
读已提交(Oracle默认):别人改数据的事务已经提交,我在我的事务中才能读到。
可重复读(MySQL默认):别人改数据的事务已经提交,我在我的事务中也不去读,以此保证重复读一致性。
串行:我的事务尚未提交,别人就别想改数据。
数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准
读未提交隔离级别下直接返回记录上的最新值,没有视图概念
在读提交隔离级别下,这个视图(read-view)是在每个SQL语句开始执行的时候创建的。
在可重复读隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。
在串行化隔离级别下直接用加锁的方式来避免并行访问。
在读提交隔离级别下,这个视图(read-view)是在每个SQL语句开始执行的时候创建的。
在可重复读隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。
在串行化隔离级别下直接用加锁的方式来避免并行访问。
事务开启
begin 或 start transaction。配套的提交语句是commit,回滚语句是rollback
set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个select语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行commit 或 rollback 语句,或者断开连接。
建议: 使用set autocommit=1
避免使用长事务
应用端
1、set autocommit=1
2、确认是否有不必要的只读事务
3、SET MAX_EXECUTION_TIME
2、确认是否有不必要的只读事务
3、SET MAX_EXECUTION_TIME
数据库端
1、 information_schema.Innodb_trx表,设置长事务阈值,超过就报警/或者kill;
2、 Percona
2、 Percona
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。长事务还占用锁资源,也可能拖垮整个库。
数据库并发场景
读-读:不存在任何问题,也不需要并发控制
读-写:有隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:可能存更新丢失问题,比如第一类更新丢失,第二类更新丢失
读-写:有隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:可能存更新丢失问题,比如第一类更新丢失,第二类更新丢失
全局锁、表锁、行锁
全局锁:命令是 Flush tables with read lock (FTWRL),MyISAM这种不支持事务的才用。
表级锁
手动 lock tables … read/write
表级的锁是MDL(metadata lock),自动开启。
行锁(InnoDB支持):
在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
一图搞定各种锁
死锁和死锁检测
死锁:不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态
1、等待超时解决,默认50秒。线上一般不用,影响用户体验。
2、发起死锁检测解决,主动回滚死锁链条中一个事务让其他事务得以继续执行。
2、发起死锁检测解决,主动回滚死锁链条中一个事务让其他事务得以继续执行。
如何解决死锁检测导致的消耗大量CPU
1、如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。不建议使用。
2、控制并发度,比如同一行同时最多只有10个线程在更新。基本思路就是,对于相同行的更新,在进入引擎之前排队。
2、控制并发度,比如同一行同时最多只有10个线程在更新。基本思路就是,对于相同行的更新,在进入引擎之前排队。
行锁:共享锁跟排它锁
lock in share modle 共享读锁
为了确保自己查到的数据没有被其他的事务正在修改,也就是说确保查到的数据是最新的数据,并且不允许其他人来修改数据。但是自己不一定能够修改数据,因为有可能其他的事务也对这些数据 使用了 in share mode 的方式上了 S 锁。
是一个给查找的数据上一个共享锁(S 锁)的功能,它允许其他的事务也对该数据上 S锁,但是不能够允许对该数据进行修改。如果不及时的commit 或者rollback 也可能会造成大量的事务等待。
for update 排它写锁
为了让自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改的时候,需要用到 for update 子句。
相当于一个 update 语句。在业务繁忙的情况下,如果事务没有及时的commit或者rollback 可能会造成其他事务长时间的等待,从而影响数据库的并发使用效率。
悲观锁跟乐观锁 思想
悲观并发控制 Pessimistic Concurrency Control 采用一种持悲观消极的态度,先取锁再访问
在读多写少,CAS竞争没这么激烈的时候,我们可以采用乐观锁策略,降低数据库加锁的开销,提高数据库并发响应
乐观并发控制 Optimistic Concurrency Control 乐观锁是假设认为即使在并发环境中,外界对数据的操作一般是不会造成冲突,实现原理跟JUC中的CAS类似。
在写多读少的场景下,因为会产生大量的CAS竞争,且重试成本比较高的情况下,我们就不建议再采用乐观锁策略了,还是直接使用悲观锁的数据库加锁吧
在数据库中,形成经典两个组合
MVCC + 悲观锁 MVCC解决读写冲突,悲观锁解决写写冲突
MVCC + 乐观锁 MVCC解决读写冲突,乐观锁解决写写冲突
MVCC 多版本并发控制
三个重要信息 3个隐式字段(rowid,insertid,deleteid),undo日志 ,(快照读视图)Read View
InnoDB 三个log
redo log
bin log
undo log
bin log
undo log
当前读
快照读
快照读
本质:MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现
三个重要信息 3个隐式字段(rowid,insertid,deleteid),undo日志 ,(快照读视图)Read View
1、事务中快照读的结果是非常依赖该事务首次出现快照读的地方,即某个事务中首次出现快照读的地方非常关键,它有决定该事务后续快照读结果的能力。
2、在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
2、在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
工作原理:可以认为当delete一条记录时,undo log中会反向思维的记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
这种情况下当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。
这种情况下当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。
读取原则
这一行的数据 隐藏增加事务ID <=本次事务ID
这一行的数据 隐藏删除事务ID >= 本次事务ID
如何 有效给table 瘦身
通过配置innodb_file_per_table 存储表结构定义跟数据格式
可以复用,而没有被使用的空间,看起来就像是空洞。删改都会引发空洞。
1、普通记录的删除只是标记位删除,只有符合条件的新记录才可以用该空洞。
2、删除数据页只是标记整个页为可用,但磁盘文件的大小是不会变的。也就是说,通过delete命令是不能回收表空间的。
2、删除数据页只是标记整个页为可用,但磁盘文件的大小是不会变的。也就是说,通过delete命令是不能回收表空间的。
重建 优化,删除碎片
alter table t engine = InnoDB
默认是 Online DDL,重建过程中可以增删改
1、从MySQL 5.6版本开始,alter table t engine = InnoDB(也就是recreate)默认的就是ONline DDL的流程了;
2、analyze table t 其实不是重建表,只是对表的索引信息做重新统计,没有修改数据,这个过程中加了MDL读锁;
3、optimize table t 等于recreate+analyze。MySQL会锁定表
4、truncate = drop + create
5、瘦身后数据页之间也有1/16的空隙
2、analyze table t 其实不是重建表,只是对表的索引信息做重新统计,没有修改数据,这个过程中加了MDL读锁;
3、optimize table t 等于recreate+analyze。MySQL会锁定表
4、truncate = drop + create
5、瘦身后数据页之间也有1/16的空隙
DBA 推荐 Github:gh-ost
count(*) 跟 order by 执行 以及随机查询
count(*)
count聚合函数的遍历整个表,判断不是NULL,累计值就加1,否则不加。最后返回累计值。
性能 : count(字段)<count(主键id)<count(1)≈count(*),尽量使用count(*)。
统计原则
MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高;
InnoDB 由于MVCC的问题,需要把数据一行一行地从引擎里面读出来,MySQL优化器会找到最小的那棵树来遍历累加求和。
show table status 统计估值而已不准
Redis 计数 并发大时也不准
计数单独放到一个mysql table中,ok
order by 执行流程
MySQL设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问。
1、从一般索引找到满足条件的数据,然后找到对应的主键索引位置,取出所需信息存到sort_buffer。然后对sort_buffer排序。最终返回结果。如果sort_buffer空间不够还需要将数据切分若干份存到磁盘,然后用归并排序。
3、有时候 max_length_for_sort_data 不满足条件还会出发 rowid,浪费点是多一次主键索引访问。
4、可以权衡的用联合索引跟覆盖索引来实现必要查询,当然前提是要考虑维护成本。
3、有时候 max_length_for_sort_data 不满足条件还会出发 rowid,浪费点是多一次主键索引访问。
4、可以权衡的用联合索引跟覆盖索引来实现必要查询,当然前提是要考虑维护成本。
rowid:对于有主键的InnoDB表来说,这个rowid就是系统生成的6字节主键ID;
随机读取
order by rand()
随机排序取前3个 : select word from words order by rand() limit 3
order by rand()使用了内存临时表跟数据排序 十分耗时,内存临时表排序的时候使用了rowid排序方法。MySQL5.6以后引入了堆排来实现limit 3。
order by rand()使用了内存临时表跟数据排序 十分耗时,内存临时表排序的时候使用了rowid排序方法。MySQL5.6以后引入了堆排来实现limit 3。
逐步优化的随机读
prepare+execute
mysql> select count(*) into @C from t;
set @Y = floor(@C * rand());
set @sql = concat("select * from t limit ", @Y, ",1");
prepare stmt from @sql;
execute stmt;
DEALLOCATE prepare stmt;
扫描总行数 = C + Y + 1
set @Y = floor(@C * rand());
set @sql = concat("select * from t limit ", @Y, ",1");
prepare stmt from @sql;
execute stmt;
DEALLOCATE prepare stmt;
扫描总行数 = C + Y + 1
mysql> select count(*) into @C from t;
set @Y1 = floor(@C * rand());
set @Y2 = floor(@C * rand());
set @Y3 = floor(@C * rand());
select * from t limit N, M-N+1;
-- 取Y1、Y2和Y3里面最大的一个数,记为M,最小的一个数记为N。 再加上取整个表总行数的C行,扫描行数 = C+M+1行。
set @Y1 = floor(@C * rand());
set @Y2 = floor(@C * rand());
set @Y3 = floor(@C * rand());
select * from t limit N, M-N+1;
-- 取Y1、Y2和Y3里面最大的一个数,记为M,最小的一个数记为N。 再加上取整个表总行数的C行,扫描行数 = C+M+1行。
MySQL处理limit Y,1 的做法就是按顺序一个一个地读出来,丢掉前Y个,然后把下一个记录作为返回结果,因此这一步需要扫描Y+1行
简单的查找一行就可能会涉及到,表锁、flush、行锁、MVCC知识点。
各种优化手段
按照底层区分:在存储引擎层实现
Hash表:等值查询
有序数组:适用于静态存储索引,插入让人头疼
搜索树:B+ 树
索引、单列索引、复合索引、主键、唯一索引、聚簇索引、非聚簇索引、唯一聚簇索引
数据都存储在主键索引的叶子节点
聚集索引:主键索引
一般情况下都要构建一个与业务无关的主键索引
非聚集索引:触发回表
索引维护
页满了 页分裂 页利用率下降
数据删除 页合并
自增主键索引 只追加可以不考虑 也分页
索引长度
优化流程
预跑sql explain
排除 缓存 sql nocache
查询缓存往往弊大于利:查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空
有时候MySQL的优化器选择索引会出错
基数:一个索引上不同的值的个数。基数越大越好。
普通索引查找后还需要回表,有时候MySQL就会用错索引。
对于由于索引统计信息不准确导致的问题,你可以用analyze table来解决。
在应用端用force index来强行指定索引
存在回表的情况:尝试用覆盖索引优化
覆盖索引 联想到 延迟关联
联合索引
不能无限建 高频场景,一次性最多联合16个
存在的意义
减少开销:建一个联合索引(col1,col2,col3),实际相当于建了(col1)、(col1,col2)、(col1,col2,col3) 三个索引。
覆盖索引:联合索引 关联到 查询必备的字段
效率高:索引列越多,通过索引筛选出的数据越少
5.6之后 索引下推 : 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
最左匹配原则
最左原则失效
索引失效
模糊搜索,左模糊或全模糊都会导致索引失效,比如'%key'和'%key%'。但是右模糊是可以利用索引的,比如'key%'
隐式类型转换,比如select * from name = xxx , name是字符串类型,但是没有加引号
当语句中带有or的时候,比如select * from table where name=‘snailmann’or age=20
不符合联合索引的最左前缀匹配, (A,B,C)的联合索引,你只where了C或B或只有B,C
按照索引定义的字段顺序写sql,合理安排联合索引顺序
给字符串加索引的建议
前缀索引:对文本的前几个字符(具体是几个字符在建立索引时指定)建立索引,这样建立起来的索引更小,所以查询更快
倒序存储:存储学号时,比如201306070110。前四个字节表示入学年份,区分度较低,此时可以采用倒序存储,然后再使用前缀索引的方式提高查询效率,有点HBase 主键设置的意思
Hash:存储邮箱时,比如 streamXXX@email.com,此时正序和倒序的区分度都比较低,可以考虑hash字段的方式,不过这样就要添加一个字段存储hash后的值。
数据库的flush的时机
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为脏页。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为干净页。
平时执行很快的更新操作,其实就是在写内存和日志,而MySQL偶尔抖一下的那个瞬间,可能就是在刷脏页(flush)。
flush发生时机
1、InnoDB的redo log写满了:将undo log数据清空一部分,把内存的脏页都flush到磁盘上。尽量避免,发生后整个系统就不能再接受更新了。
2、忽然间内存不够用了:把内存的脏页都flush到磁盘上。
InnoDB用缓冲池(buffer pool)管理内存
一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
日志写满,更新全部堵住,写性能跌为0,这种情况对敏感业务来说,是不能接受的。
日志写满,更新全部堵住,写性能跌为0,这种情况对敏感业务来说,是不能接受的。
要知道磁盘的io能力 设置innodb_io_capacity 设置为磁盘的IOPS fio测试,innodb_io_capacity设置低了 会让innoDB错误估算系统能力 导致脏页累积
innodb刷盘速度
因素一、脏页比例
因素二、redolog 写盘速度
innodb_flush_neighbors 0
机械磁盘的随机io不太行 减少随机io性能大幅提升 设置为 1最好
现在都是ssd了 设置为0就够了 8.0之后默认是0
=1时 刷自己的时候还会连带把周边关联的刷了
详细说 buffer pool
0、存在的意义 是加速查询
1、缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;
2、缓冲池通常以页(page 16K)为单位缓存数据;
3、缓冲池的常见管理算法是LRU,memcache,OS,InnoDB都使用了这种算法;
4、InnoDB对普通LRU进行了优化:将缓冲池分为老生代和新生代,入缓冲池的页,优先进入老生代,页被访问,才进入新生代,以解决预读失效的问题页被访问,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰的问题
1、缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;
2、缓冲池通常以页(page 16K)为单位缓存数据;
3、缓冲池的常见管理算法是LRU,memcache,OS,InnoDB都使用了这种算法;
4、InnoDB对普通LRU进行了优化:将缓冲池分为老生代和新生代,入缓冲池的页,优先进入老生代,页被访问,才进入新生代,以解决预读失效的问题页被访问,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰的问题
图说 Buffer Pool
1G 内存 查询 50G 数据 内存是否会爆?
由于MySQL采用的是边算边发的逻辑,因此对于数据量很大的查询结果来说,不会在server端保存完整的结果集。所以,如果客户端读结果不及时,会堵住MySQL的查询过程,但是不会把内存打爆。
3、MySQL认为系统空闲:合理利用内存 把内存的脏页都flush到磁盘上。对MySQL性能没影响。
4、MySQL正常关闭:把内存的脏页都flush到磁盘上。对MySQL性能没影响。
索引字段不要做函数操作,会破坏索引值的有序性,优化器会放弃走树结构,如果触发隐式转换 那也会走cast函数 会放弃走索引
1、where t_modified='2018-7-1’ 就比 month(t_modified)=7 好
2、where id = 10 -1 就比 where id + 1 = 10 好
3、flag varchar类型有索引,如果where flag = 1 则触发了 将字符串转换为整数的操作,导致索引失效。但是如果是int类型,where id = '1412' 这个是可以用索引的!
4、在MySQL中,字符串和数字做比较的话,是将字符串转换成数字。
5、隐式字符编码转换也会导致失败。字符集不同可能走不上索引,convert 也是函数所以走不上
2、where id = 10 -1 就比 where id + 1 = 10 好
3、flag varchar类型有索引,如果where flag = 1 则触发了 将字符串转换为整数的操作,导致索引失效。但是如果是int类型,where id = '1412' 这个是可以用索引的!
4、在MySQL中,字符串和数字做比较的话,是将字符串转换成数字。
5、隐式字符编码转换也会导致失败。字符集不同可能走不上索引,convert 也是函数所以走不上
饮鸩止渴的短时优化方案
1、 连接很宝贵,用show processlist 先断开事务外空闲太久的连接;如果这样还不够,再考虑断开事务内空闲太久的连接。
2、 减少连接过程的消耗 跳过权限验证的方法是:重启数据库,并使用–skip-grant-tables参数启动。这样,整个MySQL会跳过所有的权限验证阶段,包括连接过程和语句执行过程在内。
3、 慢查询性能问题,5.6版本以后,创建索引都支持Online DDL了
4、 语句没写好。慢查询日志(slow log)打开,并且把long_query_time设置成0,确保每个语句都会被记录入慢查询日志
2、 减少连接过程的消耗 跳过权限验证的方法是:重启数据库,并使用–skip-grant-tables参数启动。这样,整个MySQL会跳过所有的权限验证阶段,包括连接过程和语句执行过程在内。
3、 慢查询性能问题,5.6版本以后,创建索引都支持Online DDL了
4、 语句没写好。慢查询日志(slow log)打开,并且把long_query_time设置成0,确保每个语句都会被记录入慢查询日志
mysql io性能瓶颈
设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
将sync_binlog 设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。
将sync_binlog 设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。
关于主键操作
主键id 用完了咋办
big int类型
MySQL默认自带的 row id
主键id未增加到最大值已经进行分库分表了
主键自增无法保证完全自增
在MyISAM引擎里面,自增值是被写在数据文件上的。而在InnoDB中,自增值是被记录在内存的。MySQL直到8.0版本,才给InnoDB表的自增值加上了持久化的能力,确保重启前后一个表的自增值不变。
MySQL对自增主键锁做了优化,尽量在申请到自增id以后,就释放自增锁
在MyISAM引擎里面,自增值是被写在数据文件上的。而在InnoDB中,自增值是被记录在内存的。MySQL直到8.0版本,才给InnoDB表的自增值加上了持久化的能力,确保重启前后一个表的自增值不变。
MySQL对自增主键锁做了优化,尽量在申请到自增id以后,就释放自增锁
1、唯一键冲突是导致自增主键id不连续的第一种原因
2、事务回滚也会产生类似的现象
3、执行批量插入的时候申请主键自增id每一次都是前一次2倍。如果最后一次申请了16个用了10个则空洞了6个。
2、事务回滚也会产生类似的现象
3、执行批量插入的时候申请主键自增id每一次都是前一次2倍。如果最后一次申请了16个用了10个则空洞了6个。
三大范式
1、是数据的原子性
2、是确保每列都和主键相关,意思是:只做一件事
3、确保每列都和主键直接相关,而不是间接相关,意思是不能存在传递依赖
2、是确保每列都和主键相关,意思是:只做一件事
3、确保每列都和主键直接相关,而不是间接相关,意思是不能存在传递依赖
索引选择
普通索引 还是唯一索引
查找
1、 普通索引:需要查找下一个记录,直到碰到第一个不满足,因为InnoDB按照数据页读取,所以性能无差别几乎。
2、 唯一索引: 查找到第一个满足条件的记录后就可停止。
2、 唯一索引: 查找到第一个满足条件的记录后就可停止。
更新
1、普通索引可以利用 change buffer优化。
2、唯一索引更新后必须查询磁盘确保唯一性。
2、唯一索引更新后必须查询磁盘确保唯一性。
change buffer :
InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了,然后访问更新跟定时更新。
唯一索引的更新就不能使用change buffer,因为更新后要确保唯一性,实际上也只有普通索引可以使用
redo log 主要节省的是随机写磁盘的IO消耗(转成顺序写),而change buffer主要节省的则是随机读磁盘的IO消耗
索引使用原则
更新
change buffer
更新操作来了 如果数据页不在内存 就缓存下来 下次来了 更新 在就直接更新
唯一索引 需要判断 所以 用不到 change buffer
innodb的处理流程
记录在页内存
唯一索引 判断没冲突插入
普通索引 插入
记录不在页中
数据页读入内存 判断 插入
change buffer
数据读是随机IO 成本高
机械硬盘 change buffer 收益大 写多读少 marge
关于join
该不该用
1、如果可以使用被驱动表的索引,join语句还是有其优势的;
2、如果不能使用被驱动表的索引,只能使用Block Nested-Loop Join算法,这样的语句就尽量不要使用;
3、在使用join的时候,应该让小表做驱动表。
4、在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表
2、如果不能使用被驱动表的索引,只能使用Block Nested-Loop Join算法,这样的语句就尽量不要使用;
3、在使用join的时候,应该让小表做驱动表。
4、在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表
如何优化
1、无索引的原始连表join:Simple Nested-Loop Join,完全暴力两个嵌套循环查找。
2、无索引的连表join优化:Block Nested-Loop Join(BNL),驱动表引入了 join_buffer(暂存驱动表的数据)。
3、有索引的连表join:Index Nested-Loop Join(NLJ)。从驱动表一行行拿出数据然后从被驱动表通过索引查询。
4、Batched Key Acess(BKA) :先引入 Multi-Range Read实现主键无序查询变为有序查询。然后批量拿数据(join_buffer)从被驱动表匹配数据,属于NLJ优化。
5、BNL算法 被驱动表是大表的时候 可能会多次扫描被驱动表,占用磁盘IO资源;判断join条件需要执行M*N次对比占用CPU资源大,可能会导致Buffer Pool的热数据被淘汰,影响内存命中率。explain 加索引,BNL转BKA
6、让join语句能够用上被驱动表上的索引,来触发BKA算法,提升查询性能。
7、业务端获取所需数据后 自己实现hashjoin
2、无索引的连表join优化:Block Nested-Loop Join(BNL),驱动表引入了 join_buffer(暂存驱动表的数据)。
3、有索引的连表join:Index Nested-Loop Join(NLJ)。从驱动表一行行拿出数据然后从被驱动表通过索引查询。
4、Batched Key Acess(BKA) :先引入 Multi-Range Read实现主键无序查询变为有序查询。然后批量拿数据(join_buffer)从被驱动表匹配数据,属于NLJ优化。
5、BNL算法 被驱动表是大表的时候 可能会多次扫描被驱动表,占用磁盘IO资源;判断join条件需要执行M*N次对比占用CPU资源大,可能会导致Buffer Pool的热数据被淘汰,影响内存命中率。explain 加索引,BNL转BKA
6、让join语句能够用上被驱动表上的索引,来触发BKA算法,提升查询性能。
7、业务端获取所需数据后 自己实现hashjoin
若干小知识点
临时表
临时表:用于处理比较复杂的计算逻辑。由于临时表是每个线程自己可见的,所以不需要考虑多个线程执行同一个处理逻辑时,临时表的重名问题。在线程退出的时候,临时表也能自动删除,省去了收尾和异常处理的工作。
特色
使用时机
存储过程
存储过程可以看成是对一系列 SQL 操作的批处理
分库分表扩展
ShardingSphere
ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar这3款相互独立的产品组成。
他们均提供标准化的数据分片、分布式事务 和 数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
他们均提供标准化的数据分片、分布式事务 和 数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
中间件 MyCat
常用的一些语句
批量删除数据量连接
查看mysql库大小,表大小,索引大小
数据如果存在则放弃,不存在则插入:
删除模糊查询的Table
常见面试题:
数据库面试知识点汇总
MySQL基础巩固
本地连接工具:
HeidiSQL
sqlyng
Navicat
MySQL为什么不建议用Text字段
1. 数据库性能在 磁盘IO,网络带宽这些,Text太大会导致频繁磁盘IO,数据在MySQL按页存储,数据太大涉及到翻页,网络传输会比较耗时,mysql是个OLTP的工具,这样的操作尽量用OLAP。
1. 数据库性能在 磁盘IO,网络带宽这些,Text太大会导致频繁磁盘IO,数据在MySQL按页存储,数据太大涉及到翻页,网络传输会比较耗时,mysql是个OLTP的工具,这样的操作尽量用OLAP。
分库分表
分库分表查询 UserId UserName
按照 UserId 分库分表后想根据UserName查询信息咋办。
1. 建立 userName 跟 UserId 映射的中间表。
2. 把1的数据存储到redis缓存中。
3. userName通过fun函数生成UserId,函数质量要高。
4. userName基因融入userId,64UserId = 61位UserId + UserName生成的
1. 建立 userName 跟 UserId 映射的中间表。
2. 把1的数据存储到redis缓存中。
3. userName通过fun函数生成UserId,函数质量要高。
4. userName基因融入userId,64UserId = 61位UserId + UserName生成的
分库分表:把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上
MySQL单表 1000W,2G空间压力。
垂直切分:用户DB,商品DB,logDB划分到不同服务器。
水平切分: 根据UserId 进行哈希映射到 8台机器上的思路。
MySQL单表 1000W,2G空间压力。
垂直切分:用户DB,商品DB,logDB划分到不同服务器。
水平切分: 根据UserId 进行哈希映射到 8台机器上的思路。
分库引入的分布式事务
分库引入的跨库跨表join。
额外的数据管理跟数据运算。
分库引入的跨库跨表join。
额外的数据管理跟数据运算。
不停机修改字段
1. 工具 pt-osc,gh-ost,MySQL8.0 有 腾讯的修改工具包。
2. 新建若干从库修改字段,数据同步追赶上了。做主从切换实现表格切换。
3. 凌晨强制修改 online ddl
2. 新建若干从库修改字段,数据同步追赶上了。做主从切换实现表格切换。
3. 凌晨强制修改 online ddl
不停机扩容
1. 成倍扩容,避免数据迁移
2. 双写模式
3. 停机扩容: 停止服务,将2台DB数据迁移到4台机器上,然后重启服务。
TiDB:开源分布式数据库
优点
1、支持 MySQL 协议(开发接入成本低);
2、100% 支持事务(数据一致性实现简单、可靠);
3、无限水平拓展(不必考虑分库分表),不停服务。
4、TiDB 支持和 MySQL 的互备。
5、遵循jdbc原则,学习成本低,强关系型,强一致性,不用担心主从配置,不用考虑分库分表,还可以无缝动态扩展
2、100% 支持事务(数据一致性实现简单、可靠);
3、无限水平拓展(不必考虑分库分表),不停服务。
4、TiDB 支持和 MySQL 的互备。
5、遵循jdbc原则,学习成本低,强关系型,强一致性,不用担心主从配置,不用考虑分库分表,还可以无缝动态扩展
适用
1、原业务的 MySQL 的业务遇到单机容量或者性能瓶颈时,可以考虑使用 TiDB 无缝替换 MySQL。
2、大数据量下,MySQL 复杂查询很慢。
3、大数据量下,数据增长很快,接近单机处理的极限,不想分库分表或者使用数据库中间件等对业务侵入性较大、对业务有约束的 Sharding 方案。
4、大数据量下,有高并发实时写入、实时查询、实时统计分析的需求。
5、有分布式事务、多数据中心的数据 100% 强一致性、auto-failover 的高可用的需求。
2、大数据量下,MySQL 复杂查询很慢。
3、大数据量下,数据增长很快,接近单机处理的极限,不想分库分表或者使用数据库中间件等对业务侵入性较大、对业务有约束的 Sharding 方案。
4、大数据量下,有高并发实时写入、实时查询、实时统计分析的需求。
5、有分布式事务、多数据中心的数据 100% 强一致性、auto-failover 的高可用的需求。
不适用
1、单机 MySQL 能满足的场景也用不到 TiDB。
2、数据条数少于 5000w 的场景下通常用不到 TiDB,TiDB 是为大规模的数据场景设计的。
3、如果你的应用数据量小(所有数据千万级别行以下),且没有高可用、强一致性或者多数据中心复制等要求,那么就不适合使用 TiDB。
2、数据条数少于 5000w 的场景下通常用不到 TiDB,TiDB 是为大规模的数据场景设计的。
3、如果你的应用数据量小(所有数据千万级别行以下),且没有高可用、强一致性或者多数据中心复制等要求,那么就不适合使用 TiDB。
Redis
简洁:C 语言编写,KV类型NoSQL,包含多种常见数据类型,10W/s 高性能
源码专栏讲解
图形化监控方案 RedisLive
基本操作若干指令
推荐 《Redis 设计与实现》
数据结构
HyperLogLog
功能:误差允许范围内做基数统计set的时候非常有用,每个HyperLogLog的键可以计算接近2^64不同元素的基数,而大小只需要12KB。错误率大概在0.81%
底层理论
算法的最本源则是伯努利过程:知乎通俗说
伯努利 + 分桶 + 调和平均
用法
PFADD key element
PFCOUNT key
PFMERGE destkey sourcekey [sourcekey …]
Pub/Sub
但是一般较少使用,如果真需要发布订阅功能引入专业的消息中间件即可,比如RabbitMQ、ActiveMQ、Kafka等
Geo算法实战
GeoHash算法,再往深入说就是GIS了
BitMap
bitMap是位图,其实准确的来说,翻译成基于位的映射
setbit key index value
getbit key index
bitcount key [start ,end]
zipList (list Hash) : 压缩列表占用字节数,end 到 start 的长度字节数,压缩列表包含节点数量,各个节点,0xFF表示压缩列表收尾。
当list对象的所有字符串元素长度都小于64字节,并且保存的元素数量小于512个时,使用压缩列表
如果不满足上述条件中的任意一个,都会使用链表
如果不满足上述条件中的任意一个,都会使用链表
底层存储
SDS
在redis里面是采用SDS(simple dynamic string)来封装char[]的,这个也是redis存储的最小单元
redisObject封装SDS来为我们所用,通过type表示5种基本数据类型
sds 跟 c
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录buf数组中未使用的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
//记录buf数组中已使用字节的数量
int len;
//记录buf数组中未使用的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
sds自带长度存储,内存预分配跟惰性空间释放
String: 当我们 set name blog 时,redis其实会创建两个RedisObject对象, 键的RedisObject 和 值的RedisOjbect 其中它们的type=REDIS_STRING。源代码在sds.h
缓存功能、计数器、共享用户Session
List:底层数据格式为双向链表,可用来实现简单的任务队列等,源代码是在adlist.c,K=RedisObject String 类型,Value = String类型的双向链表
lrange的分页,简单的消息队列。
源码特性:双端、无环、带长度记录
Set:底层是个HashTable 可以认为是上面Hash的简单版
交集,共同好友等
字典Hash
每个字典带 两个hash表,需要扩容的时候采用渐进式 rehash:会逐渐rehash 新的键值对全部放到新的hash表
Hash结构图
Hash类型 如何插入,如何扩容,如何缩容
服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1 ;
服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;
当哈希表的负载因子小于 0.1 时, 程序自动开始对哈希表执行收缩操作;
服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;
当哈希表的负载因子小于 0.1 时, 程序自动开始对哈希表执行收缩操作;
Zset
用法: 相比于Set 对每一个item多了个值
基本用法
底层实现为跳表
原理图
插入的时候用抛硬币的思想绝对数据是否 提升为上层链表
应用
积分排行榜
根据时间排序的新闻列表
延时队列
Java操作Redis用Jedis:redis协议位于TCP层之上,即客户端和redis实例保持双工的连接,交互的都是序列化后的协议数据(RESP)
*3
$3
set
$4
name
$4
alex
$3
set
$4
name
$4
alex
内存不足时淘汰机制:最少使用LRU原则
过期策略:处理过期的缓存数据
定时删除
给key设置过期定时器,该策略可自动删除过期数据,但占用太多资源处理过期数据,影响缓存响应时间跟吞吐量。
惰性删除
访问时才判断key是否过期了,该策略可大大优化CPU资源,但是对内存不友好,存在大量占用内存情况。
定期删除
expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。
前两者的一个折中方案,隔一段时间就扫描expires来清理其中过期key。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
Redis中同时使用了惰性过期和定期过期两种过期策略。
常见知识点
Redis任务执行是队列形式执行,因此需注意开启慢查询日志
命令执行过程Round trip time: 发送命令-〉命令排队-〉命令执行-〉返回结果
pipeline 批量执行 以此加速
弱事务性
1、Redis 事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
2、Redis事务没有隔离级别的概念:
3、Redis不保证原子性:
4、 multi exec discard
2、Redis事务没有隔离级别的概念:
3、Redis不保证原子性:
4、 multi exec discard
1、编译型错误事务中所有代码均不执行,指令使用错误。
2、运行时异常,错误命令异常,其他命令可正常执行。
2、运行时异常,错误命令异常,其他命令可正常执行。
Redis用watch unwatch实现乐观锁
Redis为什么那么快
C语言编写,完全基于内存,绝大部分请求是纯粹的内存操作
弹性的数据结构 ziplist
数据结构简单,对数据操作也简单
采用单线程,避免了不必要的上下文切换和竞争条件,不用切换耗时CPU跟加锁
Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争
多路网络连接复用一个io线程
select、poll、epoll
多路复用产生的效果,完全可以由用户态去遍历文件描述符并调用其非阻塞的 read 函数实现。而多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,变成了一次系统调用 + 内核层遍历这些文件描述符。
就好比我们平时写业务代码,把原来 while 循环里调 http 接口进行批量,改成了让对方提供一个批量添加的 http 接口,然后我们一次 rpc 请求就完成了批量添加。
就好比我们平时写业务代码,把原来 while 循环里调 http 接口进行批量,改成了让对方提供一个批量添加的 http 接口,然后我们一次 rpc 请求就完成了批量添加。
高可用
持久化
RDB 默认
触发方式
自动触发
save 900 1 : 服务器在 900 秒之内,对数据库进行了至少 1 次修改
save 300 10 :服务器在 300 秒之内,对数据库进行了至少 10 次修改
save 60 10000 :服务器在 60 秒之内,对数据库进行了至少 10000 次修改
save 300 10 :服务器在 300 秒之内,对数据库进行了至少 10 次修改
save 60 10000 :服务器在 60 秒之内,对数据库进行了至少 10000 次修改
手动save
SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
手动bgsave
BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,在保存完成后向主进程发送信号告知完成。 Redis服务器在BGSAVE 执行期间仍可以继续处理客户端的请求
优点
压缩后的二进制文,适用于备份、全量复制,用于灾难恢复加载RDB恢复数据远快于AOF方式
缺点
无法做到实时持久化,每次都要创建子进程,频繁操作成本过高。保存后的二进制文件,存在老版本不兼容新版本rdb文件的问题
Copy On Write 写时复制
原理:需要时候才复制不同数据,否则数据公用。
好处:减少分配复制资源带来的延时,减少不必要资源分配。
缺点:fork()后如果父子进程继续写会产生大量分页错误。
1、Redis在持久化时,如果是采用BGSAVE命令或者BGREWRITEAOF的方式,那Redis会fork出一个子进程来读取数据,从而写到磁盘中。
2、总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。
3、而在rehash阶段上,写操作是无法避免的。所以Redis在fork出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
2、总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。
3、而在rehash阶段上,写操作是无法避免的。所以Redis在fork出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
联想到 Java 中的 CopyOnWriteArrayList、CopyOnWriteSet
AOF
以日志的形式记录每个写操作(读操作不记录),只需追加文件但不可以改写文件,redis启动时会根据日志从头到尾全部执行一遍以完成数据的恢复工作,append only
两种方式触发:有写操作就写、每秒定时写(也会丢数据)。
优点:全量数据保存。缺点:文件太大恢复也慢
图解RDB跟AOF流程
当AOF和RDB文件同时存在时,优先加载AOF,若关闭了AOF,加载RDB文件
rewrite
手动触发:BGREWRITEAOF
1、AOF重写的目的是为AOF文件瘦身,整个过程是直接读取内存中所有有效数据,整个重写过程基本上不影响Redis主进程处理命令请求,AOF可以由用户手动触发,也可以由服务器自动触发。
2、fork出子进程,Redis服务仍可用。
2、fork出子进程,Redis服务仍可用。
AOF流程图
数据同步机制
主从同步
指令流
offset
快照同步
RDB
缓冲区
集群搭建三种方式
单机问题:机器故障、容量瓶颈、QPS瓶颈。
1、Redis主从复制:全量同步跟增量同步,但是master挂掉后如何保证Redis还可用?
2、Redis哨兵(Sentinel):监控多个master-slave集群,发现master宕机后能进行自动切换,实现高可用。
哨兵
集群监控:循环监控master跟slave节点。
消息通知:当它发现有redis实例有故障的话,就会发送消息给管理员
故障转移
主观下线:单独一个哨兵发现master故障了。
客观下线:多个哨兵进行抉择发现达到quorum数时候开始进行切换。
客观下线:多个哨兵进行抉择发现达到quorum数时候开始进行切换。
配置中心:如果发生了故障转移,它会通知将master的新地址写在配置中心告诉客户端。
脑裂
哨兵模式下错误任务master挂掉了,将slave提升为master。此时出现oldMaster跟NewMaster,客户端还在给oldMaster写数据,此时写的数据会出现丢失
sentinel使用的是raft协议
3、redis-cluster集群部署:每个主节点通过哨兵机制监控安排了从节点,利用哈希槽(2^14)分配数据存储。
集群搭建
分片原则
1、节点取余 hash(key) % N
2、虚拟槽哈希 CRC16[key]&16383
2、虚拟槽哈希 CRC16[key]&16383
redis通过集群的搭建实现了多个master 的共同工作,然后在不同的master 下面还创建了slave节点来实现集群的高可用,当有某个主节点故障后会有节点迅速补上来。并且可以实现动态的增删master。最后 Java操作redis集群的时候通过JedisPool 来操作rediscluster池就OK咯哦!
一般推荐用此种模式, raft 协议实现 master 选举
RedLock:分布式锁
Redis 集群扩容
为什么是 16384个槽
在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 16K),也就是说使用2k的空间创建了16k的槽数。
虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) =65K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) =65K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
常见问题
缓存雪崩
现象:大批量key在同一时间同时失效导致所有请求都打到了MySQL
解决方法:
1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2、如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3、设置热点数据永远不过期。
缓存击穿
现象:大并发集中对这一个热点key进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
解决方法:设置热点数据永远不过期。加互斥锁来访问DB
缓存穿透
现象:缓存和数据库中都没有的数据,而用户不断发起请求,比如 id = -1这样请求
解决方法
布隆过滤器,不存在的一定不存在,存在的不一定存在,有些许误差。
后端接口层增加 用户鉴权校验,参数做校验等。
单个IP每秒访问次数超过阈值直接拉黑IP。
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null 失效时间可以为30秒防止恶意攻击
上述若干问题避免方案
事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
事中:本地 ehcache 缓存 + Hystrix 限流 + 降级,避免MySQL 被打死。
事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
事中:本地 ehcache 缓存 + Hystrix 限流 + 降级,避免MySQL 被打死。
事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
双写一致性
双写:缓存跟数据库均写入数据,如何保证数据一致性。串行化可实现但是容易阻塞。
Cache Aside Pattern:
1、先读缓存,缓存没有再读数据库,数据放入缓存,结果返回。
2、写时先写数据库,然后再删除缓存。一种lazy思想,用到缓存才去重新计算缓存。类似MyBatis的Lazy思想。
并发竞争
分布式锁
整体方案:准备分布式锁,抢到锁才可以set操作。
zookeeper实现
1、创建临时zk节点,用完删除,故障后自动删除。
2、其余节点按顺序监控比自己小的节点id,前面的删除后会自动触发最小的监控id拿锁。
3、频繁的需要zk去删除创建节点,网络抖动都可能误以为挂掉了。
2、其余节点按顺序监控比自己小的节点id,前面的删除后会自动触发最小的监控id拿锁。
3、频繁的需要zk去删除创建节点,网络抖动都可能误以为挂掉了。
Redis实现
setnx key value : 如果key不存在set成功返回int的1,这个key存在了返回0。
SETEX key seconds value: 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在,setex命令将覆写旧值。
Lua脚本实现语句组装原子性。
SETEX key seconds value: 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在,setex命令将覆写旧值。
Lua脚本实现语句组装原子性。
思路
合理的利用SETNX 跟 Set指令 配合Lua脚本实现即可。
开源redisson
大Key
bigkey命令 找到干掉
热点key
缓存时间不失效
多级缓存
布隆过滤器
读写分离
Redis:限流
setnx : 弊端太大。
zset: value=UUID表示,score=当前时间戳,指定[start,end]统计访问个数实现限流。窗口滑动,zset会越来越大!
令牌桶算法:
1、恒定速率往容器中放入令牌token。List leftPop
2、请求来容器中拿令牌,拿到说明没超出限制,拿不到说明超出限制。
3、能够限制数据的平均传输速率,允许某种程度的突发传输。
4、空轮询、重试。
5、可能出现瞬间并发高现象
2、请求来容器中拿令牌,拿到说明没超出限制,拿不到说明超出限制。
3、能够限制数据的平均传输速率,允许某种程度的突发传输。
4、空轮询、重试。
5、可能出现瞬间并发高现象
漏桶算法:
目的:控制数据注入到网络的速率
实现:请求以任意速率流入桶中,桶满则拒绝接受请求,按照固定速率流出消耗请求。
guava的RateLimiter 令牌桶算法
Redis + Lua + AOP
自定义注解实现限流
可视化UI:AnotherRedisDesktopManager
性能
可靠性
资源
运维
监控
安全
可靠性
资源
运维
监控
安全
主从不同步如何解决
微信RabbitMQ 总结Redis
MongoDB
Neo4j
Java工程
Tomcat
Tomcat入门
所谓服务器其实就是代码编写的一个可以根据用户请求实时的调用执行对应的逻辑代码的一个容器。在普通用户看来就是一个安装程序。我们只需要将服务器在操作系统上进行安装,并将我们事先编写好的逻辑处理代码根据规则放到服务器的指定位置,启动服务器,那么服务器就自动的会根据接收到请求调用。这个特定的容器在Java Web中就是Tomcat。
手写Tomcat雏形
JSP的本质
可以认为Tomca底层的JSPServlet自动帮我们实现了转换。jsp就是html中间插入java代码,最终要先编译为servlet,然后转换为class文件
小知识
当一个进程有 500 个线程在跑的话,那性能已经是很低很低了。Tomcat 默认配置的最大请求数是 150,需要考虑上 Nginx 或者 F5。
底层
TODO
MyBatis
MyBatis急速入门
经典面试题
狂神说B站教程 或 mybatis 中文官网即可
环境搭建,CRUD
核心配置文件,ResultMap
分页
limit关键字实现
RowBounds实现分页
interceptor
PageHelper
注解开发,SQL 共有片段
#{} 跟${}
#{}速度快,能防止sql注入
${}是直接拼接到语句上,执行语句,
能用的#就不用$
${}是直接拼接到语句上,执行语句,
能用的#就不用$
连表查询
一对多
collection
多对一
association
动态SQL
if、choose、trim、foreach
缓存
缓存策略
FIFO
LFU
LRU默认
缓存原理图
一级缓存是指 SqlSession 级别的缓存
二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存
MyBatisPlus
存在意义: 简化加强MyBatis而生
引入后依靠BaseMapper 跟Wrapper 几乎可以不用写xml配置
模块
CURD
插入
主键生成策略
更新
普通更新
插入时间、更新时间
数据库设计级别支持
代码级别支持
乐观锁
跟JUC中的一样
查询/分页查询
删除/逻辑删除
扩展
性能分析插件
条件构造器 QueryWrapper
代码自动生成器 AutoGenerator
Hibernate
TODO
Spring
Spring存在的意义主要是思想的理解跟消化
深入理解 IOC DI 跟AOP的思维方式。
Spring 急速入门
Spring Bean声明周期
IOC跟AOP的手动demo实现
XML解析 跟 反射
Spring AOP
动态代理
Proxy 跟 InvocationHandler
getProxyClass0
在程序运行时利用反射动态创建代理对象
byte[] proxyClassFile 跟底层就可以发现基本原理
Cglib
无Interface时
AOP实现的五种环绕
1.前置通知
2.环绕通知
3.后置通知
4.异常通知
5.最终通知
2.环绕通知
3.后置通知
4.异常通知
5.最终通知
顺序
Spring 整合MyBatis
聊1小时IOC refresh(里面13个函数)
聊1小时AOP
Spring 循环依赖
什么是循环依赖
如何解决循环依赖
解决的原理
1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
Spring中设计模式
SpringMVC
(1):用户请求发送给DispatcherServlet,DispatcherServlet调用HandlerMapping处理器映射器;
(2):HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;
(3):DispatcherServlet会调用相应的HandlerAdapter;
(4):HandlerAdapter经过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet
(5):DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;
(6):DispatcherServlet根据View进行渲染视图;
(2):HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;
(3):DispatcherServlet会调用相应的HandlerAdapter;
(4):HandlerAdapter经过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet
(5):DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;
(6):DispatcherServlet根据View进行渲染视图;
->DispatcherServlet->HandlerMapping->Handler
->DispatcherServlet->HandlerAdapter处理handler->ModelAndView
->DispatcherServlet->ModelAndView->ViewReslover->View
->DispatcherServlet->返回给客户
->DispatcherServlet->HandlerAdapter处理handler->ModelAndView
->DispatcherServlet->ModelAndView->ViewReslover->View
->DispatcherServlet->返回给客户
核心DispatcherServlet
图
SpringBoot
什么是微服务
微服务原文
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置核心思想开展工作
SpringBoot教程
01 springBoot HelloWorld
急速上手
02 SpringBoot 运行原理初探
父依赖,版本仲裁
spring-boot-starter 跟 spring-boot-starter-xxx
META-INF/spring.factories 各种各样的autoconfigure 具体可看第五节原理分析
03 SPringBoot yaml 语法
以数据为中心
各种配置 跟注入属性
04 SpringBoot 多环境配置
05 SpringBoot 自动配置原理
1、SpringBoot启动会加载大量的自动配置类
2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
5、xxxxAutoConfigurartion:自动配置类;给容器中添加组件
6、xxxxProperties:封装配置文件中相关属性;
2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
5、xxxxAutoConfigurartion:自动配置类;给容器中添加组件
6、xxxxProperties:封装配置文件中相关属性;
06 SpringBoot 自定义starter
相对来说根据上一章节操作即可,可以选择性的定制化一些东西
07 SSpringBoot Web开发静态资源问题
1、网页访问/webjars/** 会在 classpath:/META-INF/resources/webjars/ 找资源
2、网页访问"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
3、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
4、favicon.ico :所有的 **/favicon.ico 都是在静态资源文件下找;
2、网页访问"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
3、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
4、favicon.ico :所有的 **/favicon.ico 都是在静态资源文件下找;
08 SpringBoot Thymeleaf模板引擎 跟 MVC自动配置原理
模板引擎意义:后台处理好的数据 + 模板引擎 = 前端最终展示页面
常见模板引擎:jsp、freemarker、Thymeleaf
Thymyleaf
中文汇总教程
TODO
SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的
子主题
可以自定义一些MVC组件然后跟系统组件拼装起来一起用,谨慎使用@EnableWebMvc
09 SpringBoot 配置国际化
整体来说没啥特别难的,都是百度配置即可,主要是找好看的UI(bootstrap) 然后设置中英文切换按钮
layui 是一款采用自身模块规范编写的前端 UI 框
X-admin基于layui的轻量级前端后台管理框架
10 SpringBoot 拦截器跟 WebServer个性化配置
HandlerInterceptor 的子类实现跟注册
Servlet、Filter、Listener 功能的实现
11、SpringBoot 整合JDBC 、Druid、MyBatis
Spring Data的使命是为数据访问提供熟悉且一致的基于Spring的编程模型,同时仍保留底层数据存储的特殊特性。
HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀;
JDBCTemplate 的使用
JDBCTemplate 的使用
Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控,如何整合 如何配置查看 SQL查询
整合MyBatis
pom依赖的引入,application.yaml的配置等,相对来说简单
12、SpringBoot 整合 Security
功能:实现 用户认证(Authentication)和用户授权(Authorization)
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式
相比 Shiro更加简单方便,添加若干配置即可完成权限配置。
13、SpringBoot 整合Shrio
1、引入Shrio必备依赖
2、自定义实现Realm用户登陆跟权限认证。
3、编写ShiroConfig实现Realm、SecurityManager、ShiroFilterFactoryBean三个必备组件到集成。
4、相比Spring Security 复杂一些可定制化更多一点。
2、自定义实现Realm用户登陆跟权限认证。
3、编写ShiroConfig实现Realm、SecurityManager、ShiroFilterFactoryBean三个必备组件到集成。
4、相比Spring Security 复杂一些可定制化更多一点。
14、SpringBoot 终极整合 Swagger
前后端交互文档,定制化文档,多文档,开发环境记得关闭
15、SpringBoot 异步、邮件、定时
比较简单 会用即可
SpringBoot教程wiki汇总
方志朋SpringBoot 各种整合三方
整合Redis
老版本 jedis:采用直连,多个线程操作是不安全的,如果避免不安全需考虑jedis pool 更像BIO
新版本 lettuce : 采用netty,实例可以在多个线程中共享,更像NIO
多个微服务组合带来的问题(问题的根本:网络不可靠)
1、客户端如何访问多个服务,如何服务路由
2、多服务之间如何通讯,如何RPC调用
3、多个服务如何治理,如何高可用
4、有服务挂了咋办呢,如何服务降级
2、多服务之间如何通讯,如何RPC调用
3、多个服务如何治理,如何高可用
4、有服务挂了咋办呢,如何服务降级
解决方案
Spring Cloud NetFlix 一站式继绝方案
Api网关:zuul组件
Feign:Http通讯方式,同步并阻塞
服务注册跟发现: Eureka
熔断机制:Hystrix
Api网关:zuul组件
Feign:Http通讯方式,同步并阻塞
服务注册跟发现: Eureka
熔断机制:Hystrix
Apache Dubbo zookeeper
API : 没有,找第三方实现
Dubbo : 高性能的基于Java的RPC通信框架
服务注册跟发现 : zookeeper
熔断: 借助Hystrix
API : 没有,找第三方实现
Dubbo : 高性能的基于Java的RPC通信框架
服务注册跟发现 : zookeeper
熔断: 借助Hystrix
Spring Clound alibaba 一站式解决方案
服务网格:下一代微服务标准 Server Mesh
代表方案:istio
代表方案:istio
开源项目
练手个人博客模板
技术体系:Docker+SpringBoot2.0+Mybatis+thymeleaf
eladmin后台管理
Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前后端分离的后台管理系统
根据语言分类 + star + 自我需求 然后各种开源网址找demo即可
6个开源项目
RPC框架
Spring Cloud(HTTP调用实现RPC的功能)
用的少,markdown下的了
用的少,markdown下的了
方志朋讲解
跟我学SpringCLoud
由来:微服务化思想的理解 + 服务QPS太多 ==> 多种服务集群式部署
核心点:网络访问传输不可靠 如何解决
微服务需解决的核心四个问题
1、集群式服务对外网关如何提供
2、服务之间如何通讯,RPC还是HTTP
3、服务之间如何治理,注册跟发现
4、服务熔断跟降级如何处理
微服务思维理解
优点
缺点
SpringCloud简介
Spring Cloud是一个微服务框架的规范,注意,只是规范,他不是任何具体的框架。而不同组织可以根据这个规范造就不同产品
微服务技术栈
业界常见解决方案
1、 Spring Cloud NetFlix
API网关:zuul组件
通信: Http通信方式,同步,阻塞
服务注册和发现:Eureka
熔断机制:Hystrix
负载均衡:Ribbon 或 Feign
通信: Http通信方式,同步,阻塞
服务注册和发现:Eureka
熔断机制:Hystrix
负载均衡:Ribbon 或 Feign
2、Apache Dubbo Zookeeper
API网关:没有,找第三方组件(比如整合zull组件),或者自己实现
通信:Dubbo 是一个基于Java的高性能的RPC通信框架(性能比Feign强大)
服务注册和发现:Zookeeper
熔断机制:没有,需要借助Hystrix
通信:Dubbo 是一个基于Java的高性能的RPC通信框架(性能比Feign强大)
服务注册和发现:Zookeeper
熔断机制:没有,需要借助Hystrix
3、Spring Cloud Alibaba
依托阿里巴巴实际业务技术提供最新的一站式解决方案
SpringCloud Netfilx
伦敦地铁站的名称版本
官网
SpringCloud 与Dubbo区别
SpringCloud:采用HTTP的REST方式调用,定位是微服务架构下一站式解决方案。
Dubbo:RPC通讯,存在代码级别强依赖,Dubbo的定位是一款RPC框架。
组件使用步骤
1、导入依赖
2、编写配置文件
3、开启这个功能@EnableXXX
4、配置类
NetFilx 5大神兽配置
Eureka (由蕊卡)
功能:类似于Dubbo的注册中心,比如Zookeeper。
主要组成部分
Eureka Server:提供服务的注册与发现
Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能找到
Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务
EureKa自我保护机制:好死不如赖活着
某时刻某一个微服务不可用,eureka不会立即清理,依旧会对该微服务的信息进行保存!
该保护机制的目的是避免网络连接故障,因为服务只有在启动时才会注册到Eureka中
测试阶段可设置 关闭自我保护模式
注意搞成Eureka集群来保证高可用
Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
Eureka 跟Zoomkeeper 区别
分布式CAP
C (Consistency) 强一致性
A (Availability) 可用性
P (Partition tolerance) 分区容错性
A (Availability) 可用性
P (Partition tolerance) 分区容错性
Zookeeper 保证的是CP
如果master挂掉有30~120秒的选举新master空档期导致全部服务不可用
Eureka 保证的是AP
在设计时就优先保证可用性,Eureka各个节点都是平等的
负载均衡(load balance)
Ribbon:基于客户端
在客户端配置添加依赖配置启动注解实现负载均衡
一般是 Ribbon注解 + RestTemplate
如自我实现轮询算法需注意包的位置,IRule
Feign:基于服务端
Feign默认集成了Ribbon,通过Feign只需要定义服务绑定接口且以声明式的方法,需要再后端设置好interface跟Rest关联。更像接口编程
抉择:根据个人习惯而定,如果喜欢REST风格使用Ribbon;如果喜欢社区版的面向接口风格使用Feign.
Hystrix:服务熔断
服务雪崩:
用法
服务降级
服务降级:当某些服务访问火热时尝试将冷门服务停止,尽可能的将系统资源让给优先级高的服务。双11会把跟交易无关系统进行降级,你无法退货跟查看蚂蚁森林等。
场景:当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
降级考虑
1、那些服务是核心服务,哪些服务是非核心服务
2、那些服务可以支持降级,那些服务不能支持降级,降级策略是什么
3、除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
2、那些服务可以支持降级,那些服务不能支持降级,降级策略是什么
3、除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
自动降级分类
服务熔断和降级的区别
位置
服务熔断在服务端:某个服务超时异常引发熔断跟保险丝类似
服务降级在客户端:从网站请求负载考虑,当服务不可用时返回一个默认值。
服务降级在客户端:从网站请求负载考虑,当服务不可用时返回一个默认值。
触发原因
熔断一般是某个服务故障导致,是框架级别处理。
降级一般从整体负载考虑,对业务有层级之分。
降级一般从整体负载考虑,对业务有层级之分。
实现方式
熔断一般是自我熔断。
降级具有代码侵入性,由控制器完成或自动降级。
降级具有代码侵入性,由控制器完成或自动降级。
限流:限制并发的请求访问量,超过阈值则拒绝;
降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;
熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复
降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;
熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复
dashboard:Spring Boot Actuator 配套监控页面
Zuul 路由网关
路由功能:
负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
滤器功能:
负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
SpringCloud Config
首先对于分布式系统而言我们就不应该去每个应用下去分别修改配置文件,再者对于重启应用来说,服务无法访问所以直接抛弃了可用性,这是我们更不愿见到的
可以把共有配置在本地一个公共目录保存或者提交到GIt/SVN
服务端
客户端
Spring Cloud Bus
如果我在应用运行时去更改远程配置仓库(Git)中的对应配置文件,那么依赖于这个配置文件的已启动的应用不会进行其相应配置的更改
用于将服务和服务实例与分布式消息系统链接在一起的事件总线。在集群中传播状态更改很有用(例如配置更改事件)。
Spring Cloud Zipkin
是一个可以采集并且跟踪分布式系统中请求数据的组件,让开发者可以更加直观的监控到请求在各个微服务所耗费的时间等,Zipkin:Zipkin Server、Zipkin Client。
SpringCloud Alibaba 先mark下 需要的话学
Dubbo(官方文档大法好)
远程过程调用(Remote Procedure Call) 通俗说
什么是RPC
1、解决分布式系统中,服务之间的调用问题。
2、远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。
2、远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。
RPC 框架所需部件:约定要通信协议,序列化的格式、一些容错机制、负载均衡策略、监控运维和一个注册中心!
Dubbo简介
官网:http://dubbo.apache.org/zh-cn/
阿里巴巴研发开源工具,主要为2.6.x跟2.7.x版本。是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:
面向接口的远程方法调用
智能容错和负载均衡
以及服务自动注册和发现。
智能容错和负载均衡
以及服务自动注册和发现。
感觉有点SpringCloud 的意思
Dubbo 分层架构
Business(业务层)
Service:业务层,就是咱们开发的业务逻辑层。
RPC 层
Config配置层:主要围绕 ServiceConfig 和 ReferenceConfig,初始化配置信息。
Proxy 代理层:服务提供者还是消费者都会生成一个代理类,使得服务接口透明化,代理层做远程调用和返回结果。
Register 注册层:封装了服务注册和发现。
Cluster 路由和集群容错层:负责选取具体调用的节点,处理特殊的调用要求和负责远程调用失败的容错措施
Monitor 监控层:负责监控统计调用时间和次数。
Portocol 远程调用层:主要是封装 RPC 调用,主要负责管理 Invoker,Invoker代表一个抽象封装了的执行体
Remoting
Exchange 信息交换层:用来封装请求响应模型,同步转异步
Transport 网络传输层:抽象了网络传输的统一接口,这样用户想用 Netty 就用 Netty,想用 Mina 就用 Mina
Serialize 序列化层:数据序列化跟反序列化。
Dubbo调用过程
服务暴露过程
Provider 启动,通过 Proxy 组件根据具体的协议 Protocol 将需要暴露出去的接口封装成 Invoker,再通过 Exporter 包装一下将Exporter通过Registry注册到注册中心。
消费过程
1、消费者启动从注册中心拉取服务提供者元信息。
2、调用流程通过Proxy实现,Proxy持有个Invoker对象,
3、通过Cluster先从Directory获取可用服务Invoker列表,配合一些路由规则。
4、可用Invoker通过LoadBalance选取一个,经过Filter做统计,通过Client进行数据传输,比如Netty。
5、Codec接口进行序列化跟反序列化后最终发给服务提供者。
6、服务端收到信息反序列化后请求仍绕线程池。
7、线程池中拿个线程来找对应的Exporter,Invoker,最终实现服务调用后将结果原路返回。
2、调用流程通过Proxy实现,Proxy持有个Invoker对象,
3、通过Cluster先从Directory获取可用服务Invoker列表,配合一些路由规则。
4、可用Invoker通过LoadBalance选取一个,经过Filter做统计,通过Client进行数据传输,比如Netty。
5、Codec接口进行序列化跟反序列化后最终发给服务提供者。
6、服务端收到信息反序列化后请求仍绕线程池。
7、线程池中拿个线程来找对应的Exporter,Invoker,最终实现服务调用后将结果原路返回。
Dubbo调用过程
SPI(Service Provider Interface)机制讲解
SPI定义
全称 Service Provider Interface,是一种服务发现机制。约定在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后文件里面记录的是此 jar 包提供的具体实现类的全限定名
当引用了某个 jar 包的时候就可以去找这个 jar 包的 META-INF/services/ 目录,再根据接口名找到文件,然后读取文件里面的内容去进行实现类的加载与实例化
当引用了某个 jar 包的时候就可以去找这个 jar 包的 META-INF/services/ 目录,再根据接口名找到文件,然后读取文件里面的内容去进行实现类的加载与实例化
Java SPI 源码分析
简单的说就是先找当前线程绑定的 ClassLoader,如果没有就用 SystemClassLoader,然后清除一下缓存,再创建一个 LazyIterator。
1、通过文件里填写的全限定名加载类,并且创建其实例放入缓存之后返回实例。
2、约定一个目录,根据接口名去那个目录找到文件,文件解析得到实现类的全限定名,然后循环加载实现类和创建其实例。
3、不足:查找扩展实现类遍历配置文件然后实现类全部实例化!实例化有些用不上。
2、约定一个目录,根据接口名去那个目录找到文件,文件解析得到实现类的全限定名,然后循环加载实现类和创建其实例。
3、不足:查找扩展实现类遍历配置文件然后实现类全部实例化!实例化有些用不上。
Dubbo SPI
通过 ExtensionLoader,我们可以加载指定的实现类,类似 Java SPI 中 ServiceLoader 的存在。
getExtensionLoader():判断缓存是否有该类型的ExtensionLoader,没有则创建加入缓存再返回。
getExtension(): double check检查缓存,缓存未命中则创建拓展对象
createExtension():
通过 getExtensionClasses 获取所有的拓展类
通过反射创建拓展对象
向拓展对象中注入依赖
将拓展对象包裹在相应的 Wrapper 对象中
通过反射创建拓展对象
向拓展对象中注入依赖
将拓展对象包裹在相应的 Wrapper 对象中
Adaptive 注解 - 自适应扩展
Dubbo 通过一个代理机制实现了自适应扩展,为想扩展的接口生成一个代理类,可以通过JDK 或者 javassist 编译你生成的代理类代码,然后通过反射创建实例。
这个实例里面的实现会根据本来方法的请求参数得知需要的扩展类,然后通过 ExtensionLoader.getExtensionLoader(type.class).getExtension(从参数得来的name),来获取真正的实例来调用。
Dubbo SPI 则自己实现了 SPI,可以通过名字实例化指定的实现类,并且实现了 IOC 、AOP 与 自适应扩展 SPI
1、加载标注有@Adaptive注解的接口,如果不存在,则不支持Adaptive机制;
2、为目标接口按照一定的模板生成子类代码,并且编译生成的代码,然后通过反射生成该类的对象;
3、结合生成的对象实例,通过传入的URL对象,获取指定key的配置,然后加载该key对应的类对象,最终将调用委托给该类对象进行。
2、为目标接口按照一定的模板生成子类代码,并且编译生成的代码,然后通过反射生成该类的对象;
3、结合生成的对象实例,通过传入的URL对象,获取指定key的配置,然后加载该key对应的类对象,最终将调用委托给该类对象进行。
@Adaptive注解
在修饰类的时候不会生成代理类,因为这个类就是代理类
修饰在方法上的时候会生成代理类
WrapperClass - AOP
一个扩展接口可能有多个扩展实现类,而这些扩展实现类会有一个相同的或者公共的逻辑,如果每个实现类都写一遍代码就重复了,并且比较不好维护。因此就搞了个包装类,Dubbo 里帮你自动包装
injectExtension - IOC
查找 set 方法,根据参数找到依赖对象则注入。
Activate 注解
group 表示修饰在哪个端,是 provider 还是 consumer,value 表示在 URL参数中出现才会被激活,order 表示实现类的顺序。
Dubbo的服务暴露过程
URL
Dubbo 就是采用 URL 的方式来作为约定的参数类型,被称为公共契约, Dubbo 用 URL 作为配置总线,贯穿整个体系
protocol://username:password@host:port/path?key=value&key=value
protocol:指的是 dubbo 中的各种协议,如:dubbo thrift http
username/password:用户名/密码
host/port:主机/端口
path:接口的名称
parameters:参数键值对
username/password:用户名/密码
host/port:主机/端口
path:接口的名称
parameters:参数键值对
配置解析
Dubbo 利用了 Spring 配置文件扩展了自定义的解析xml
spring.schemas 就是指明了约束文件的路径
spring.handlers 指明了利用该 handler 来解析标签
让用户去固定路径的固定文件名去找你扩展的东西
本质就是为了生成 Spring 的 BeanDefinition,然后利用 Spring 最终创建对应的对象。
暴露浅析
代码的流程
第一步是检测配置,如果有些配置空的话会默认创建,并且组装成 URL 。
spring IOC 实现自动刷新调用, doExportUrls Dubbo 支持多注册中心,并且支持多个协议
doExportUrlsFor1Protocol
1、根据配置往map中put后构建URL
2、根据URL进行本地暴露,远程暴露,监控中心。
1、根据自适应扩展调用方法时由Javassist 生成代理类,代理类被封装正Invoker(屏蔽细节,统一暴露一个可执行体),最终通过SPI export 出去。
2、暴露的本地服务在内部调用的时候可以直接消费同一个 JVM 的服务避免了网络间的通信
3、通过自适应扩展根据URL内容调用不同的protocol export
2、暴露的本地服务在内部调用的时候可以直接消费同一个 JVM 的服务避免了网络间的通信
3、通过自适应扩展根据URL内容调用不同的protocol export
第二步是暴露服务,包括暴露到本地的服务和远程的服务。
第三步是注册服务至注册中心。
从对象构建转换的角度
第一步是将服务实现类转成 Invoker。
第二部是将 Invoker 通过具体的协议转换成 Exporter
第二部是将 Invoker 通过具体的协议转换成 Exporter
暴露总结
服务暴露的过程起始于 Spring IOC 容器刷新完成之时 ServiceBean ,具体的流程就是根据配置拼接 URL,再利用 Dubbo SPI 机制根据 URL 的参数选择对应的实现类,实现扩展。
通过 javassist 动态封装 ref (你写的服务实现类),统一暴露出 Invoker 使得调用方便,屏蔽底层实现细节,然后封装成 exporter 存储起来,等待消费者的调用,并且会将 URL 注册到注册中心,使得消费者可以获取服务提供者的信息。
通过 javassist 动态封装 ref (你写的服务实现类),统一暴露出 Invoker 使得调用方便,屏蔽底层实现细节,然后封装成 exporter 存储起来,等待消费者的调用,并且会将 URL 注册到注册中心,使得消费者可以获取服务提供者的信息。
暴露流程细节图
Dubbo的服务引用过程
服务的引入时机有两种(默认 懒汉式)
饿汉式:通过实现 Spring 的InitializingBean接口中的 afterPropertiesSet方法,容器通过调用 ReferenceBean的 afterPropertiesSet方法时引入服务
懒汉式:懒汉式是只有当这个服务被注入到其他类中时启动引入流程,也就是说用到了才会开始服务引入。
关于Spring
BeanFactory: IOC 容器,具有多种实现类, Spring 里面的 Bean 都归它管
FactoryBean:也是一个Bean,但是可以把所需Bean封装一层做复杂组装操作,通过getObject()获取Bean。
ObjectFactory :用于延迟查找的场景,只有当getObject()方法时,才会触发 Bean 实例化等生命周期。
FactoryBean:也是一个Bean,但是可以把所需Bean封装一层做复杂组装操作,通过getObject()获取Bean。
ObjectFactory :用于延迟查找的场景,只有当getObject()方法时,才会触发 Bean 实例化等生命周期。
服务引入的三种方式
本地引入:服务暴露时本地暴露,避免网络调用开销。
直接连接引入远程服务:不启动注册中心,直接写死远程Provider地址 进行直连。
通过注册中心引入远程服务:通过注册中心抉择如何进行负载均衡调用远程服务。
服务引入源码跟踪
1、先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过URL上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。
2、如果是注册中心那么会向注册中心注册自己的信息,然后订阅注册中心相关信息,得到远程 provider的 ip 等信息,再通过netty客户端进行连接,默认是共享连接。
3、在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。
4、然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。
2、如果是注册中心那么会向注册中心注册自己的信息,然后订阅注册中心相关信息,得到远程 provider的 ip 等信息,再通过netty客户端进行连接,默认是共享连接。
3、在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。
4、然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。
大致流程
Dubbo服务调用过程
常见三种通讯传输协议形式(Dubbo采用第三种)
固定长度形式:固定长度读协议,后面读数据,效率高但是存在浪费。
特殊字符隔断形式:优点是长度自由,缺点就是需要一直读,直到读到一个完整的协议单元之后才能开始解析
header+body 形式:头部是固定长度的,然后头部里面会填写 body 的长度, body 是不固定长度的,这样伸缩性就比较好了,可以先解析头部,然后根据头部得到 body 的 len 然后解析 body。Dubbo采用此方式
序列化
约定序列化器,序列化大致分为两大类,一种是字符型(XML或json 人可看懂 传输效率低),一种是二进制流(数据紧凑,机器友好)。默认使用hessian2。
调用三种方式 DubboInvoker 的 doInvoke
oneway:不关心请求是否发送成功,消耗最小。
异步调用Async:Dubbo天然异步,客户端调用请求后将返回的ResponseFuture存到上下文中,用户可以随时调用future.get获取结果。
异步调用通过唯一ID标识此次请求,
同步调用sync : 在 Dubbo 源码中就调用了 future.get,用户感觉方法被阻塞了,必须等结果后才返回。
调用流程之服务端源码分析
默认全部都会进入线程池
调用流程总结:
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 的线程,完成一次远程调用全过程。
Dubbo集群容错负载均衡
Invoker是啥
将能调用的服务的对象都封装成 invoker
ClusterInvoker : 封装了服务引入生成的 invoker 们,赋予其集群容错等能力
服务目录是啥Directory
类似服务的目录,通过这个目录来查找远程服务,实际是一堆invoker集合。同样的服务可能有多个提供者。还实现了监听注册中心的功能(指的是RegistryDirectory )
RegistryDirectory
1、获取 invoker 列表
2、监听注册中心的变化
3、刷新 invokers。
2、监听注册中心的变化
3、刷新 invokers。
StaticDirectory
是静态的是因为多注册中心是写在配置里面的,不像服务可以动态变更
用在多注册中心的时候,它是一个静态目录,即固定的不会增减的
路由规则
条件路由 ConditionRouter (常用)
格式: [服务消费者匹配条件] => [服务提供者匹配条件]
脚本路由 ScriptRouter
标签路由 TagRouter
Dubbo 的 Cluster作用
是一个中间层,为消费者屏蔽了服务提供者的情况,简化了消费者的使用,并且也更加方便的替换各种集群容错措施。
架构师初始思维:没有什么是加一层解决不了滴
架构师初始思维:没有什么是加一层解决不了滴
FailoverClusterInvoker:识别自重重试若干次,还自带记录上次invoker功能。
FailfastClusterInvoker:只调用一次,失败后立即抛出异常。
FailsafeClusterInvoker:失败安全的策略,抛错就日志记录,返回空结果。
FailbackClusterInvoker:记录调用失败的任务并返回空结果,定时任务对失败调用重调。
ForkingClusterInvoker:所有 invoker 都通过线程池进行并发调用,有一个返回则认为任务完成。
BroadcastClusterInvoker:把所有 invoker 逐个调用,有一个出现失败则异常。
FailfastClusterInvoker:只调用一次,失败后立即抛出异常。
FailsafeClusterInvoker:失败安全的策略,抛错就日志记录,返回空结果。
FailbackClusterInvoker:记录调用失败的任务并返回空结果,定时任务对失败调用重调。
ForkingClusterInvoker:所有 invoker 都通过线程池进行并发调用,有一个返回则认为任务完成。
BroadcastClusterInvoker:把所有 invoker 逐个调用,有一个出现失败则异常。
服务目录、集群、负载均衡 结合使用
Dubbo 中的负载均衡
RandomLoadBalance(默认):加权随机
LeastActiveLoadBalance:最少活跃数负载均衡
ConsistentHashLoadBalance:一致性 Hash 负载均衡算法
RoundRobinLoadBalance:加权轮询到平滑加权轮询
LeastActiveLoadBalance:最少活跃数负载均衡
ConsistentHashLoadBalance:一致性 Hash 负载均衡算法
RoundRobinLoadBalance:加权轮询到平滑加权轮询
Dubbo面试题
1、RPC名词解释,跟HTTP区别,跟SpringCloud区别。
2、Dubbo整体流程,架构图。
3、服务暴露的流程,服务发现的流程,服务调用的流程,
4、Directory,Cluster,LoadBalance
5、Dubbo自己的SPI, Javassist进行动态代理(字符串拼接成字节码)
2、Dubbo整体流程,架构图。
3、服务暴露的流程,服务发现的流程,服务调用的流程,
4、Directory,Cluster,LoadBalance
5、Dubbo自己的SPI, Javassist进行动态代理(字符串拼接成字节码)
自我实现RPC 重要点
1、Netty高性能网络传输。网络传输协议,序列化二进制。
2、透明化提供服务需用代理实现。
3、通过集群进行服务的注册跟发现。
4、监控机制、埋点统计运维。
5、搞一个跨语言服务IDL,比如Thrift来实现跨语言服务。
2、透明化提供服务需用代理实现。
3、通过集群进行服务的注册跟发现。
4、监控机制、埋点统计运维。
5、搞一个跨语言服务IDL,比如Thrift来实现跨语言服务。
配置优先级
方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。
如果级别一样,则消费方优先,提供方次之。
Dubbo高频面试题
Netty
TODO
前端
HTML
TCP/IP
CSS
JavaScript
Node js
Ajax
VUE
HTTP
Get Post 区别
1. get 获取数据, 一次请求 在 明文URL中,HTTP对 长度无限制但是浏览器做了限制。IE限制2048字节,firefox支持10W
2. post 提交数据,在 URL 对应body中,第一次请求解析请求头 比如看是否有权限什么的,服务端返回100-continue 再二次发送请求体给服务端。
3. 本质都是 TCP 链接。
2. post 提交数据,在 URL 对应body中,第一次请求解析请求头 比如看是否有权限什么的,服务端返回100-continue 再二次发送请求体给服务端。
3. 本质都是 TCP 链接。
HTTP版本迭代
1.1 长连接传输,AB发送无需等待 | 只能压缩body,头部阻塞,来回冗余配置,只能客户端请求。
2 头部压缩,二进制传输,优先级引入,多路复用 | 多路复用导致timeout,超时阻塞还未解决。
3 特制UDP,连接管理,拥塞窗口,流量控制,QUIC协议。
2 头部压缩,二进制传输,优先级引入,多路复用 | 多路复用导致timeout,超时阻塞还未解决。
3 特制UDP,连接管理,拥塞窗口,流量控制,QUIC协议。
HTTP 1.1 提速点
短链接 变长连接
服务器 重定向
请求缓存机制
减少请求次数,多个小文件合并为1个大请求,按需加载。
无损压缩跟有损压缩
TCP
2 MSL 等待延迟
1,保证TCP全双工连接能够可靠关闭
2,保证这次连接的重复数据段从网络中消失
2,保证这次连接的重复数据段从网络中消失
进行DNS解析操作,根据DNS解析的结果查到服务器IP地址
通过ip寻址和arp,找到服务器,并利用三次握手建立TCP连接
浏览器生成HTTP报文,发送HTTP请求,等待服务器响应
服务器处理请求,并返回给浏览器
根据HTTP是否开启长连接,进行TCP的挥手过程
浏览器根据收到的静态资源进行页面渲染
通过ip寻址和arp,找到服务器,并利用三次握手建立TCP连接
浏览器生成HTTP报文,发送HTTP请求,等待服务器响应
服务器处理请求,并返回给浏览器
根据HTTP是否开启长连接,进行TCP的挥手过程
浏览器根据收到的静态资源进行页面渲染
SYN攻击
一,可缩短 SYN Timeout时间,可以通过缩短从接收到SYN报文到确定这个报文无效并丢弃该连接的时间,可以降低服务器负荷。
二,设置SYN Cookie,给每个请求连接的IP地址分配一个Cookie,如果短时间内收到同一个IP的重复SYN报文,则以后从这个IP地址来的包会被丢弃。
二,设置SYN Cookie,给每个请求连接的IP地址分配一个Cookie,如果短时间内收到同一个IP的重复SYN报文,则以后从这个IP地址来的包会被丢弃。
面向连接的、可靠的、基于字节流的、工作在传输层的数据传输服务
网络层 + 链路层
IP 分类 A B C 类 公网IP 主机IP 掩码
ARP RARP
MAC
路由器
DHCP
NAT NAPT
ICMP
网络基础
网卡(Network Interface Card)
工作在链路层组件,是局域网中连接计算机和传输介质的接口,网线插到网卡上,网卡插在电脑的主板上。起到连接你上网的作用
网卡上面装有处理器和存储器(包括RAM和ROM)
1、数据的封装跟解封
2、链路管理,自动处理错误帧数据。
3、编码与译码,数据加工发到网络,接受网络数据解析。
2、链路管理,自动处理错误帧数据。
3、编码与译码,数据加工发到网络,接受网络数据解析。
Mac地址
Media Access Control(MAC)地址是6字节数据烧录在网卡上的,MAC地址跟身份证号一样具有全球唯一性。
集线器(hub)
一个口收到的信号,原封不动的发送给所有其他的口,由其他的口上的设备自己决定是否接收信号。有点类似广播,但是比广播更纯粹
交换机(Switch)
交换机形象说
作用:交换机是用来连接多个终端(电脑、手机、打印机),然后帮各个终端来转发数据的,适合局域网内互联,工作在数据链路层
原理:交换机的转发原理,是记录下每个终端的mac地址,以及这个mac地址对应哪个接口。
网关(Gateway)
网关这个概念是逻辑层面的,网关一个大概念,不具体特指一类产品,只要连接两个不同的网络的设备都可以叫网关
路由器(Router)
一般特指能够实现路由寻找和转发的特定类产品,路由器很显然能够实现网关的功能
(1)网络互连,实现不同网络互相通信
(2)数据处理,提供分组过滤转发、优先级、防火墙等功能
(3)网络管理,提供配置管理、性能管理、流量控制等功能
(2)数据处理,提供分组过滤转发、优先级、防火墙等功能
(3)网络管理,提供配置管理、性能管理、流量控制等功能
路由器通俗说
网络知识小汇总
原理: 固定统一的通信协议TCP/IP
猫:学名宽带无线猫
猫的学名叫调制解调器,它的作用是将数字信号(电脑想要发送的信息)转换成模拟信号(网线中的电流脉冲)从而使信息在网线中传输。家用路由器一般都是路由猫,即路由器兼顾了猫和简单交换机的功能
路由器做了2个普通交换机做不了的
1、不同网段互连:每个主机有自己的ip地址,ip地址又是分网段的,要实现不同网段的互连,必须用到路由器。各地的玩家,ip地址一定是不同网段的。
2、网络地址转换:主机在企业内部,或者网吧内部,使用的都是私网ip地址,私网ip地址无法进入互联网,想进入互联网,必须转成公网ip。ip地址的转换,也是由路由器来做的。
2、网络地址转换:主机在企业内部,或者网吧内部,使用的都是私网ip地址,私网ip地址无法进入互联网,想进入互联网,必须转成公网ip。ip地址的转换,也是由路由器来做的。
DNS
域名服务器(Domain Name Server)。在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,DNS就是进行域名解析的服务器 。
DHCP服务器
(动态主机配置协议)是一个局域网的网络协议。指的是由服务器控制一段IP地址范围,客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码
Scala
推荐看韩顺平Scala280 讲解不错
超全思维导图
Scala概述
一切皆对象,无基本类型,一切加减乘除都为方法
Scala中数据类型
Scala代码跟JVM关系,最终编译成class 文件存在,找几个demo用JD-GUI反编译理解加深,理解Object存在意义
运算符 程序流程控制
无break continue
函数式编程基础 面向对象编程基础
包,抽象,封装,继承,抽象类,匿名子类
伴生对象,trait,自身类型,implicit
可变集合不可变集合
map、flatMap、reduce、flod、scan、zip、iterator、stream、view、par、match
吊炸天的match
偏函数,高级函数,匿名函数,参数推断,闭包,柯里化,控制抽象
Scala中的递归
Akka 网络编程 Spark Master Worker 进程通讯项目
设计模式、泛型、上下界、视图界定、上下文界定、协变逆变不变
主要是面向对象编程跟流式编程的思维转变
写起来爱不释手
Scala数据结构算法
方法跟 函数 区别
重点主要是跟Spark还有Flink 联动起来coding
Scala版WordCount
消息中间件
RabbitMQ
快速入手RabbitMQ
存在的意义:异步处理、应用解耦、流量削峰、日志处理
如何确保消息正确地发送至RabbitMQ
事务
同步或者异步的confirm机制
MQ发送响应信息给生产者
如何确保消息接收方消费了消息
消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息
如何避免消息重复投递或重复消费
死信队列和延迟队列
理解主要的模式跟场景
常见面试题
Kafka
顺序读写磁盘 + 直接内存映射MMAP + 零拷贝 + 批量处理 + 分区消费
Kafka 导致磁盘满了 : 修改设置 数据最大保存小时数
Kafka分区策略
1、Range 面向每个主题,对同一个主题里面的分区按照序号进行排序,并把消费者线程按照字母顺序进行排序。然后用分区数除以消费者线程数量来判断每个消费者线程消费几个分区。
2、RoundRobin 将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询算法逐个将分区以此分配给每个消费者。
3、Sticky Sticky分配策略,分区的分配要尽可能的均匀,分区的分配尽可能的与上次分配的保持相同。
2、RoundRobin 将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询算法逐个将分区以此分配给每个消费者。
3、Sticky Sticky分配策略,分区的分配要尽可能的均匀,分区的分配尽可能的与上次分配的保持相同。
Kafka重平衡
基本流程就是 Coordinator协调者 感知到 消费者组的变化,在心跳的过程中发送重平衡信号通知各个消费者离组,
然后消费者重新以 JoinGroup 方式加入 Coordinator,并选出Consumer Leader。
当所有消费者加入 Coordinator,Consumer Leader会根据 Coordinator给予的分区信息给出分区方案。
Coordinator 将该方案以 SyncGroup 的方式将该方案执行下去,通知各个消费者,这样就完成了一轮 重平衡了。
然后消费者重新以 JoinGroup 方式加入 Coordinator,并选出Consumer Leader。
当所有消费者加入 Coordinator,Consumer Leader会根据 Coordinator给予的分区信息给出分区方案。
Coordinator 将该方案以 SyncGroup 的方式将该方案执行下去,通知各个消费者,这样就完成了一轮 重平衡了。
RocketMQ
ActiveMQ
ActiveMQ:Apache出品,最早使用的消息队列产品,时间比较长了,最近版本更新比较缓慢。
RabbitMQ:erlang语言开发,支持很多的协议,非常重量级,更适合于企业级的开发。性能较好,但是不利于做二次开发和维护。
RocketMQ:阿里开源的消息中间件,纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点,分布式事务。
ZeroMQ:号称最快的消息队列系统,尤其针对大吞吐量的需求场景,采用 C 语言实现。
RabbitMQ:erlang语言开发,支持很多的协议,非常重量级,更适合于企业级的开发。性能较好,但是不利于做二次开发和维护。
RocketMQ:阿里开源的消息中间件,纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点,分布式事务。
ZeroMQ:号称最快的消息队列系统,尤其针对大吞吐量的需求场景,采用 C 语言实现。
消息队列的选型需要根据具体应用需求而定,ZeroMQ 小而美,RabbitMQ 大而稳,Kakfa 和 RocketMQ 快而强劲
搜索引擎
ElasticSearch
什么是倒排索引?
倒排索引为什么叫倒排索引
大数据
大数据时代
1. Volume(大量)
2. Velocity(高速)
3. Variety(多样)
4.Value(低价值密度)
2. Velocity(高速)
3. Variety(多样)
4.Value(低价值密度)
1. 高并发
2. 高可用
3. 高性能
2. 高可用
3. 高性能
收集、存储、建模、分析、变现
定位方向
大数据架构:偏基建与架构
1、架构理论:高并发、高可用、并行计算、MapReduce、spark。
2、数据流理论:Flume、Kafka、Spark stream。
3、数据存储:HDFS、Ceph。
4、软件应用:Hive、HBase、PrestoDB
5、可视化:D3、HightCharts等
6、每个层面做深入理解与落地。
2、数据流理论:Flume、Kafka、Spark stream。
3、数据存储:HDFS、Ceph。
4、软件应用:Hive、HBase、PrestoDB
5、可视化:D3、HightCharts等
6、每个层面做深入理解与落地。
大数据分析:偏建模跟分析
1、数据库应用:RDBMS、NoSQL、MySQL、Hive、Cassandra等(提取所需数据)
2、数据加工:ETL、Python
3、数据统计:统计、概率。
4、数据分析:建模,挖掘,ML,
5、业务知识:跟业务结合使用。
2、数据加工:ETL、Python
3、数据统计:统计、概率。
4、数据分析:建模,挖掘,ML,
5、业务知识:跟业务结合使用。
大数据开发:偏应用实现
更注重服务端开发,数据库开发,呈现与可视化,数据的加工跟用户功能落地与实现
1、数据库开发:RDBMS,NoSql,Hive
2、数据量工具开发:Flume、Kafka。
3、数据前端开发:HightCharts、D3、H5、JavaScriprt
4、数据获取开发:爬虫,分词,NLP。
1、数据库开发:RDBMS,NoSql,Hive
2、数据量工具开发:Flume、Kafka。
3、数据前端开发:HightCharts、D3、H5、JavaScriprt
4、数据获取开发:爬虫,分词,NLP。
分布式理论(重要)
1、CAP
一致性(Consistency)
一致性,是指对每个节点一个数据的更新,整个集群都知道更新,并且是一致的
可用性(Availability)
分布式系统一直处于可用状态,对于请求总是能在有限的时间内返回结果致性;
分区容错性(Partition Tolerance)
除非整个网络故障,分布式系统在任何网络或者单点故障时,仍能对外提供满足一致性和可用性的服务;
一般情况下P必须要,大部分时候都在AC之前抉择平衡
spring cloud eureka 保证 AP
zookeeper 保证 CP
2、Base 理论
我们说,CAP 不可能同时满足,而分区容错是对于分布式系统而言,是必须的。最后,我们说,如果系统能够同时实现 CAP 是再好不过的了,所以出现了 BASE 理论,今天就来讲讲 Base 理论。
BASE:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)
Base 理论是对 CAP 中一致性和可用性权衡的结果,既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
Basically Available(基本可用)
假设系统出现不可预知故障,但还是能用相比正常系统响应时间变大,部分功能上的降级。
Soft state(软状态)
要求多个节点的数据副本都是一致的,这是一种 硬状态。
软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
Eventually consistent(最终一致性)
软状态,然后不能一直是软状态,系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。
总结
总的来说,BASE 理论面向的是大型高可用可扩展的分布式系统,和传统事务的 ACID 是相反的,它完全不同于 ACID 的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间是不一致的。
3、一致性协议之 2PC
2PC(Two-Phase Commit)可认为是一种算法协议。目的是为保证分布式系统数据一致性。
为了系统可达CAP有了Base协议,但Base需在可用性跟一致性权衡,于是产生了一致性协议。
顾名思义,二阶段提交就是将事务的提交过程分成了两个阶段来进行处理
协调者:分布式系统多个节点需要个统一调度的节点来执行逻辑决定是否要把所有节点事务真正提交。
2PC 阶段一(投票)
1、事务询问:向所有参与者询问是否准备好执行事务
2、执行事务:各参与者节点执行事务操作,将undo跟redo信息写入事务日志。
3、参与者向协调者响应:执行成功返回ok,不成功返回no。
2、执行事务:各参与者节点执行事务操作,将undo跟redo信息写入事务日志。
3、参与者向协调者响应:执行成功返回ok,不成功返回no。
2PC 阶段二(执行)
执行事务提交
1、发送提交请求:协调者向所有参与者发出 commit 请求。
2、事务提交:参与者收到 commit 请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
3、反馈事务提交结果:参与者在完成事务提交之后,向协调者发送 Ack 信息。
4、协调者接收到所有参与者反馈的 Ack 信息后,完成事务。
2、事务提交:参与者收到 commit 请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
3、反馈事务提交结果:参与者在完成事务提交之后,向协调者发送 Ack 信息。
4、协调者接收到所有参与者反馈的 Ack 信息后,完成事务。
中断事务
1、发送回滚请求:协调者向所有参与者发出 Rollback 请求。
2、事务回滚:参与者接收到 Rollback 请求后,会利用其在阶段一种记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
3、反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送 Ack 信息。
4、中断事务:协调者接收到所有参与者反馈的 Ack 信息后,完成事务中断。
2、事务回滚:参与者接收到 Rollback 请求后,会利用其在阶段一种记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
3、反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送 Ack 信息。
4、中断事务:协调者接收到所有参与者反馈的 Ack 信息后,完成事务中断。
核心:对每个事务都采用先尝试后提交的处理方式,因此也可以将二阶段提交看成一个强一致性的算法。
优缺点
优点
原理简单,实现方便
缺点
1、同步阻塞:所有节点都在等待其他节点响应。
2、单点问题:在提交阶段如果协调者挂掉,整个流程无法运转,其他参与者一直处于锁定事务资源中
3、数据不一致:协调者发送commit给参与者,由于网络导致部分参与者没收到。
4、过于保守:二阶段提交协议没有设计较为完善的容错机制,任意一个节点是失败都会导致整个事务的失败。
2、单点问题:在提交阶段如果协调者挂掉,整个流程无法运转,其他参与者一直处于锁定事务资源中
3、数据不一致:协调者发送commit给参与者,由于网络导致部分参与者没收到。
4、过于保守:二阶段提交协议没有设计较为完善的容错机制,任意一个节点是失败都会导致整个事务的失败。
3、TCC
TCC解决分布式事物的思路是,一个大事务拆解成多个小事务。
2PC的一个变种,一个大事务拆解成多个小事务, Try 阶段去占库存,Confirm 阶段则实际扣库存,如果库存扣减失败 Cancel 阶段进行回滚,释放库存。
原本一个方法,现在却需要三个方法来支持,可以看到 TCC 对业务的侵入性很强,而且这种模式并不能很好地被复用,会导致开发量激增。还要考虑到网络波动等原因,为保证请求一定送达都会有重试机制,所以考虑到接口的幂等性。
科技哥:分布式事务
4、一致性协议之 3PC
3PC,全称 three phase commit,是 2PC 的改进版,其将 2PC 的 提交事务请求 过程一分为二。
三个阶段
1、CanCommit:协调者询问参与者是否可以执行事务,返回yes或no。
2、PreCommit:
执行事务预提交
1、发送预提交请求:协调者向所有参与者发送PreCommit请求,并进入prepared状态。
2、事务预提交:参与者收到 preCommit 请求后,会执行事务操作,对应 2PC 中的 执行,也会 Undo 和 Redo 信息记录到事务日志中。
3、参与者返回结果:如果参与者成功执行了事务,就反馈 Ack 响应,同时等待指令:提交(commit) 或终止(abor)。
2、事务预提交:参与者收到 preCommit 请求后,会执行事务操作,对应 2PC 中的 执行,也会 Undo 和 Redo 信息记录到事务日志中。
3、参与者返回结果:如果参与者成功执行了事务,就反馈 Ack 响应,同时等待指令:提交(commit) 或终止(abor)。
中断事务
1、发送中断请求:协调者向所有参与者节点发出 abort 请求 。
2、中断事务:参与者如果收到 abort 请求或者超时了,都会中断事务。
2、中断事务:参与者如果收到 abort 请求或者超时了,都会中断事务。
3、do Commit
执行提交
1、发送提交请求:协调者给参与者发doCommit请求。
2、事务提交:参与者收到doCommit请求进行提交。
3、反馈事务提交结果:参与者完成事务提交给协调者发送ACK信息
4、完成事务:协调者收到ACK完成事务。
2、事务提交:参与者收到doCommit请求进行提交。
3、反馈事务提交结果:参与者完成事务提交给协调者发送ACK信息
4、完成事务:协调者收到ACK完成事务。
中断事务
1、协调者有收到no响应或者超时,执行中断事务。
2、发送中断请求
3、事务回滚。
4、反馈事务回滚信息
5、最终中断事务。
2、发送中断请求
3、事务回滚。
4、反馈事务回滚信息
5、最终中断事务。
此阶段如果 协调者出现问题 或者 协调者跟参与者网络故障 都会导致参与者无法收到 doCommit 请求或者 abort 请求。此时参与者超时等待后继续提交事务。
优缺点
优点:相比较 2PC,最大的优点是减少了参与者的阻塞范围(第一个阶段是不阻塞的),并且能够在单点故障后继续达成一致(2PC 在提交阶段会出现此问题,而 3PC 会根据协调者的状态进行回滚或者提交)。
缺点:如果参与者收到了 preCommit 消息后,出现了网络分区,那么参与者等待超时后,都会进行事务的提交,这必然会出现事务不一致的问题。
5、一致性算法 Paxos
Paxos 的目标:保证最终有一个提案会被选定,当提案被选定后,其他议员最终也能获取到被选定的提案。
Paxos 协议用来解决的问题可以用一句话来简化: 将所有节点都写入同一个值,且被写入后不再更改。
Paxos 协议用来解决的问题可以用一句话来简化: 将所有节点都写入同一个值,且被写入后不再更改。
1、paxos的核心就是少数服从多数。一个提议必须被大多数人同意才能生效。
2、协议要求:如果接收者没有收到过提案编号,他必须接受第一个提案编号
3、提交的协议是有序号的,并且各种道道很多,建议看demo解释
2、协议要求:如果接收者没有收到过提案编号,他必须接受第一个提案编号
3、提交的协议是有序号的,并且各种道道很多,建议看demo解释
6、Raft 算法
目标就是成为一个易于理解的一致性算法。以替代 Paxos 的晦涩难懂。
任期
领导者心跳信息
随机选举超时时间
先来先服务的投票原则
大多数选票原则
领导者心跳信息
随机选举超时时间
先来先服务的投票原则
大多数选票原则
组成
领导选举
日志复制
挺好玩的在线demo
小灰通俗说 Raft
7、一致性协议之 ZAB
参考ZooKeeper中讲解
8、 Consistent Hash(一致性哈希算法)
主要是对IP进行hashcode的求值,然后可能涉及到虚拟节点来均衡映射
9、漫画拜占庭将军问题
计算机网络中所存在的一致性问题。如何解决呢? 存在某个节点故意传播坏消息的情况下
解决一致性的一种方法比如 Raft算法 或者 Paxos算法 ZAB算法等
10、悟空说理论
数据中台概述
ZooKeeper
是一个为分布式应用提供高效且可靠的分布式协调服务
ZK 定位 几乎是大数据体系的整体管理员
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目
Zookeeper = 文件系统 + 通知机制
Zk特性
1、简单数据存储结构:共享的树形结构,存储于内存。
2、可以构建集群:避免单点故障,超过半数即可工作。
3、顺序访问:对于读请求zk分配ZXID保证递增性,实现高级协调服务。
4、高性能:基于内存操作 3台QPS可达13W+。
2、可以构建集群:避免单点故障,超过半数即可工作。
3、顺序访问:对于读请求zk分配ZXID保证递增性,实现高级协调服务。
4、高性能:基于内存操作 3台QPS可达13W+。
zk特点(重点)
1、Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群。
2、集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
3、全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
4、更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
5、数据更新原子性,一次数据更新要么成功,要么失败。
6、实时性,在一定时间范围内,Client能读到最新数据。
7、满足的CAP中的CP原则(一致性跟分区容错性),如果出现master挂掉则选举时候无法提供对外服务。
2、集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
3、全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
4、更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
5、数据更新原子性,一次数据更新要么成功,要么失败。
6、实时性,在一定时间范围内,Client能读到最新数据。
7、满足的CAP中的CP原则(一致性跟分区容错性),如果出现master挂掉则选举时候无法提供对外服务。
zk数据结构
与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个ZNode。每一个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。
应用场景
统一配置管理
分布式系统配置文件写入Znode,一旦Znode修改通知各个客户端服务器。
统一集群管理
分布式系统实时掌握大数据集群节点状态。Hadoop、Kafka等。
服务器节点动态上下线
启动服务后注册到zk集群,服务出现任何变化客户端可实时感知到。
软负载均衡
zk中记录集群中节点访问次数,最少访问服务器处理最新请求。
统一命名服务
分布式环境对应用服务统一命名,便于识别。
选举机制(重点)
1、半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
2、在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
2、在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
选举流程知识点
选举demo
节点类型
持久(Persistend):客户端跟服务器断开连接后创建的节点不删除。
持久顺序(Persistend Sequential):客户端跟服务器断开连接后创建的节点不删除,且节点名带自增后缀。
短暂(Ephemeral):客户端跟服务器断开连接后创建的节点删除。
短暂(Ephemeral Sequential):客户端跟服务器断开连接后创建的节点删除,且节点名带自增后缀。
创建语句:create [-s 顺序节点] [-e 临时节点] path路径 data数据 [acl权限]
其他的增删改查节点操作 百度命令即可
监听原理
监听节点数据的变化 get path [watch]
监听子节点增减的变化 ls path [watch]
监听子节点增减的变化 ls path [watch]
写原理
Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议。
ZAB( Zookeeper Atomic Broadcast) 原子广播协议
写过程
1、所有客户端写入数据都是写入到 主进程(称为 Leader)中,然后,由 Leader 复制到备份进程(称为 Follower)中。从而保证数据一致性。
2、复制过程类似 2PC,ZAB 只需要 Follower 有一半以上返回 Ack 信息就可以执行提交,大大减小了同步阻塞。也提高了可用性。
消息广播
1. 将数据都复制到 Follwer 中
2. 等待 Follwer 回应 Ack,最低超过半数即成功
3. 当超过半数成功回应,则执行 commit操作(先提交自己,再发送 commit 给所有 Follwer)
2. 等待 Follwer 回应 Ack,最低超过半数即成功
3. 当超过半数成功回应,则执行 commit操作(先提交自己,再发送 commit 给所有 Follwer)
- Leader 在收到客户端请求之后,会将这个请求封装成一个事务,并给这个事务分配一个全局递增的唯一 ID,称为事务ID(ZXID),ZAB 协议需要保证事务的顺序,因此必须将每一个事务按照 ZXID 进行先后排序然后处理。
- 在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,解除同步阻塞。
- zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是 Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。
- 实际上,这是一种简化版本的 2PC,不能解决单点问题。等会我们会讲述 ZAB 如何解决单点问题(即 Leader 崩溃问题)。
- 在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,解除同步阻塞。
- zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是 Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。
- 实际上,这是一种简化版本的 2PC,不能解决单点问题。等会我们会讲述 ZAB 如何解决单点问题(即 Leader 崩溃问题)。
ZXID
zxid是事务编号,是64位的。可以把他拆分为两部分,分别都为32位。
低32位
可以看作是一个简单的递增的计数器,针对客户端的每一个事务请求,Leader 都会产生一个新的事务 Proposal 并对该计数器进行 + 1 操作。代表了每代 Leader 中事务的唯一性。
高32位
是leader周期epoch。 这里可能有点难理解,你可以把他理解为年代,每个leader都有一个年代。在leader所属的年代,该leader拥有最高权力。如果该leader挂了,那么就会换下一个leader,此时年代也应该+1.
会有这种情况,如果上一个年代的leader复活了,之前的leader可能由于网络原因,现在好了。这可能会出现脑裂。 但上一个leader所属的时代已过去了,那他就会成为folower。
其实这样也是为了一致性,如果上一个leader还有权力,那么脑裂的时间会更长。还有,上一个leader,可能数据是不一致的,他会成为follower之后进行回退的操作,直到一个被集群中过半机器提交的最新事务。
会有这种情况,如果上一个年代的leader复活了,之前的leader可能由于网络原因,现在好了。这可能会出现脑裂。 但上一个leader所属的时代已过去了,那他就会成为folower。
其实这样也是为了一致性,如果上一个leader还有权力,那么脑裂的时间会更长。还有,上一个leader,可能数据是不一致的,他会成为follower之后进行回退的操作,直到一个被集群中过半机器提交的最新事务。
崩溃恢复
ZAB 定义了2 个原则
1、ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
2、ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
2、ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
数据同步
当崩溃恢复之后,需要再正式工作前确认事务都已经搞定来保证数据一致。Leader处理或丢弃事务依赖ZXID。
总结
1、ZAB 协议和我们之前看的 Raft 协议实际上是有相似之处的,比如都有一个 Leader,用来保证一致性(Paxos 并没有使用 Leader 机制保证一致性)。再有采取过半即成功的机制保证服务可用(实际上 Paxos 和 Raft 都是这么做的)。
2、ZAB 让整个 Zookeeper 集群在两个模式之间转换,消息广播和崩溃恢复,消息广播可以说是一个简化版本的 2PC,通过崩溃恢复解决了 2PC 的单点问题,通过队列解决了 2PC 的同步阻塞问题。
3、支持崩溃恢复后数据准确性的就是数据同步了,数据同步基于事务的 ZXID 的唯一性来保证。通过 + 1 操作可以辨别事务的先后顺序。
2、ZAB 让整个 Zookeeper 集群在两个模式之间转换,消息广播和崩溃恢复,消息广播可以说是一个简化版本的 2PC,通过崩溃恢复解决了 2PC 的单点问题,通过队列解决了 2PC 的同步阻塞问题。
3、支持崩溃恢复后数据准确性的就是数据同步了,数据同步基于事务的 ZXID 的唯一性来保证。通过 + 1 操作可以辨别事务的先后顺序。
ZK实战
1、服务注册跟发现
服务启动时候往zk中注册下
调用该服务的从zk中获取指定目录,然后监控数据变化,实时更改可用服务池实现 服务的注册跟发现
2、分布式锁
核心思想:在zk中有一个唯一的临时节点,只有拿到节点的才可以操作数据,没拿到就需要等待。
第一种实现
基于同名节点的分布式锁
缺点:可能引发羊群效应,第一个用完后瞬间有999个同时并发的向zk请求获得锁。
第二种实现
高性能分布式锁
思想:很简单,每个要操作的不要乱抢了,在zk中进行排队拿号,轮到了再操作数据。这样可以有效避免并发
相比来说 Redis的分布式锁性能更加好一些,完全基于内存不涉及节点的创建跟删除
3、集群选举
思想几乎跟负载均衡一样
4、配置中心
比如mybatis通过URL访问mysql,我们通过动态的修改URL实现从访问A数据库到访问B数据库的切换。核心就监听 dataChange。
zk客户端操作
1. zk原生态API
zk客户端是异步的哦!需要引入CountDownLatch 来确保连接好了再做下面操作。zk 原生api是不支持迭代式的创建跟删除路径的
缺点
1. 会话的连接是异步的;必须用到回调函数
2. Watch需要重复注册: 看一次watch注册一次
3. Session重连机制:有时session断开还需要重连接。
4. 开发复杂性较高:开发相对来说比较琐碎
2. Watch需要重复注册: 看一次watch注册一次
3. Session重连机制:有时session断开还需要重连接。
4. 开发复杂性较高:开发相对来说比较琐碎
2、zkClient
优化一、在session loss和session expire时自动创建新的ZooKeeper实例进行重连。
优化二、将一次性watcher包装为持久watcher
3、Curator
是Netflix公司开源的一套Zookeeper客户端框架。了解过Zookeeper原生API都会清楚其复杂度。Curator帮助我们在其基础上进行封装、实现一些开发细节,包括接连重连、反复注册Watcher和NodeExistsException等。目前已经作为Apache的顶级项目出现,是最流行的Zookeeper客户端之一
ZK常见面试题
ZK注意事项
Zk数据与日志清理:dataDir目录下面
默认最大连接数 默认为60,配置maxClientCnxns参数,配置单个客户端机器创建的最大连接数;
集群中机器的数量并不是越多越好,一个写操作需要半数以上的节点ack,所以集群节点数越多,整个集群可以抗挂点的节点数越多(越可靠),但是吞吐量越差。集群的数量必须为奇数;
zk是基于内存进行读写操作的,有时候会进行消息广播,因此不建议在节点存取容量比较大的数据;
可视化工具:ZooInspector
Hadoop
重要知识点概述
大数据部门组织
1、平台组:平台环境搭建、集群性能监控及调优。
2、数仓组:ETL工程师、数仓建模
3、数据挖掘组:算法、推荐、用户画像
4、报表开发组:Java开发工程师
2、数仓组:ETL工程师、数仓建模
3、数据挖掘组:算法、推荐、用户画像
4、报表开发组:Java开发工程师
Hadoop版本
1、Apache版:原始版
2、Cloudera版:企业用的较多 CDH版(解决了版本兼容问题)
3、Hortonworks版:文档较好,没咋见过。
2、Cloudera版:企业用的较多 CDH版(解决了版本兼容问题)
3、Hortonworks版:文档较好,没咋见过。
Hadoop 部署方式
本地模式
伪分布模式
集群模式
伪分布模式
集群模式
集群的规划
同步脚本编写
ssh之间 公钥 私钥的交换
各种细节思维导图
https://github.com/SoWhat1412/xmindfile
HDFS
Google 论文 及GFS延伸出了HDFS(Distributed File System)
特性
分布式文件系统,适合一次写入多次读出场景,不支持修改。
优点
1、高可用:一个数据有多个复本
2、高扩展:集群节点动态增删
3、高性能:MapReduce并行思想
3、高容错:job失败自动重试
2、高扩展:集群节点动态增删
3、高性能:MapReduce并行思想
3、高容错:job失败自动重试
缺点
1、不适合低延时访问
2、无法高效处理大量小文件
3、无法支撑并发写入跟文件修改,只支持追加。
2、无法高效处理大量小文件
3、无法支撑并发写入跟文件修改,只支持追加。
Hadoop 若干Shell 指令
增删改查 基本跟shell指令类似
几个重要组件
NameNode:就是Master,它是一个主管、管理者。
DataNode:就是Slave,NameNode下达命令,DataNode执行实际的操作。
Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。需配置HA才可实现
NN 跟SNN工作机制
重点
HDFS 中的写请求
HDFS中的读请求
机架感知
数据分配
集群安全模式的进入跟退出
数据完整性校验crc
节点的增加与退役
Hadoop中的HA
MapReduce
是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上。
优点
1、易于编程:完全跟八股文编程方式框架。
2、良好扩展性:计算力不足可叠加机器
3、高容错性:单节点任务失败会自动重试。
4、适合大量离线数据计算。
2、良好扩展性:计算力不足可叠加机器
3、高容错性:单节点任务失败会自动重试。
4、适合大量离线数据计算。
缺点
1、不用于实时计算: 无法像MySQL一样
2、不用于流式计算:流式计算数据动态输入的而MapReduce数据源是固态的。
3、不用于DAG计算:频繁性磁盘IO所以也不适合。
2、不用于流式计算:流式计算数据动态输入的而MapReduce数据源是固态的。
3、不用于DAG计算:频繁性磁盘IO所以也不适合。
核心思想
1)分布式的运算程序往往需要分成至少2个阶段。
2)第一个阶段的maptask并发实例,完全并行运行,互不相干。
3)第二个阶段的reduce task并发实例互不相干,但是他们的数据依赖于上一个阶段的所有maptask并发实例的输出。
4)MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个mapreduce程序,串行运行。
2)第一个阶段的maptask并发实例,完全并行运行,互不相干。
3)第二个阶段的reduce task并发实例互不相干,但是他们的数据依赖于上一个阶段的所有maptask并发实例的输出。
4)MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个mapreduce程序,串行运行。
hadoop独特序列化:紧凑、快速、可扩展、互操作。
MapTask
并行度由一个文件切片个数决定
先积攒数据到缓冲区进行快排,文件数据的溢出跟归并排序形成大文件
在不影响结果情况下 可选择性 加入 Combiner合并 局部汇总
Partition分区 规定最终结果文件数 默认是1个
ReduceTask工作机制
任务数默认是1,可以自定义实现
1、copy阶段:reducetask从maptask拷贝结果数据。
2、merge阶段:为防止内存跟磁盘使用过多,会对文件进行merge。
3、sort阶段:对最终数据进行归并排序
4、reduce阶段;结果最终写入HDFS中。
2、merge阶段:为防止内存跟磁盘使用过多,会对文件进行merge。
3、sort阶段:对最终数据进行归并排序
4、reduce阶段;结果最终写入HDFS中。
MapReduce涉及到的排序
MapReduce都会对数据排序 这是默认操作
map阶段涉及草缓存区的快排 跟溢出区若干小文件的归并排序
reduce阶段根据文件大小会选择性归并排序,
hadoop涉及到一些不同格式文件压缩 来简化io跟磁盘空间
注意点总结
Join 用法
reduce join
思想:主要是 key-Bean的模式,在Reduce端 进行join
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签(tag), 比如:tag=0表示来自文件File1,tag=2表示来自文件File2。即:map阶段的主要任务是对不同文件中的数据打标签。
在reduce阶段,reduce函数获取key相同的来自File1和File2文件的value list, 然后对于同一个key,对File1和File2中的数据进行join(笛卡尔乘积),即:reduce阶段进行实际的连接操作。
map join
思想:在Map端进行数据的合并工作
使用场景:一张表十分小、一张表很大
Yarn
Yarn是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台,而mapreduce等运算程序则相当于运行于操作系统之上的应用程序。在Hadoop2.* 版本中出现
组成
ResourceManager
(1)处理客户端请求
(2)启动或监控ApplicationMaster
(3)监控NodeManager
(4)资源的分配与调度
(2)启动或监控ApplicationMaster
(3)监控NodeManager
(4)资源的分配与调度
ApplicationMaster(AM)
ApplicationMaster管理在YARN内运行的每个应用程序实例。ApplicationMaster负责协调来自ResourceManager的资源,并通过NodeManager监视容器的执行和资源使用(CPU、内存等的资源分配)
(1)负责数据的切分
(2)为应用程序申请资源并分配给内部的任务
(3)任务的监控与容错
(1)负责数据的切分
(2)为应用程序申请资源并分配给内部的任务
(3)任务的监控与容错
YARN工作机制
NodeManager(NM)
NodeManager管理YARN集群中的每个节点。NodeManager提供针对集群中每个节点的服务,从监督对一个容器的终生管理到监视资源和跟踪节点健康,NodeManager管理抽象容器
(1)管理单个节点上的资源
(2)处理来自ResourceManager的命令
(3)处理来自ApplicationMaster的命令
(1)管理单个节点上的资源
(2)处理来自ResourceManager的命令
(3)处理来自ApplicationMaster的命令
Container
Container是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等,当AM向RM申请资源时,RM为AM返回的资源便是用Container表示的。YARN会为每个任务分配一个Container,且该任务只能使用该Container中描述的资源。
作用:对任务运行环境进行抽象,封装CPU、内存等多维度的资源以及环境变量、启动命令等任务运行相关的信息
作用:对任务运行环境进行抽象,封装CPU、内存等多维度的资源以及环境变量、启动命令等任务运行相关的信息
作业调度平台
FIFO
Capacity Scheduler (Hadoop2.7.2默认 容量调度器)
Fair Scheduler(公平调度器)
推测执行
当大部分任务完成,只有一个任务没完成时候会选择性的多跑一个改任务复本 实现高可用
HDFS HA
HDFS HA功能通过配置Active/Standby两个nameNodes实现在集群中对NameNode的热备来解决上述问题。如果出现故障,如机器崩溃或机器需要升级维护,这时可通过此种方式将NameNode很快的切换到另外一台机器。
YARN HA
原理跟 HDFS HA类似
Hadoop调优
输入
(1)合并小文件
(2)采用ConbinFileInputFormat来作为输入
(2)采用ConbinFileInputFormat来作为输入
map 阶段
1)减少spill次数
2)减少merge次数
3)在 map 之后先进行combine处理
2)减少merge次数
3)在 map 之后先进行combine处理
Reduce阶段
1、合理设置map跟reduce个数
2、设置map跟reduce共存
3、合理设置reduce端buffer大小
2、设置map跟reduce共存
3、合理设置reduce端buffer大小
IO阶段
合理使用序列化跟压缩
数据倾斜问题
1、通过抽样设置预分区值
2、自定义分区
3、Combine
4、用Map join 避免Reduce join
2、自定义分区
3、Combine
4、用Map join 避免Reduce join
HDFS小文件
1、Hadoop Archive 对小文件打包
2、Sequence file KV存储文件
3、CombineFileInputFormat
4、开启JVM重用
2、Sequence file KV存储文件
3、CombineFileInputFormat
4、开启JVM重用
MapReduce任务慢
(1)计算机性能:CPU、内存、磁盘健康、网络
(2)I/O 操作优化
(1)数据倾斜
(2)map和reduce数设置不合理
(3)reduce等待过久 shuffle太久
(4)小文件过多
(5)大量的不可分块的超大文件
(6)spill次数过多 环形缓冲区
(7)merge次数过多等。
(2)I/O 操作优化
(1)数据倾斜
(2)map和reduce数设置不合理
(3)reduce等待过久 shuffle太久
(4)小文件过多
(5)大量的不可分块的超大文件
(6)spill次数过多 环形缓冲区
(7)merge次数过多等。
Hive(6W字Hive讲义)
Hive:Facebook开源的一款数仓工具,将基于MapReduce的OLAP任务封装成类SQL查询服务。本质是将HQL转化成MapReduce程序
特点
Hive处理的数据存储在HDFS
Hive分析数据底层的实现是MapReduce
执行程序运行在Yarn上
Hive分析数据底层的实现是MapReduce
执行程序运行在Yarn上
优点
操作接口采用类SQL语法,提供快速开发的能力(简单、容易上手)。
避免了去写MapReduce,减少开发人员的学习成本。
Hive的执行延迟比较高,因此Hive常用于数据分析,对实时性要求不高的场合。
Hive优势在于处理大数据,对于处理小数据没有优势,因为Hive的执行延迟比较高。
Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数
避免了去写MapReduce,减少开发人员的学习成本。
Hive的执行延迟比较高,因此Hive常用于数据分析,对实时性要求不高的场合。
Hive优势在于处理大数据,对于处理小数据没有优势,因为Hive的执行延迟比较高。
Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数
缺点
Hive的HQL表达能力有限
迭代式算法无法表达
数据挖掘方面不擅长
Hive的效率比较低
Hive自动生成的MapReduce作业,通常情况下不够智能化
Hive调优比较困难,粒度较粗
迭代式算法无法表达
数据挖掘方面不擅长
Hive的效率比较低
Hive自动生成的MapReduce作业,通常情况下不够智能化
Hive调优比较困难,粒度较粗
Hive和数据库比较
数据库可以用在 Online 的应用中,Hive 是为数据仓库而设计的
查询语言、数据存储位置、数据更新、索引、执行延时、可扩展性、数据规模、
Hive中元数据是啥
本质上只是用来存储hive中有哪些数据库,哪些表,表的模式,目录,分区,索引以及命名空间。为数据库创建的目录一般在hive数据仓库目录下。
一个真实操作的大致流程
hive 集群的搭建
百度即可,注意依赖与HDFS跟YARN 需确保两者开启,MySQL也要开启配置哦
1、hive的log日志需配置下,不然在/opt目录下
2、hive的参数配置,配置文件设置,命令行设置,参数声明方式set
数据类型:基本上跟Java中基本类型一样,多了ARRAY、MAP 和 STRUCT
重点是注意 行列的分隔符如何设置 数据的存储方式
DDL 跟DML
DDL
语句demo
内部表跟外部表
内部表
默认创建的表都是所谓的管理表,有时也被称为内部表,删除表后数据也删除了
desc
Table Type: MANAGED_TABLE
formatted
student2; Table Type: MANAGED_TABLE
外部表(external )
因为表是外部表,所以Hive并非认为其完全拥有这份数据。删除该表并不会删除掉这份数据,不过描述表的元数据信息会被删除掉。
hive (default)> desc formatted dept;
Table Type: EXTERNAL_TABLE
Table Type: EXTERNAL_TABLE
partitioned by 分区
create table dept_partition(
deptno int, dname string, loc string
)
partitioned by (month string)
row format delimited fields terminated by ‘\t’;
deptno int, dname string, loc string
)
partitioned by (month string)
row format delimited fields terminated by ‘\t’;
在数仓中用的非常之多,一般都是按照日期进行切片的,分区针对的是数据的存储路径(多加几个文件夹)
clustered 分桶
分桶针对的是数据文件(同一个文件夹内将文件分成多份),插入数据需要若干设置才可完成
create table stu_buck(id int, name string)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
分桶抽样
stored as
row format
row format
DML
加载数据
创建
create table student(id string, name string) row format delimited fields terminated by ‘\t’;
加载本地数据
load data local inpath ‘/opt/module/datas/student.txt’ into table default.student;
加载HDFS数据
load data inpath ‘/user/atguigu/hive/student.txt’ into table default.student;
数据导出
本地文件
insert overwrite local directory ‘/opt/module/datas/export/student’ select * from student;
磁盘文件
insert overwrite directory ‘/user/atguigu/student2’ ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’ select * from student;
各种基本算子 max min avg 等
各种join 注意on只支持等值条件
select nvl(comm,-1) from emp; 空值赋值
排序
Order By:全局排序,一个Reducer
Sort By:每个Reducer内部进行排序,对全局结果集来说不是排序。
set mapreduce.job.reduces=3; 可以设置reduce个数
Distribute By(分区排序):类似MR 中 partition,进行分区,结合sort by使用。
注意:Hive要求Distribute By语句要写在sort by语句之前。
对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
hive (default)> set mapreduce.job.reduces=3;
hive (default)> insert overwrite local directory ‘/opt/module/datas/distribute-result’ select * from emp distribute by deptno sort by empno desc;
hive (default)> insert overwrite local directory ‘/opt/module/datas/distribute-result’ select * from emp distribute by deptno sort by empno desc;
Cluster By:当distribute by 和 sorts by 字段相同时,可以使用cluster by方式。
cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。
hive (default)> select * from emp cluster by deptno;
hive (default)> select * from emp distribute by deptno sort by deptno;
上面等价
hive (default)> select * from emp distribute by deptno sort by deptno;
上面等价
总结
case when 注意对应性
列转行
CONCAT(string A/col, string B/col…):
返回输入字符串连接后的结果,支持任意个输入字符串;
CONCAT_WS(separator, str1, str2,…):
特殊形式的 CONCAT()。第一个参数剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串,分隔符不可一世null
COLLECT_SET(col):
函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型字段。多行汇总成一个array类型。
行转列
EXPLODE(col):将hive一列中复杂的array或者map结构拆分成多行。
LATERAL VIEW:用于和split, explode等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。
demo
窗口函数(重点)
需求:既显示聚集前的数据,又要显示聚集后的数据,这时我们便引入了窗口函数.
注意:在SQL处理中,窗口函数都是最后一步执行,而且仅位于Order by字句之前.
OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化,用在over函数里面
CURRENT ROW:当前行
n PRECEDING:往前 n 行数据
n FOLLOWING:往后 n 行数据
UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
下面写over 前面
LAG(col,n):往前第 n 行数据
LEAD(col,n):往后第 n 行数据
NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE 返回此行所属的组的编号。注意:n必须为int类型。
CURRENT ROW:当前行
n PRECEDING:往前 n 行数据
n FOLLOWING:往后 n 行数据
UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
下面写over 前面
LAG(col,n):往前第 n 行数据
LEAD(col,n):往后第 n 行数据
NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE 返回此行所属的组的编号。注意:n必须为int类型。
Rank
rank():排序相同时会重复,总数不会变
dense_rank():排序相同时会重复,总数会减少
row_number():会根据顺序计算
dense_rank():排序相同时会重复,总数会减少
row_number():会根据顺序计算
select name,subject,score,rank() over(partition by subject order by score desc) rp,
dense_rank() over(partition by subject order by score desc) drp,row_number() over(partition by subject order by score desc) rmp
from score;
dense_rank() over(partition by subject order by score desc) drp,row_number() over(partition by subject order by score desc) rmp
from score;
函数
系统自带函数
1、查看系统自带的函数 :show functions
2、详细显示自带函数用法:desc function extended upper;
2、详细显示自带函数用法:desc function extended upper;
自定义函数
1、UDF(User-Defined-Function):一进一出 类似upper,trim
2、UDAF(User-Defined Aggregation Function): 聚集函数,多进一出 类似count/max/min
3、UDTF(User-Defined Table-Generating Functions): 一进多出 比如 lateral view explore()
2、UDAF(User-Defined Aggregation Function): 聚集函数,多进一出 类似count/max/min
3、UDTF(User-Defined Table-Generating Functions): 一进多出 比如 lateral view explore()
压缩跟存储
压缩
可尝试开启map阶段压缩跟reduce阶段的压缩来提速
存储
Hive支持的存储数的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET。
textfile
和sequencefile
的存储格式都是基于行
存储的;orc
和parquet
是基于列
式存储的。 存储文件的压缩比总结:ORC > Parquet > textFile
存储文件的查询速度总结:查询速度相近。
在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy,lzo。
存储文件的查询速度总结:查询速度相近。
在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy,lzo。
行存储的特点
查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点
因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
调优
Fetch抓取= more 在全局查找、字段查找、limit查找等都不走mapreduce。直接拿数据展示
开启本地模式,小的MapReduce任务直接在本机执行
小表在左 join 大表
开启MapJoin 后可避免数据倾斜,默认是Common Join (在Reduce阶段join,容易发生数据轻泄)
Group By 开启2阶段MapReduce任务
Count(Distinct) 去重统计,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换
笛卡尔积:尽量避免笛卡尔积,join的时候不加on条件,或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。
行列过滤
列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤
select o.id from bigtable b join ori o on o.id = b.id where o.id <= 10;
select b.id from bigtable b join (select id from ori where id <= 10 ) o on b.id = o.id;
select b.id from bigtable b join (select id from ori where id <= 10 ) o on b.id = o.id;
动态分区(Dynamic Partition)
对分区表Insert数据时候,数据库自动会根据分区字段的值,将数据插入到相应的分区中,不过需要根据实际工作设置若干参数,比如分区最大值,HDFS最大文件数
数据倾斜
通常情况下,作业会通过input的目录产生一个或者多个map任务
·主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。
reduce优化
是不是map数越多越好
答案是否定的。如果一个任务有很多小文件( << 128M),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
是不是保证每个map处理接近128m的文件块,就高枕无忧了
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。
针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
小文件进行合并:在map执行前合并小文件,减少map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)
复杂文件增加Map数 :computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize))) blocksize=128M公式,调整maxSize最大值。让maxSize最大值低于blocksize就可以增加map的个数。
map优化
处理大数据量利用合适的reduce数;
使单个reduce任务处理数据量大小要合适;
使单个reduce任务处理数据量大小要合适;
过多的启动和初始化reduce也会消耗时间和资源;
reduce任务太多会产生很多小结果文件
reduce任务太多会产生很多小结果文件
并行执行
Hive会将一个查询转化成一个或者多个阶段,默认情况下,Hive一次只会执行一个阶段。
尝试开启并行执行,没有关联的阶段并行执行
严格模式 strict
hive提供了严格模式可以防止用户执行那些可能意想不到的不好的影响的查询
1、对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。
2、对于使用了order by语句的查询,要求必须使用limit语句。因为排序过程会将所有的结果数据分发到同一个Reducer中进行处理,limit可提速
3、限制笛卡尔积的查询。
JVM重用
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响
推测执行
Hadoop采用了推测执行(Speculative Execution)机制,根据一定法则推测出拖后腿的任务,并为这样的任务启动个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
开启 MapReduce阶段压缩
执行计划(Explain)
EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query
Dbeaver 客户端可视化工具
Hue 页面化操作
Hive调优
数仓
Hive 执行底层
HBase
什么是HBase
HBase是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群。
就是一款NoSQL数据库,面向列存储,用于存储处理海量数据。
HBase并不是足够快,而是数据量很大的时候它慢的不明显
单表数据量超过千万,而且并发量很大;
数据分析需求较弱,或者不需要那么实时灵活。
数据分析需求较弱,或者不需要那么实时灵活。
为什么会有HBase
传统关系型数据库 分表分库的概念存储 及行式存储不足
HBase 特点
海量存储、列式存储(列族)、极易扩展、高并发IO、稀疏(针对灵活列)
1、Table(表):一个表格由若干列族组成
2、Column Family(列族):创建表时候需指定列族,列是动态的。HBase会尽可能把相同列族放在同一台机器。
3、Row(行):HBase是一个面向列存储的数据库,一个行中的数据可以分布在不同的服务器上。
4、RowKey(行键):类似MySQL主键,rowkey按照字典排序。一个rowkey对应的是一行数据,用户不指定则随机生成。
5、Region:就是若干行数据的集合,类似MySQL中的水平切分结果。Region基于HDFS存储的。
6、RegionServer:就是存放Region的容器,直观上说就是服务器上的一个服务。负责管理维护Region。
2、Column Family(列族):创建表时候需指定列族,列是动态的。HBase会尽可能把相同列族放在同一台机器。
3、Row(行):HBase是一个面向列存储的数据库,一个行中的数据可以分布在不同的服务器上。
4、RowKey(行键):类似MySQL主键,rowkey按照字典排序。一个rowkey对应的是一行数据,用户不指定则随机生成。
5、Region:就是若干行数据的集合,类似MySQL中的水平切分结果。Region基于HDFS存储的。
6、RegionServer:就是存放Region的容器,直观上说就是服务器上的一个服务。负责管理维护Region。
逻辑存储
物理存储
1、NameSpace:类似MySQL中的DataBase,
2、Row:一行数据由一个 RowKey 和多个 Column(列)组成,查询数据时只能根据 RowKey 进行检索
3、Column :每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定。
4、TimeStamp:时间戳(写入 HBase 的时间),用于标识数据的不同版本(version),因为HBase是基于HDFS的而HDFS是可以增删查而不支持改的。
5、Cell:单元格,由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存储
2、Row:一行数据由一个 RowKey 和多个 Column(列)组成,查询数据时只能根据 RowKey 进行检索
3、Column :每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定。
4、TimeStamp:时间戳(写入 HBase 的时间),用于标识数据的不同版本(version),因为HBase是基于HDFS的而HDFS是可以增删查而不支持改的。
5、Cell:单元格,由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存储
HBase架构解析
1、Master:负责启动的时候分配Region到具体的RegionServer,数据的读写操作与master无关,不过涉及到DDL跟region的分割合并要用到。
2、RegionServer:在它上边有多个region。我们读写的数据就存储在Region中。
3、Region:它是表拆分出来的一部分,HBase是一个会自动切片的数据库。当数据库过高时,就会进行拆分。
4、HBase的数据存储是基于HDFS的,它是真正承载数据的载体。
5、Zookeeper:在集群中负责存储hbase:meata的位置存储信息,客户端在写数据时需要先读取元数据信息。
2、RegionServer:在它上边有多个region。我们读写的数据就存储在Region中。
3、Region:它是表拆分出来的一部分,HBase是一个会自动切片的数据库。当数据库过高时,就会进行拆分。
4、HBase的数据存储是基于HDFS的,它是真正承载数据的载体。
5、Zookeeper:在集群中负责存储hbase:meata的位置存储信息,客户端在写数据时需要先读取元数据信息。
宏观图
RegionServer详细说
1、WAL(Write-Ahead Log)预写入日志:跟HDFS中的edits文件类似,写入的数据先写到此处,再写到基于内存实现的MemStore中。定期刷写到HFile中。
2、多个Region:它就是数据库的一部分,每一个Region都有起始的rowkey和结束的rowkey,代表了它存储的row的范围。
3、一个Region包含多个Store:一个Store就是对应一个列族的数据,Store是由MemStore和HFile组成的。
2、多个Region:它就是数据库的一部分,每一个Region都有起始的rowkey和结束的rowkey,代表了它存储的row的范围。
3、一个Region包含多个Store:一个Store就是对应一个列族的数据,Store是由MemStore和HFile组成的。
Region细节
Store 细节
1、MemStore:每个Store都有一个MemStore实例。数据写入到WAL之后就会被放入MemStore中。MemStore是内存的存储对象,只有到达一定的时机才会被刷写到HFile中去。
2、HFile:在Store中有多个HFile,每次刷写都会形成一个HFile文件落盘在HDFS上。HFile直接跟HDFS打交道,它是数据存储的实体。
2、HFile:在Store中有多个HFile,每次刷写都会形成一个HFile文件落盘在HDFS上。HFile直接跟HDFS打交道,它是数据存储的实体。
数据进入HFile之前就已经被持久化了,为什么还要放入MemStore
MemStore的意义在于维持数据按照rowkey的字典序排列,而不是做一个缓存提高写入效率。
Store细节
刷写细节
HBase架构组成
1、Client:Client包含了访问Hbase的接口,另外Client还维护了对应的cache来加速Hbase的访问,比如cache的.META.元数据的信息。
2、Zookeeper:HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作
3、Hmaster:主要负责负载均衡Region跟对应的DDL
4、HregionServer:直接对接用户的读写请求,是真正的干活的节点,处理客户端读写请求。
5、HDFS:提供元数据和表数据的底层分布式存储服务,数据多副本,保证的高可靠和高可用性
2、Zookeeper:HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作
3、Hmaster:主要负责负载均衡Region跟对应的DDL
4、HregionServer:直接对接用户的读写请求,是真正的干活的节点,处理客户端读写请求。
5、HDFS:提供元数据和表数据的底层分布式存储服务,数据多副本,保证的高可靠和高可用性
安装及增删改查
HBase 安装前依赖 Hadoop 跟 ZooKeeper
指令级别的CRUD,没啥说的百度教程吧
读写原理
写原理
zk ==> meta ==> regionserver ==> RegionIP
读原理
重点:不要理解为读数据先从MemStore中读取,然后读BlockCache中,如果没有再从HFile中读取,然后将数据写入到BlockCache中。
HBase 把磁盘跟内存数据一起读,然后把磁盘数据放到BlockCache中,BlockCache是磁盘数据的缓存。读比写慢的工具。
HBase 把磁盘跟内存数据一起读,然后把磁盘数据放到BlockCache中,BlockCache是磁盘数据的缓存。读比写慢的工具。
写入时:WAL(自动故障恢复) + MemStore(内存存储数据)
BlockCache称为读缓存,HBase将一次文件查到的Block块缓存到Cache来避免昂贵IO。
BlockCache称为读缓存,HBase将一次文件查到的Block块缓存到Cache来避免昂贵IO。
Scanner
Flush 各种参数配置
1、WAL 个数 :文件个数超过阈值会进行刷写。
2、memstore:regionServer的全局memstore的大小。flush会阻塞客户端读写。
3、lower.limit:写量大于flush量时候会阻塞写入。
4、optionalcacheflushinterval:内存中的文件在自动刷新之前能够存活的最长时间,默认是1h
5、.flush.size:单个region里memstore的缓存大小,超过那么整个HRegion就会flush,默认128M
2、memstore:regionServer的全局memstore的大小。flush会阻塞客户端读写。
3、lower.limit:写量大于flush量时候会阻塞写入。
4、optionalcacheflushinterval:内存中的文件在自动刷新之前能够存活的最长时间,默认是1h
5、.flush.size:单个region里memstore的缓存大小,超过那么整个HRegion就会flush,默认128M
StoreFile Compaction
memstore 每次刷写都会生成一个新的 HFile,且同一个字段的不同版本(timestamp) 和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。 为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。
Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction
Minor Compaction
会将临近的若干个较小的 HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。
Major Compactio
会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期和删除的数据。
数据删除时间
flush 可以删除在同一个内存中的数据,flush只会操作当前内存。并且写入本地后 内存数据会丢失。
Major compaction 大合并时候会自动把所以数据排序删除旧数据。
Major compaction 大合并时候会自动把所以数据排序删除旧数据。
Region Split
默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写Region 会自动进 行拆分
split时机
容易产生 数据倾斜 解决方法:提前做好Region组的规划,0-1k,1k-2k,2k-3k这样的。
官方不建议用多个列族,比如有CF1,CF2,CF3,但是 CF1数据很多而CF2跟CF3数据很少,那么当触发了region切分的时候,会把CF2跟CF3分成若干小份,不利于系统维护。
HBase API
环境准备 IDEA + Maven + HBase
万年不变的 xml配置打包 跟CRUD,百度看API 教程
删除记得先下线 再删除
大数据跟HBase
HBase就把他当做大数据中的数据库即可,然后任何可以分析HBase的引擎比如MR,Hive,spark链接上HBase都可以实现控制。
MapReduce操作HBase
Hive操作HBase
可以把Hive跟HBase进行关联,Hive中的数据不再由HDFS存储而是存储到HBase中,并且关联后Hive中添加数据在HBase中可看到,HBase中添加数据Hive也可看到。
HBase 优化
1、高可用
Hmaster负责监控RegionServer的生命周期,均衡RegionServer的负载,如果Hmaster挂掉了,那么整个HBase集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以HBase支持对Hmaster的高可用配置。
2. 预分区(重要)
没预先分区绝对会数据倾斜。创建表的时候要设置好分区。根据就是数据大小跟机器规模
1. 手动设定预分区
2. 生成16进制序列预分区
3. 按照文件中设置的规则预分区
4. API 分区
3.RowKey设计(重要)
设计rowkey的目的 ,让数据均匀的分布于所有的region中(散列性,唯一性,长度(生产中甚至可能70~100位)),在一定程度上防止数据倾斜。
1. 生成随机数、hash、散列值
2. 字符串反转(时间戳翻转)
3. 字符串拼接
4. 内存优化
HBase操作过程中需要大量的内存开销,但是不建议分配非常大的堆内存 RegionServer刷新会导致服务不可用
5、若干配置参数设置
允许在HDFS的文件中追加内容、优化DataNode允许的最大文件打开数、优化数据的写入效率、设置RPC监听数量、优化hbase客户端缓存等等
HBase 面试
1、HBase存在的意义及数据存储的底层
2、rowKey设计跟数据倾斜的预防以及提速
2、rowKey设计跟数据倾斜的预防以及提速
Hbase 为什么写比读快
根本原因Hbase底层的存储引擎为LSM-Tree(Log-Structured Merge-Tree)存储引擎
核心思想:假设内存足够大,更改跟插入操作先写入内存。达到条件再flush磁盘(归并排序方式)。
读取时需先看是否命中内存,否则访问磁盘。写性能比MySQL高了一个数量级,读性能低了一个数量级
结构化合并树把一棵大树拆分成N棵小树,首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期做merge成一棵大树,以优化读性能。
LSM 树解析
Hbase为什么读取速度快
1、有 BlockCache 跟 MemCache 缓存功能。都找不到才加载HFile内容。
2、按照rowKey有序存储。HFile文件按页存储。
3、缓存 + 高效有序而复杂的数据存储
2、按照rowKey有序存储。HFile文件按页存储。
3、缓存 + 高效有序而复杂的数据存储
Sqoop
功能定位
Sqoop(发音:skup)是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql、postgresql...)间进行数据相互传递
原理
将导入或导出命令翻译成 mapreduce 程序来实现。在翻译出的 mapreduce 中主要是对inputformat 和 outputformat 进行定制。
导入跟导出
导入数据 import
从非大数据集群(RDBMS)向大数据集群(HDFS,HIVE,HBASE)中传输数据,使用import关键字。
1. 全部导入数据
2. 查询导入
$CONDITIONS存在的意义
简而言之 保证数据结果有序化
3. 导入指定列
4. 使用sqoop关键字筛选查询导入数据
5、RDBMS到Hive
第一步将数据导入到HDFS,第二步将导入到HDFS的数据迁移到Hive仓库
6、RDBMS到Hbase
导出数据 export
导出概念指:从大数据集群(HDFS,HIVE,HBASE)向非大数据集群(RDBMS)中传输数据,叫做:导出,即使用export关键字。
HIVE/HDFS到RDBMS
demo
脚本式执行任务
总结:相对来说不是很难,主要是在常规关系型数据库到大数据体系一个转换的工具,命令多查询即可
Kafka
快的原因:Zero Copy + 顺序读写磁盘 + 直接内存映射 + sendfile0 + 多路IO复用 + Batch Deal
文件机制
Kafka高频面试题
Kafka重平衡
基本流程就是 Coordinator(协调者) 感知到 消费者组的变化,然后在心跳的过程中发送重平衡信号通知各个消费者离组,
然后消费者重新以 JoinGroup 方式加入 Coordinator,并选出Consumer Leader。
当所有消费者加入 Coordinator,Consumer Leader会根据 Coordinator给予的分区信息给出分区方案。
Coordinator 将该方案以 SyncGroup 的方式将该方案执行下去,通知各个消费者,这样就完成了一轮 重平衡了。
然后消费者重新以 JoinGroup 方式加入 Coordinator,并选出Consumer Leader。
当所有消费者加入 Coordinator,Consumer Leader会根据 Coordinator给予的分区信息给出分区方案。
Coordinator 将该方案以 SyncGroup 的方式将该方案执行下去,通知各个消费者,这样就完成了一轮 重平衡了。
Kafka未来的2.8版本将要放弃Zookeeper
数仓
Spark
flink
StreamSets
官网
官方文档
中台
数据中台说
数据中台不是大数据平台,也不是个系统。数据中台是聚合和治理跨域数据,将数据抽象封装成服务,提供给前台以业务价值的逻辑概念
业务层、数据中台层、源数据层
核心数据模型变化缓慢,数据维护较大,业务创新较快。如何协调数据开发跟应用开发。
1. 效率问题:应用开发增加个报表,因为数据源变化了。
2. 协作问题:业务开发逻辑类似,数据源不一样,重新梳理。
3. 能力问题:多个应用公开发人员,数据开发人员太少。
2. 协作问题:业务开发逻辑类似,数据源不一样,重新梳理。
3. 能力问题:多个应用公开发人员,数据开发人员太少。
DData API 是数据中台的核心,它是连接前台和后台的桥梁,通过 API 的方式提供数据服务,而不是直接把数据库给前台、让前台开发自行使用数据。
用运营方式经营数据中台,分析我们对外API调用的情况,及时调整改变优化数据服务,实现可经营化中台。
数据中台
数据中台是企业级的逻辑概念,体现企业 D2V(Data to Value)的能力,为业务提供服务的主要方式是数据 API
数据中台距离业务更近,为业务提供速度更快的服务
数据中台可以建立在数据仓库和数据平台之上,是加速企业从数据到业务价值的过程的中间层
数据平台
在大数据基础上出现的融合了结构化和非结构化数据的数据基础平台,为业务提供服务的方式主要是直接提供各种数据集,用户把所需数据集提取出来 单独分析建立模型
数据仓库
是一个相对具体的功能概念,是存储和管理一个或多个主题数据的集合,为业务提供服务的方式主要是分析报表
C++
Qt
UI开发
Linux
操作系统(Operating System)
认识操作系统
存在的意义:真正干活的是硬件 比如主板、显示器、显卡、鼠标、CPU等,而人们直接操作太难了需要往上封装一层 实现便捷调用
操作系统整体的思维 :没有什么是加一层解决不了的。
操作系统整体的思维 :没有什么是加一层解决不了的。
计算机概述
CPU
类似人类大脑,跟内存或寄存器交互,主要任务周而复始的提取、解码、执行。有不同品牌的CPU X86或者ARM
寄存器
更加靠近CPU的 小存储 为提速而存在 L1 16KB 这样的
程序计数器(program counter): 存储内存提取指令的地址
堆栈指针(stack pointer): 局部变量跟临时变量的存储
程序状态字(Program Status Word): 存储系统状态,在系统调用给IO作用巨大,可通过字段控制是 用户态还是内核态
多核CPU:在一枚处理器中集成两个或多个完整的计算引擎(内核 Die) 提速
多个Die 可以共享L2 缓存 也可以各种有独自的缓存
多核CPU 跟 多CPU组成的多核:还是时间效率角度考虑,指令级别 交互
GPU:成千上文个Die 组成的中央处理器,主要擅长大量的并行简单计算。用于AI行业
时钟周期:CPU振荡器两个脉冲间隔时间单位,1GHz = 每秒执行10亿个时钟周期
磁盘(硬盘):访问数量级比CPU慢三个
硬盘基本知识(磁头、磁道、扇区、柱面)
Kafka单元有配图说明
Kafka单元有配图说明
扇区(sector):硬盘的读写以扇区为基本单位 512字节
磁道(track):磁盘上的磁道是一组记录密度不同的同心圆
柱面(cylinder):上下一串盘片中,相同半径的磁道所组成的一个圆柱型的环壁,就称为柱面
磁头(head):通过磁性原理读取磁性介质上数据的部件
SSD 固态硬盘:不是磁盘 数据存储在闪存中,速度比硬盘快很多
虚拟内存
含义:期望存储空间 》实际内存
方法:程序存储磁盘上,主存存储最频繁使用的部分,需要制作快速映射内存地址
总线(Bus)
计算机各种功能部件之间传送信息的公共通信干线(导线组成)
计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号
PCle:以前传输是并行共享,现在是高速串行点对点双通道高带宽传输,不共享总线带宽。
主板(mainboard)
一般为矩形电路板,里面有BIOS芯片、IO控制芯片、键盘跟面板控制开关、指示灯、内存跟显卡插槽等等。
冯诺依曼计算机体系
计算机启动过程
BIOS自检
系统引导
启动内核
初始化系统
操作系统概念
进程
进程是指在系统中正在运行的一个应用程序,是系统进行资源分配和调度的一个独立单位
地址空间
操作系统在管理内存时,每个进程都有一个独立的进程地址空间,进程地址空间的地址为虚拟地址,对于32位操作系统,该虚拟地址空间为2^32=4GB。 进程在执行的时候,看到和使用的内存地址都是虚拟地址,而操作系统通过MMU部件将进程使用的虚拟地址转换为物理地址。
文件
OS提供文件系统类似HDFS,屏蔽磁盘IO细节提供文件模型,目录、分隔符、权限。
UNIX中一切皆文件,特殊文件使得IO设备像文件一样,管道pipe是一种虚文件用来连接两个进程。
权限设置,对应Linux中的chmod
shell (计算机壳层)
为使用者提供操作界面的软件(命令解析器),用来简单直观的实现用户跟内核交互的一个外壳工具。
系统调用
OS 提供了一套 可移植操作系统接口 POSIX,
UNIX
进程管理系统调用
fork、waitpid、exit
文件管理系统调用
open、close、read、write、iseek、stat
目录管理系统调用
mkdir、rmdir、link、unlink、mount、unmount
由执行某些操作或执行代码组成实现系统调用。
Win 32API
事件驱动的机制
OS 核心点
内存管理
虚拟内存
进程向OS申请地址空间运行程序,操作系统提供一种机制将不同进程申请的空间地址跟不同内存的物理地址映射起来。不同进程运行时,写入不同的物理地址,避免冲突
程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address)
实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)
CPU 芯片中的内存管理单元(Memory Management Unit)实现虚拟内存到物理内存的映射
如何管理虚拟地址与物理地址之间的关系
内存分段
程序由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。
段选择因子 + 段内偏移量
优点
产生连续的内存空间
缺点
内存碎片
内存交换的效率低
Swap空间:用来内存交换,将内存数据写入磁盘后再读取写入到内存来实现数据内存位置的变更。
内存分页
分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小,一页(Page,大小4KB),页表实际上存储在 CPU 的内存管理单元 (MMU) 中
采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。
页表一定要覆盖全部虚拟地址空间
局部性原理 多级分页
x86:先进行段式映射,然后才能进行页式映射
TLB(Translation Lookaside Buffer)
通常称为页表缓存、转址旁路缓存、快表等
段页式内存管理
内存分段 + 内存分页并
先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;
接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;
Linux 内存管理
Linux 内存主要采用的是页式内存管理,但同时也不可避免地涉及了段机制
Ring0~Ring3
CPU硬件级别划分 用户态跟内核态
进程管理
进程Process
运行中的程序,就被称为进程,CPU分配资源基本单位
并行跟并发
进程控制块(process control block,PCB)
进程描述信息:进程标识符、用户标识符
进程控制和管理信息:进程当前状态、进程优先级
资源分配清单、CPU相关信息
通过链表的方式进行组织,把具有相同状态的进程链在一起,组成各种队列
进程的创建、终止、阻塞、唤醒的过程 通过PCB
根据进程状态 进行不同的链式存储
进程的上下文切换
进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。
进程间通信方式
1、管道 |
2、消息队列 :MQ
3、共享内存 :JVM
4、信号量:unix
5、Socket:网络编程
2、消息队列 :MQ
3、共享内存 :JVM
4、信号量:unix
5、Socket:网络编程
调度程序(scheduler):目的就是要使得进程要「快」
调度时机
非抢占式调度算法:不会理时钟中断,运行完毕后再运行别的
抢占式调度算法:时间片结束直接运行另外一个线程
调度原则
CPU 利用率:调度程序应确保 CPU 是始终匆忙的状态,这可提高 CPU 的利用率;
系统吞吐量:吞吐量表示的是单位时间内 CPU 完成进程的数量,长作业的进程会占用较长的 CPU 资源,因此会降低吞吐量,相反,短作业的进程会提升系统吞吐量;
周转时间:周转时间是进程运行和阻塞时间总和,一个进程的周转时间越小越好;
等待时间:这个等待时间不是阻塞状态的时间,而是进程处于就绪队列的时间,等待的时间越长,用户越不满意;
响应时间:用户提交请求到系统第一次产生响应所花费的时间,在交互式系统中,响应时间是衡量调度算法好坏的主要标准。
调度算法
1、先来先服务调度算法
每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。
2、最短作业优先调度算法
优先选择运行时间最短的进程来运行
3、 高响应比优先调度算法
每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行
4、时间片轮转调度算法
每个进程被分配一个时间段,称为时间片(Quantum 时间片设为 20ms~50ms),即允许该进程在该时间段中运行。
5、 最高优先级调度算法
从就绪队列中选择最高优先级的进程进行运行,这称为最高优先级(Highest Priority First,HPF)调度算法
6、 多级反馈队列调度算法
时间片轮转算法 和 最高优先级算法 的综合和发展
「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列
「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列
线程 Thread
线程是进程当中的一条执行流程,线程之间可以并发运行且共享相同的地址空间。
同一个进程内多个线程之间可以共享代码段、数据段、打开的文件等资源,但每个线程都有独立一套的寄存器和栈,这样可以确保线程的控制流是相对独立的。
优点
一个进程中可以同时存在多个线程;
各个线程之间可以并发执行;
各个线程之间可以共享地址空间和文件等资源;
各个线程之间可以并发执行;
各个线程之间可以共享地址空间和文件等资源;
缺点
进程中的一个线程奔溃时,会导致其所属进程的所有线程奔溃。
线程的上下文切换
所谓任务调度,实际上调度对象是线程,而进程只是给线程提供了虚拟内存、全局变量等资源
当进程只有一个线程时,可以认为进程就等于线程;
当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不需要修改的;
当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不需要修改的;
当两个线程不是属于同一个进程,则切换的过程就跟进程上下文切换一样;
当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据;
当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据;
线程的实现
用户线程
用户线程的整个线程管理和调度,操作系统是不直接参与 也看不到的,而是由用户级线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度等。
优点
每个进程都需要有它私有的线程控制块(TCB)列表,用来跟踪记录它各个线程状态信息
用户线程的切换也是由线程库函数来完成的,无需用户态与内核态的切换,所以速度特别快
缺点
1、操作系统无法调度线程,如果线程调用导致阻塞,会导致阻塞。
2、多线程瓜分进程得到的时间片,每个线程获得时间更少。
3、线程开始跑后 如果不交CPU控制权其他线程无法运行。
2、多线程瓜分进程得到的时间片,每个线程获得时间更少。
3、线程开始跑后 如果不交CPU控制权其他线程无法运行。
内核线程
内核线程是由操作系统管理的,线程对应的 TCB 自然是放在操作系统里的,这样线程的创建、终止和管理都是由操作系统负责。
轻量级进程
轻量级进程是内核支持的用户线程,它是基于内核线程的高级抽象,系统只有先支持内核线程才能有 轻量级进程
Linux中没有真正的线程,因为Linux并没有为线程准备特定的数据结构。在内核看来只有进程而没有线程,在调度时也是当做进程来调度。Linux所谓的线程其实是与其他进程共享资源的进程。但windows中确实有线程。
协程
比线程更小的一种级别,在Python、go 等语言中进行了实现,核心点在于多协程调用时候不会涉及到 内核态到用户态的来回切换。所以操作在用户态完成即可。
进程线程区别
1、进程是资源(包括内存、打开的文件等)分配的单位,线程是 CPU 调度的单位
2、进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈
3、线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系
4、线程能减少并发执行的时间和空间开销
2、进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈
3、线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系
4、线程能减少并发执行的时间和空间开销
线程相比进程能减少开销
文件系统管理
功能:负责管理跟存储数据到磁盘中
Linux :一切皆文件
每个文件拥有:索引节点(index node)和目录项(directory entry),它们主要用来记录文件的元信息和目录层次结构。
索引节点,也就是 inode,用来记录文件的元信息,比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等等。索引节点是文件的唯一标识,它们之间一一对应,也同样都会被存储在硬盘中,所以索引节点同样占用磁盘空间。
目录项,也就是 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来,就会形成目录结构,但它与索引节点不同的是,目录项是由内核维护的一个数据结构,不存放于磁盘,而是缓存在内存。
目录跟目录项
目录是个文件,持久化存储在磁盘
目录项是内核一个数据结构,缓存在内存,把已经读过的目录用目录项这个数据结构缓存在内存 方便查询
读写基本单位
磁盘读写的最小单位是扇区,扇区的大小只有 512B,文件系统把多个扇区组成了一个逻辑块 4KB,一次性读写 8 个扇区,文件系统的基本操作单位是数据块
虚拟文件系统(Virtual File System,VFS)
定义了一组所有文件系统都支持的数据结构和标准接口
文件系统分类
磁盘的文件系统,它是直接把数据存储在磁盘中
内存的文件系统,这类文件系统的数据不是存储在硬盘的,而是占用内存空间 /proc 和 /sys
网络的文件系统,用来访问其他计算机主机数据的文件系统,比如 NFS、SMB 等。
文件的存储
连续空间存放方式
文件存放在磁盘连续的物理空间中,文件头里需要指定起始块的位置和长度,读写效率很高,但有磁盘空间碎片和文件长度不易扩展的缺陷
非连续空间存放方式
链表方式
索引方式
空闲空间管理
空闲表法
空闲表法就是为所有空闲空间建立一张表
空闲链表法
每一个空闲块里有一个指针指向下一个空闲块
位图法
利用二进制的一位来表示磁盘中一个盘块的使用情况,Linux 文件系统就采用了位图的方式来管理空闲空间
软链接和硬链接
硬链接是多个目录项中的索引节点指向一个文件,不可用于跨文件系统,只有删除所有硬链接以及源文件时,系统才会彻底删除该文件。等价 cp -p 加 同步更新
软链接可以理解成快捷方式,相当于重新创建一个文件,这个文件有独立的 inode
文件 I/O
缓冲与非缓冲 I/O:是否利用标准库缓冲
缓冲 I/O,利用的是标准库的缓存实现文件的加速访问,而标准库再通过系统调用访问文件。
非缓冲 I/O,直接通过系统调用访问文件,不经过标准库缓存。
直接与非直接 I/O:否利用操作系统的缓存
直接 I/O,不会发生内核缓存和用户程序之间数据复制,而是直接经过文件系统访问磁盘。
非直接 I/O,读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存,再由内核决定什么时候写入数据到磁盘。
读取个文件数据操作
CPU把数据从磁盘读到内核缓冲区。
CPU把数据从内核缓冲区拷贝到用户缓冲区。
CPU把数据从内核缓冲区拷贝到用户缓冲区。
阻塞 非阻塞
阻塞:线程发起read请求就 被阻塞住了
非阻塞:线程发起read请求 一直查看是否read完毕
IO多路复用: 一个专门线程来监控多个IO
select、poll、epoll
同步 异步
同步跟异步关注的是我发出调用请求后,调用直接返回结果还是调用者要一直等待。
阻塞跟非阻塞关注的是程序在等待调用结果时的状态。
数据从内核态 到 用户态 是否有 用户线程完毕
同步跟异步关注的是我发出调用请求后,调用直接返回结果还是调用者要一直等待。
阻塞跟非阻塞关注的是程序在等待调用结果时的状态。
BIO
同步阻塞 模式 ,一个请求一个线程
NIO
同步非阻塞模式,一个线程有一个selector 可以同时监控多个channel
AIO
异步非阻塞模式
IO 如何观测
在日常生活中,80% 的场景,我用 iostat 就够了。其实存储本身没有那么多花里胡哨的东西。
性能考虑的点就:iops,吞吐,时延;
用户能够决定哪些IO的点:IO 大小,io 并发深度,随机还是顺序;
iostat 就能提供以上这些数据,让磁盘的 IO 可观测。你尝试在机器上执行 iostat -x -k 1 这条命令,就能看到所有磁盘的 io 情况了。
通过这些你能获悉到哪些信息?
1. 磁盘读写 iops
2. 磁盘读写带宽
3. io合并情况(如果是顺序读写,这里会有)
4. io 的时延
5. io 的平均大小(扇区单位)
6. 磁盘繁忙程度和真实时延的指标(这两个指标有些场景不准,没有参考意义,准确的说,某些场景只对单通道的hdd盘有用)
性能考虑的点就:iops,吞吐,时延;
用户能够决定哪些IO的点:IO 大小,io 并发深度,随机还是顺序;
iostat 就能提供以上这些数据,让磁盘的 IO 可观测。你尝试在机器上执行 iostat -x -k 1 这条命令,就能看到所有磁盘的 io 情况了。
通过这些你能获悉到哪些信息?
1. 磁盘读写 iops
2. 磁盘读写带宽
3. io合并情况(如果是顺序读写,这里会有)
4. io 的时延
5. io 的平均大小(扇区单位)
6. 磁盘繁忙程度和真实时延的指标(这两个指标有些场景不准,没有参考意义,准确的说,某些场景只对单通道的hdd盘有用)
I/O 设备管理
设备控制器(Device Control)
CPU 通过读、写设备控制器中的寄存器来控制设备
组成
状态寄存器(Status Register):存储设备状态
命令寄存器(Command Register):转接翻译CPU上的命令
数据寄存器(Data Register):信息存储
输入输出设备分类
块设备(Block Device)
把数据存储在固定大小的块中,每个块有自己的地址,硬盘、USB 是常见的块设备。
控制器设立了一个可读写的数据缓冲区 为块设备
CPU 写入数据到控制器的缓冲区时,当缓冲区的数据囤够了一部分,才会发给设备。
CPU 从控制器的缓冲区读取数据时,也需要缓冲区囤够了一部分,才拷贝到内存。
CPU 从控制器的缓冲区读取数据时,也需要缓冲区囤够了一部分,才拷贝到内存。
字符设备(Character Device)
以字符为单位发送或接收一个字符流,字符设备是不可寻址的,也没有任何寻道操作,鼠标是常见的字符设备。
I/O 控制方式
第一种:CPU轮询等待控制器寄存器中状态标志位。
第二种:硬件级别中断控制器告知CPU任务完成,让CPU来处理中断。
第三种:引入 DMA(Direct Memory Access)由其控制拿去数据放到内存中。
第二种:硬件级别中断控制器告知CPU任务完成,让CPU来处理中断。
第三种:引入 DMA(Direct Memory Access)由其控制拿去数据放到内存中。
设备驱动程序
为了屏蔽「设备控制器」的差异,引入了设备驱动程序 提供统一的接口给操作系统,这样不同的设备驱动程序,就可以以相同的方式接入操作系统
通用块层
对于块设备,为了减少不同块设备的差异带来的影响 用此管理
向上提供统一接口,向下统一块访问方式
IO调度运行
没有调度算法:一般用在虚拟IO中
先入先出调度算法:队列
完全公平调度算法:一般默认用此
优先级调度:优先队列
最终期限调度算法:读写创建不同IO队列。
先入先出调度算法:队列
完全公平调度算法:一般默认用此
优先级调度:优先队列
最终期限调度算法:读写创建不同IO队列。
存储系统 I/O 软件分层
文件系统层:向上提供统一标准文件访问接口,向下通过通用块层管理磁盘数据
通用块层: IO队列跟 调度器
设备层:包括硬件设备、设备控制器和驱动程序,负责最终物理设备的 I/O 操作
IO 多路复用
多路复用产生的效果,完全可以由用户态去遍历文件描述符并调用其非阻塞的 read 函数实现。而多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,变成了一次系统调用 + 内核层遍历这些文件描述符。
学习推荐
操作系统 - 清华大学
操作系统 - 哈工大
面试题
阻塞挂起区别
常用指令
windows 根据端口杀死占用端口进程
Linux根据端口杀进程:
crontab:简单任务调度
tmux:后台好工具
iftop:查看实时的网络流量
windows查看是否可ping 同某个端口
telnet IP port
Linux查看端口占用情况
netstat -anp | grep port
常见性能调优:top、iostat、pidstat、ps、vmstat、netstat、sar
Python
基础语法
廖雪峰Python教程
高淇Python400集
基本思维导图
有点胶水语言意思:AI、爬虫、数据分析、Web全栈开发。各种三方安装包装后high起来。
开发环境:pycharm + anaconda。严格缩进,代码规范优雅。一切皆对象有 id,type,value组成。基本数据类型,list、set、dict。lambda表达式,python3类型提醒
函数的定义跟使用,返回值,递归优化。不可变参数(相当于C系语言的值传递)、可变参数(相当于C系语言的指针传递)
可迭代对象Iterable
迭代器Iterator
生成器generator
也就是在需要的时候才产生结果,不是立即产生结果。
1、生成器是只能遍历一次的
2、生成器是一类特殊的迭代器。
2、生成器是一类特殊的迭代器。
1、函数中 return value 编程 yield value。
2、生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号(),比如 tmp = (x for x in list)
2、生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号(),比如 tmp = (x for x in list)
序列(字符串、列表、元组):对序列进行for循环遍历时后台会对容器对象调用iter()函数,返回一个定义了_next_方法的 迭代器Iterator。
字典
区分:容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dict comprehension)
使用isinstance()判断一个对象是否是Iterable对象
函数作为返回值,闭包的引入
装饰器decorator
Python中默认 __name__就是特殊变量,_xxx是非公开的,
OOP
Class跟Instance,__init__,创建对象不用new,合理规定类中访问权限,多继承跟多态。types跟isinstance(),dir()=获得一个对象的所有属性和方法,
@staticmethod和@classmethod
__slots__ 限制实例的属性,@property将方法调用包装成属性调用,枚举类,使用type()动态创建类,__XX__进行定制类,延伸到元类metaclass。
try.except.finally,assert,unittest单元测试,doctest文档测试。
with open 读写数据,StringIO跟 BytesIO 在内存读写,os,glob,pickle序列化。
1、多进程:fork、multiprocessing、Pool,通讯Queue、Pipes。
2、多线程:threading、lock、伪多线程,GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁
3、正则表达式。
2、多线程:threading、lock、伪多线程,GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁
3、正则表达式。
datetime、collections、base64、struct、hashlib、hmac、itertools、contextlib、urllib、XML、HTMLParser、Pillow、Requests、psutil
email、pymysql、SQLAlchemy
异步IO:执行耗时的IO操作时,只发出IO指令,不等待IO结果。然后去执行其他代码。协程概念思想。
Jupyter notebook
MarkDown + Web形式 进行进行Python编程
jupyter lab 下一代的jupyter notebook
jupyter_contrib_nbextension 必备安装
numpy
pandas
爬虫
通俗说
scrapy
Web
flask
Nginx、uWSGI和Flask 关系理解
flask官方文档
Django
airFlow:DAG任务调度框架
AirFlow大致讲解
可视化绘图
Pyecharts
Matplotlib
plotly
AI
机器学习
机器学习实战
李航统计学习方法
周志华 机器学习 西瓜书
机器学习与应用---雷明
吴恩达 李飞飞
机器学习算法图
分类章节
1、所需数学知识(ML的根基)
1、微积分:主要用到了微分部分,作用是求函数的极值
1、导数和偏导数的定义与计算方法
2、梯度向量的定义
3、极值定理,可导函数在极值点处导数或梯度必须为0
4、雅克比矩阵,这是向量到向量映射函数的偏导数构成的矩阵,在求导推导中会用到
5、Hessian矩阵,这是2阶导数对多元函数的推广,与函数的极值有密切的联系
6、凸函数的定义与判断方法
7、泰勒展开公式
8、拉格朗日乘数法,用于求解带等式约束的极值问题
2、线性代数:几乎所有地方都有使用
机器学习算法处理的数据一般都是向量、矩阵或者张量。经典的机器学习算法输入的数据都是特征向量,深度学习算法在处理图像时输入的2维的矩阵或者3维的张量
1、向量和它的各种运算,包括加法,减法,数乘,转置,内积
2、向量和矩阵的范数,L1范数和L2范数
3、矩阵和它的各种运算,包括加法,减法,乘法,数乘
4、逆矩阵的定义与性质
5、行列式的定义与计算方法
6、二次型的定义
7、矩阵的正定性
8、矩阵的特征值与特征向量
9、矩阵的奇异值分解
10、线性方程组的数值解法,尤其是共轭梯度法
3、概率论:把机器学习所处理的样本数据看作随机变量/向量,我们就可以用概率论的观点对问题进行建模
1、随机事件的概念,概率的定义与计算方法
2、随机变量与概率分布,尤其是连续型随机变量的概率密度函数和分布函数
3、条件概率与贝叶斯公式
4、常用的概率分布,包括正态分布,伯努利二项分布,均匀分布
5、随机变量的均值与方差,协方差
6、随机变量的独立性
7、最大似然估计
4、最优化方法:几乎所有机器学习算法归根到底都是在求解最优化问题,指导思想是在极值点出函数的导数/梯度必须为0,必须理解梯度下降法,牛顿法,坐标下降法
凸优化理论,拉格朗日对偶(等式 + 不等式约束条件),KKT条件。
白话大数据与机器学习
1、数据的认识跟应用是大数据跟机器学习的基础,数据、信息、算法、统计、概率、数据挖掘、BI是和核心基础概念与要素。
2、扔硬币的排列组合,有限的古典概率跟非古典概率。
3、统计与分布:平均值,加权值,标准差,众数,中位数,欧式距离(直线距离),曼哈顿距离(不同坐标距离绝对值和),同步跟环比,抽样simple,高斯分布(正态分布),28原则,泊松分布,伯努利分布,指标化评判(pv,uv)
2、扔硬币的排列组合,有限的古典概率跟非古典概率。
3、统计与分布:平均值,加权值,标准差,众数,中位数,欧式距离(直线距离),曼哈顿距离(不同坐标距离绝对值和),同步跟环比,抽样simple,高斯分布(正态分布),28原则,泊松分布,伯努利分布,指标化评判(pv,uv)
4、香农(C. E. Shannon)信息论应用概率来描述不确定性。结果越大事件越不确定,n表示的结果多少种
信息熵:信息越确定熵越小,信息越不确定熵越大
5、向量Vector跟维度,矩阵Matrix跟矩阵计算,数据立方体,上卷Rollup跟下钻Drilldown
6、监督学习 回归Regression:归纳的思想,由果索因,打尺带最小二乘法,y=ax+b+e(残差分析,偏导数求ab系数),欠拟合Underfitting与过拟合overfitting,泛化能力。线性可分与线性不可分
结果只有两个情况的二项分布回归叫逻辑回归 Logistic Regression
7、非监督学习 聚类Clustering:归纳归类,K-Means算法(指定类簇个数,初始化类中心),孤立点点清洗跟处理
层次聚类:
凝聚层次聚类,自底向上
AgglomerativeClustering
Ward:所有类簇方差最小化
Maxinum:类簇间距离最大值最小化
Average linkage: 类簇之间距离平均值最小化
Maxinum:类簇间距离最大值最小化
Average linkage: 类簇之间距离平均值最小化
分裂点层次聚类,从上到下,比如KMeans
密度聚类
聚类形状不规则,sklearn.cluster.DBSCAN
聚类评估
聚类趋势,霍普金斯统计值趋向 1
簇数确定,肘方法 确定到底 簇数
测定簇类质量
A点距离自己所在簇距离平均值B,A点距离其他类簇最小值C,C大于B才好
8、分类
朴素贝叶斯 Naive Bayesian :概率论
P(A|B) P(B) = P(B|A) P(A)
P(A): 先验概率 A发生点概率
P(B|A):似然度,A发生的情况下B发生的概率
P(A|B) : 后验概率, B发生情况下A发生的概率,要计算的概率
P(B): 标准化常量, B发生的概率
P(B|A):似然度,A发生的情况下B发生的概率
P(A|B) : 后验概率, B发生情况下A发生的概率,要计算的概率
P(B): 标准化常量, B发生的概率
掉包时
高斯朴素贝叶斯:利用高斯概率密度公式进行分类拟合
多项式朴素贝叶斯:高纬度向量分类,文章分类
伯努利朴素贝叶斯:布尔类型特征值向量分类
多项式朴素贝叶斯:高纬度向量分类,文章分类
伯努利朴素贝叶斯:布尔类型特征值向量分类
贝叶斯理论体系是一种典型的人类自身推测逻辑行为
决策树Decision Tree
信息熵的使用,每次选择使得信息增益最大的字段。
前剪枝跟后剪枝来优化系统提高泛化能力
随机森林 Random forest
随机构造多棵树,用民主投票方式给出结果
from sklearn.ensemble import RandomForestClassifier
隐马尔可夫模型 Hidden Markov Model
隐含状态链
想知道隐含状态链
维特比算法:找出可能性最大的隐藏序列,在这段信息后最可能出现那些前置内容,比如拼音打字
想知道一个目标结果的概率
前向算法:所有可以产生该结果的概率相加
想知道转换概率
支持向量机 Support Vector Machine
比较强的一个二分类算法,推导涉及偏导数,凸优化理论等,线性不可分时用超平面 核函数Kernal映射到高维度
遇到线下不可分时候
SVM 将数据特征映射到高维进行分割
神经网络 增加输入的变量,网络的层次,输出层
遗传算法 Genetic Algorithm:物竞天择适者生存
直接选择、基因重组、基因突变
分类:大部分都是基于统计概率下的分类算法,本质是贝叶斯概率体系下的分类原则。
9、关联分析:数据挖掘
经典案例 啤酒和尿布,数据挖掘体系关键点。
频繁模式:找出同时买组支持度跟置信度的数据
Support(支持度):指的是有用性,所有记录中多少个是包含我们需要的模式。
Confidence(可信度):指的有方向性,购买了A的有多少购买了B。
Apriori算法 大致步骤
1、设置最小支持度阈值 扫描。
2、扫描出来的数据进行组合计算置信度。
3、再根据置信度阈值过滤。
4、二次组合,笛卡尔积的使用 不断过滤。
2、扫描出来的数据进行组合计算置信度。
3、再根据置信度阈值过滤。
4、二次组合,笛卡尔积的使用 不断过滤。
Lift(提升度)
关联性=1 表明AB发生概率无关系。
关联性>1 表明A的发送促进了B的发生。
关联性<1 表明A的发送抑制了B的发生。
关联性>1 表明A的发送促进了B的发生。
关联性<1 表明A的发送抑制了B的发生。
稀有模式:多个事件同事发生的概率很低的那种。
负模式: 两个时间发生的概率 此消彼长。
负模式: 两个时间发生的概率 此消彼长。
10、推荐系统
协同过滤:利用某兴趣相投、拥有共同经验之群体的喜好来推荐用户感兴趣的信息
1、基于用户 User Based Collaborative Filtering
余弦相似性:[-1,1],值越大说明用户之间喜爱越相似
2、基于商品 Item Based Collaborative Filtering
算法核心:有很多人喜欢A同时也喜欢B,所以A跟B是类似东西。
分子:同时喜欢AB的用户数
分母:喜欢A跟喜欢B的乘积平方根
分母:喜欢A跟喜欢B的乘积平方根
1、规模跟效率:从活跃用户购物列表找产品,从最近半年购买的产品中找候选产品。
2、覆盖率:勿全部推荐一样商品,确保多样性。每一行用到归一化。
2、覆盖率:勿全部推荐一样商品,确保多样性。每一行用到归一化。
11、文本挖掘
文本挖掘:从文本数据中抽取事先位置的可理解的最终可用的知识过程,是从非结构化文本寻找知识的过程。
1、搜索跟信息检索:
2、文本聚类:
3、文本分类:
4、Web挖掘:
5、信息抽取
6、NLP
2、文本聚类:
3、文本分类:
4、Web挖掘:
5、信息抽取
6、NLP
子主题
有监督学习
无监督学习
强化学习
深度学习
子主题
BP神经网络
玻尔兹曼机(旅行商问题)
CNN
知乎卷积通俗说
掷骰子、蒸馒头、图像卷积旋转、吃冰淇淋
关键点是理解 对折 跟 位移,CNN中都是用的最终矩阵
连续形式
离线形式
卷积层
局部感知
参数共享
采样层
数据结构与算法
数据结构
算法复杂度
时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述
空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。
一般情况下时间跟空间很难同时兼顾,有侧重点的大部分都是时间
大O表示法
程序 = 数据结构 + 算法
数据结构struct 的逻辑存储跟物理存储
物理存储:数据真实在磁盘跟内存中存储的形式
顺序存储结构
链式存储结构
逻辑存储:人根据自己的思维而创建的各种数据格式
线性表
线性存储
树形存储
图形存储
算法 Algorithm
算法特性
1、输入输出
2、有穷性
3、确定性
4、可行性
2、有穷性
3、确定性
4、可行性
涉及算法要求
1、正确性
2、可读性
3、健壮性
4、时间效率高和存储量低
2、可读性
3、健壮性
4、时间效率高和存储量低
算法像一种事物运行的逻辑和规则
排序
O(n^2)
冒泡排序 稳定
选择排序 不稳定
插入排序 稳定
O(nlogn)
希尔排序 不稳定
归并排序 稳定
快速排序 不稳定
堆排序 不稳定
O(n) 稳定
计数排序
基数排序
桶排序
十大排序
线性表
数组 Array
链表 LinkedList
单链表
双向链表
循环链表
静态链表:使用数组模拟链表
栈 Stack
特点:插入和删除限定为仅在一端进行,后进先出
子弹上膛、浏览器返回上一级
顺序栈:数组实现
链式栈: 链表实现
递归就是栈的应用
如何进入,如何break、汉诺塔
尾递归优化
队列 Queue
特点: 先进先出
普通队列:一端进 一端出
双端队列:两端均可进出
循环队列:数据存储成环,MySQL中的 redo log
阻塞队列:队列max有上线
优先队列:根据优先级执行
并发队列
字符串匹配
朴素算法
KMP
子主题
Trie树
功能:专门处理字符串匹配的数据结构
特点:
根节点不包含字符,除根节点外每一个节点都只包含一个字符
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
每个节点的所有子节点包含的字符都不相同
根节点不包含字符,除根节点外每一个节点都只包含一个字符
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
每个节点的所有子节点包含的字符都不相同
用处
找到具有同一前缀的全部键值。
按词典序枚举字符串的数据集。
找到具有同一前缀的全部键值。
按词典序枚举字符串的数据集。
理念:Trie的核心思想是空间换时间
AC自动机
散列表HashTable
实现思想:空间换时间,KV存储类似,注意哈希冲突,关键是散列函数的散列性
散列函数
直接定址法
平方取中法
折叠法
除留取余法
平方取中法
折叠法
除留取余法
冲突解决
链表法
开放寻址
动态扩容
这一块理解了 Java中的HashMap则一切尽在不言中
位图
装填因子 = 表中的记录数 / 哈希表的长度 ,一般0.5为好
树 Tree
二叉树
完全二叉树、满二叉树
哈夫曼树:路径之和最小化
二叉查找树 Binary Search Tree
Left < Root < Right
平衡二叉树 Balanced Binary Tree
在二叉查找树的基础上要求 左右深度差不得大于1,又名AVL树
遍历:递归跟非递归 前序、中序、后序、层序
平衡二叉查找树
AVL树 又名平衡二叉树
红黑树 Red Black Tree
重点理解红黑树跟2-3-4树的关联,理解红黑树5大要素的来源
树跟森林的转换
多路查找树
B树
B+树
B*树
MySQL 底层存储
堆 Heap
大根堆:父节点的值大于或等于子节点的值
小根堆:父节点的值小于或等于子节点的值
图 Graph
生活中真实的关系网就是图,上面都是图的简单抽象化
重要两个属性 :节点Vertex、边Edge
数据的存储方式
1、邻接矩阵:一维数组存定点,二维数组存边。如果边少存在浪费。
2、邻接表:一维数组存定点,每个数组item后用链表连接到的边,可以有向图可以无向图。关注了出度就无法关注入度
3、十字链表:一个节点有出度链表跟入度链表,属于邻接表有向图的优化。
4、邻接多重表:关注无向边,属于邻接表无向图的优化。
5、边集数组:一个数组存节点信息,一个数组每个item存begin、end、weight
图的遍历 Traversing Graph
深度优先遍历:DFS(depth first search)
广度优先遍历:BFS(Breadth first search)
最小生成树:以最小的代价将节点关联起来(贪心算法)
普里姆(Prim)算法
思维:定点角度出发,将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 A 类),剩下的是另一类(假设为 B 类),适合边多的图
克鲁斯卡尔算法(Kruskal算法)
思维:将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。适合边少的图
并查集、路径压缩、按秩合并
最短路径:两点之间权重最少路径
迪杰斯特拉(Dijkstra算法)
最朴素的思想就是按长度递增的次序产生最短路径。即每次对所有可见点的路径长度进行排序后,选择一条最短的路径,这条路径就是对应顶点到源点的最短路径。
弗洛伊德算法:任意两顶点之间的最短路径
对于网中的任意两个顶点(例如顶点 A 到顶点 B)来说 无非就是 Dis(A,K)+ Dis(K,B)< Dis(A,B)
DAG 有向无环图
拓扑排序
拓扑排序指的是将有向无环图(又称“DAG”图)中的顶点按照图中指定的先后顺序进行排序。一般用来在工程中描述任务之间的依赖关系
AOV网(Activity On Vertex NetWork)用顶点表示活动,边表示活动(顶点)发生的先后关系。
工程关键路径
AOE网的定义:在带权有向图中若以顶点表示事件,有向边表示活动,边上的权值表示该活动持续的时间,这样的图简称为AOE网。
关键在于从 AOE 网中找到一条从起始点到结束点长度最长的路径
对于所有的边来说,如果它的最早开始时间等于最晚开始时间,称这条边所代表的活动为关键活动。由关键活动构成的路径为关键路径。
查找
折半查找法
各种树的查找
常见算法思想
贪心算法
基本思路
建立数学模型来描述问题
把求解的问题分成若干个子问题
对每个子问题求解,得到子问题的局部最优解
把子问题的解局部最优解合成原来问题的一个解
把求解的问题分成若干个子问题
对每个子问题求解,得到子问题的局部最优解
把子问题的解局部最优解合成原来问题的一个解
存在问题
不能保证求得的最后解是最佳的
不能用来求最大值或最小值的问题
只能求满足某些约束条件的可行解的范围
不能用来求最大值或最小值的问题
只能求满足某些约束条件的可行解的范围
案例:活动安排、找零钱、背包、移动纸牌、最大整数
分治算法
思想:分而治之
1、原问题可以分解为多个子问题
2、原问题在分解过程中,递归地求解子问题
3、在求解并得到各个子问题的解后应能够采用某种方式、方法合并或构造出原问题的解。
2、原问题在分解过程中,递归地求解子问题
3、在求解并得到各个子问题的解后应能够采用某种方式、方法合并或构造出原问题的解。
案例:二分查找法、归并排序、ForkJoin
回溯算法
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。它是隐式地,通过递归,构建出一棵解的空间树
案例:八皇后、01背包问题、矩形最短路径问题
枚举算法
思想:将问题的所有可能的答案一一列举,然后根据条件判断此答案是否合适
优点:算法简单,在局部地方使用枚举法,效果十分的好
缺点:运算量过大,当问题的规模变大的时候,循环的阶数越大,执行速度越慢
缺点:运算量过大,当问题的规模变大的时候,循环的阶数越大,执行速度越慢
案例:百钱买白鸡,在暴力解题的基础上看如何减少循环
动态规划(Dynamic Programming)
斐波那契数组(兔子问题)优化、青蛙跳台阶、矩形组合、数字变化字母
经典最小路径和
最长上升子序列
刷题人生
CSDN刷题
剑指offer
剑指offer全集
书籍推荐
各种数据结构算法视频集
博客推荐 July
编程之法 面试和算法心得:kvol
程序员的真谛:多看、多思考、Keep Learning
设计模式
设计模式的六大原则
总原则:开闭原则(Open Close Principle)就是说对扩展开放,对修改关闭
图解设计模式
小争哥 极客时间
1、单一职责原则:每个类应该实现单一的职责
2、里氏替换原则(Liskov Substitution Principle): 子类型必须能够替换掉它们的父类型
3、依赖倒转原则(Dependence Inversion Principle): 开闭原则的基础,面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
4、接口隔离原则(Interface Segregation Principle):接口尽量细化,同时接口中的方法尽量少。每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。
5、迪米特法则(最少知道原则)(Demeter Principle):高内聚,低耦合。 一个类对自己依赖的类知道的越少越好;一个对象应该对其他对象有最少的了解。
6、合成复用原则(Composite Reuse Principle):尽量首先使用合成/聚合的方式,而不是使用继承
2、里氏替换原则(Liskov Substitution Principle): 子类型必须能够替换掉它们的父类型
3、依赖倒转原则(Dependence Inversion Principle): 开闭原则的基础,面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
4、接口隔离原则(Interface Segregation Principle):接口尽量细化,同时接口中的方法尽量少。每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。
5、迪米特法则(最少知道原则)(Demeter Principle):高内聚,低耦合。 一个类对自己依赖的类知道的越少越好;一个对象应该对其他对象有最少的了解。
6、合成复用原则(Composite Reuse Principle):尽量首先使用合成/聚合的方式,而不是使用继承
三大类
创建型模式
结构型模式
行为型模式
DDD
领域 驱动 设计 Domain Driven Design
只是一种理念,关键是考虑微服务的边界,分层及架构演进
容器
Docker
Docker技术入门与实战离线版
学习内容
基本内容
Docker入门
由来:Docker使用Go语言开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离, 属于操作系统层面的虚拟化技术
优势
1、更高效的利用系统资源:容器不需要进行硬件虚拟跟运行完整OS,Docker的运行速度,内存损耗,文件存储都比传统好。
2、更快速的启动时间:传统VM启动数分钟,现在Docker直接运行在宿主内存,毫秒启动。
3、一直的运行环境:开发、测试、生成 环境一直性问题。防止测试跟开发扯皮。
4、持续交付和部署:对开发部署(DevOps)来说 可以DockerFile来构建镜像。
2、更快速的启动时间:传统VM启动数分钟,现在Docker直接运行在宿主内存,毫秒启动。
3、一直的运行环境:开发、测试、生成 环境一直性问题。防止测试跟开发扯皮。
4、持续交付和部署:对开发部署(DevOps)来说 可以DockerFile来构建镜像。
Docker基本概念
镜像(image),容器Container,仓库Repository
指令集
Docker Engine组成
Docker 进程(Docker Daemon)
REST API:指定了和进程交互的接口
CLI(command line interface):通过 REST API 和 daemon 通信,诸如:docker run <image>, docker ps...
REST API:指定了和进程交互的接口
CLI(command line interface):通过 REST API 和 daemon 通信,诸如:docker run <image>, docker ps...
Docker Machine
Docker Machine 是一种提供管理主机的 工具。常规,你会安装 Docker Machine 在你的本地机器上。
Docker Machine 有自己的命令client:docker-machine
Docker Engine 则有client:docker
我们可以使用 Docker Machine 来安装 Docker Engine 在一个或者多个虚拟系统上,这些虚拟系统可以是本地的(比如Virtualbox里),也可以是远程的(云)。
Docker Machine 有自己的命令client:docker-machine
Docker Engine 则有client:docker
我们可以使用 Docker Machine 来安装 Docker Engine 在一个或者多个虚拟系统上,这些虚拟系统可以是本地的(比如Virtualbox里),也可以是远程的(云)。
官网安装手册
Docker 镜像
日常镜像的增删
镜像是什么:镜像是一种轻量级、可执行的独立软件保,用来打包软件运行环境和基于运行环境开发的软件,他包含
运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。
运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。
镜像加载原理
UnionFs (联合文件系统)
分层、轻量级并且高性能的文件系统,层层叠加配合重用 实现高效
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加载 kernel
rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目
录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。
录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层层的在下载 最大的好处,莫过于资源共享,可以通过 docker image inspect redis 查看Redis需要哪些层。整体来说就是千层饼 不断叠加
特点:Docker 镜像都是只读的,启动容器后在镜像上增加一个可写层
save、load、导入导出镜像,export、import导出容器导成镜像
图形化Ui
portainer
dockerui
hipyard
进阶
容器数据卷
什么是容器数据卷:Docker容器中产生的数据,同步到本地!这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!
容器的持久化和同步操作!容器间也是可以数据共享的!
使用容器卷volume
直接使用命令来挂载
docker run -it -v 主机目录 : 容器目录
以通过docker inspect 容器id 来查看挂载情况 mounts
匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
查看所有卷的情况 docker volume ls 可看到若干匿名挂载好的卷
具名挂载
-v 卷名:容器内路径
docker run -d -P --name nginx02 -v juming- nginx:/etc/nginx nginx
先找到卷所在路径 docker volume inspect 卷名
Dockerfile 里指定挂载
VOLUME ["volume01","volume02"] # 也是匿名挂载
容器之间的配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!
docker run -it --name docker01 sw/centos
docker run -it --name docker02 --volumes-from docker01 sw/centos
docker run -it --name docker03 --volumes-from docker01 sw/centos
实现 多个容器之间共享数据卷
docker run -it --name docker02 --volumes-from docker01 sw/centos
docker run -it --name docker03 --volumes-from docker01 sw/centos
实现 多个容器之间共享数据卷
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxx/_data下
扩展
-v 容器内路径:ro 或 rw 改变读写权限 ro : readonly 只读 rw #readwrite 可读可写,默认是 rw
只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
一旦创建容器时设置了容器权限,容器对我们挂载出来的内容就有限定了
docker run -d -P --name nginx05 -v juming:/etc/nginx:ro nginx
docker run -d -P --name nginx05 -v juming:/etc/nginx:rw nginx
docker run -d -P --name nginx05 -v juming:/etc/nginx:rw nginx
DockerFile
镜像的定制就是规定每一层添加配置文件,如果我们把每一次修改安装构建操作写入一个脚本来透明性构建即可。
docker build -t nginx:v2 . 镜像构建上下文
1、每个保留关键字(指令)都是必须是大写字母
2、执行从上到下顺序
3、# 表示注释
4、每一个指令都会创建提交一个新的镜像曾,并提交!
2、执行从上到下顺序
3、# 表示注释
4、每一个指令都会创建提交一个新的镜像曾,并提交!
RUN,CMD,ENTRYPOINT 区别
若干构建命令
build后
Docker网络
默认 bridge模式
1、宿主机只要安装了docker就可以通过 ip addr 查看到docker0这个默认网卡,该网卡就是专门用于宿主机之间通信的。
2、当我们启动一个容器时,从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中
3、bridge模式是docker的默认网络模式,不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能
2、当我们启动一个容器时,从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中
3、bridge模式是docker的默认网络模式,不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能
bridge
Docker中所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件),只要容器一删除,对应的一对网桥就没有!
docker 启动时候 默认 自带 --net bridge,bridge=docker0
结论:
所有的容器不指定网络的情况下,都是经 docker0 路由的,docker 会给我们的容器分配一个默认的可
用ip
用ip
Docker使用的是Linux的桥接技术,宿主机是一个Docker容器的网桥 docker0
docker0问题:不支持容器名连接访问!
--link
问题:编写了一个微服务,database url = ip ,项目不重启,数据库ip换掉了,希望可以通过名字来访问容器,如何解决
默认情况下 docker exec -it container1 ping container2 是搞不定的,只能通过IP地址
docker network ls,docker network inspect id 查看网络配置
运行容器时候 添加 --link containerName,相当于在运行容器/etc/hosts文件中添加了映射。所以才可以实现直接ping 名字。
运行容器时候 添加 --link containerName,相当于在运行容器/etc/hosts文件中添加了映射。所以才可以实现直接ping 名字。
--link用的也是docker0自带的网络机制,一般很少用实际工作中
自定义网络
查看所有的docker网络 docker network ls
网络模式
自定义网络:docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
查看网络配置:docker network inspect mynet
启动容器1:docker run -d -P --name tomcat-net-01 --net mynet tomcat 自动分配的 IP=192.168.0.2
启动容器2:docker run -d -P --name tomcat-net-02 --net mynet tomcat 自动分配的 IP=192.168.0.3
查看网络配置:docker network inspect mynet
测试容器互ping:docker exec -it tomcat-net-01 ping 192.168.0.3,docker exec -it tomcat-net-01 ping tomcat-net-02 都OK。
查看网络配置:docker network inspect mynet
启动容器1:docker run -d -P --name tomcat-net-01 --net mynet tomcat 自动分配的 IP=192.168.0.2
启动容器2:docker run -d -P --name tomcat-net-02 --net mynet tomcat 自动分配的 IP=192.168.0.3
查看网络配置:docker network inspect mynet
测试容器互ping:docker exec -it tomcat-net-01 ping 192.168.0.3,docker exec -it tomcat-net-01 ping tomcat-net-02 都OK。
--driver bridge 网络模式定义为 :桥接
--subnet 192.168.0.0/16 定义子网 ,范围为:192.168.0.2 ~ 192.168.255.255
--gateway 192.168.0.1 子网网关设为: 192.168.0.1
--subnet 192.168.0.0/16 定义子网 ,范围为:192.168.0.2 ~ 192.168.255.255
--gateway 192.168.0.1 子网网关设为: 192.168.0.1
在使用自定义的网络时,docker都已经帮我们维护好了对应关系,推荐我们平时这样使用网络!!
好处
redis——不同的集群使用不同的网络,保证了集群的安全和健康
mysql——不同的集群使用不同的网络,保证了集群的安全和健康
mysql——不同的集群使用不同的网络,保证了集群的安全和健康
网络连通
用docker0运行的容器如何跟 用自定义网络的容器 ping
docker exec -it docker0Container ping mynetContainer 无法ping通
解决办法
docker exec -it docker0Container ping mynetContainer 无法ping通
测试打通:docker network connect mynet docker0Container。这里的意思是将docker0Container放到了mynet网络下。docker0Container有两个IP地址,参考阿里云共有IP跟私有IP。
docker network inspect mynet 可以看到 docker0Container被包含到该网络组里了。再执行 docker exec -it docker0Container ping mynetContainer 成功。
测试打通:docker network connect mynet docker0Container。这里的意思是将docker0Container放到了mynet网络下。docker0Container有两个IP地址,参考阿里云共有IP跟私有IP。
docker network inspect mynet 可以看到 docker0Container被包含到该网络组里了。再执行 docker exec -it docker0Container ping mynetContainer 成功。
结论:假设要跨网络操作别人,就需要使用docker network connect 连通功能。
SpringBoot + Docker 部署
简单的*.jar 配合 dockerfile 即可直接交付生产
Docker Compose
定义:
Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容器 进行管理
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
Compose 需单独安装
docker-compose.yml 配置文件编写详解
network配置
dockerfile 网络设置过度到compose
关键三个步骤
使用 Dockerfile 定义应用程序的环境。
使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
最后,执行 docker-compose up 命令来启动并运行整个应用程序。docker-compose down停止应用程序
使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
最后,执行 docker-compose up 命令来启动并运行整个应用程序。docker-compose down停止应用程序
烦人的docker 网络 在compose中自动解决
compose demo wordpress
Docker Swarm
定义:
Swarm是Docker公司推出的用来管理docker集群的平台,几乎全部用GO语言来完成的开发的,它是将一群Docker宿主机变成一个单一的虚拟主机,Swarm使用标准的Docker API接口作为其前端的访问入口
Raft协议
搭建集群、动态管理容器、启动服务
子主题
K8s
kubernetes,是Google多年大规模容器管理技术的开源版本,是众多厂商推崇的docker管理优秀之作,可以认为是docker swarm的 升级版,偏向运维了
常见面试题
工程
秒杀系统如何设计
a、CDN:静态数据缓存
b、缓存:上层抗住流量
c、限流:防止应用挂掉
d、答题验证码:削峰
e、MQ:异步/削峰/解耦
f、风控、接口幂等:防刷
g、分库分表:减轻 DB 压力
b、缓存:上层抗住流量
c、限流:防止应用挂掉
d、答题验证码:削峰
e、MQ:异步/削峰/解耦
f、风控、接口幂等:防刷
g、分库分表:减轻 DB 压力
服务器CPU 100% 排查
线上 OOM 的排查方案?
Java2020面试总结
后端技术栈了解
生产者 + 消费者
生产者
消费者
调用者
CPU 使用过高如何排查
1. top -c 显示进程列表
2. 键入大写P 按照CPU使用率看进程, 比如找到了进程PID= 10765
3. top -Hp 10765 显示进程中线程列表
4. 键入大写P 线程按照CPU使用率排序,比如找到线程PID = 10804
5. 将进程PID 10804 转化为16进制 printf "%x" 10804 2a34
6. jstack 10765 | grep '0x2a34' -C9 # -C是匹配行和它前后各n行
7. 找到 耗CPU高的线程对应线程名字 以及在干什么
2. 键入大写P 按照CPU使用率看进程, 比如找到了进程PID= 10765
3. top -Hp 10765 显示进程中线程列表
4. 键入大写P 线程按照CPU使用率排序,比如找到线程PID = 10804
5. 将进程PID 10804 转化为16进制 printf "%x" 10804 2a34
6. jstack 10765 | grep '0x2a34' -C9 # -C是匹配行和它前后各n行
7. 找到 耗CPU高的线程对应线程名字 以及在干什么
短链路设计
高并发的理解
高性能:并行处理能力,1秒跟0.1秒的响应。
手段:集群部署,缓存CDN跟Redis,分库分表,TiDB ,降低IO,MQ,限流,HTTP池化跟线程池,JVM优化。
高可用:系统可以正常服务的时间,3个9 还是4个9,全面。容灾,灰度发布。
手段:Nginx/F5负载均衡,服务降级保证核心,进行熔断,心跳检测,接口幂等性,CPU/磁盘/网络/JVM/数据库监控体系。
高扩展: 系统的动态扩展能力,能否平稳处理高峰流量。扩展一倍设备系统性能提升多少? 服务集群、数据库、缓存和消息队列等中间件、负载均衡、带宽、依赖的第三方等。
手段:合理的业务分层,数据存储拆分,按照商品服务跟订单服务拆分,按照核心跟非核心拆分,请求资源TO C 还是 TO B,
通用纵向扩展 单机:增大内存,CPU,磁盘,固态硬盘,减少IO,并发,异步操作。
通用横向扩展 做好分层架构,读写分离,主从同步,负载均衡,CDN,统一网关。
通用横向扩展 做好分层架构,读写分离,主从同步,负载均衡,CDN,统一网关。
Slf4j、Logback、Log4j啥关系
海量数据去重
1. 分治思维,大数据分成若干小文件,小文件去重然后合并。
2. 使用Java的 HashSet,Redis中的Set去重,MySQL唯一索引去重,
3. Guava 布隆过滤器去重,Redis中的HapLogLog
2. 使用Java的 HashSet,Redis中的Set去重,MySQL唯一索引去重,
3. Guava 布隆过滤器去重,Redis中的HapLogLog
算法
两个线程交替打印奇偶数
工具
IDEA
IDEA教程
IDEA必备插件
IDEA破解
IDEA配置迁移新电脑
Maven
Scope概念
Maven plugin中的lifecycle、phase、goal、mojo概念及作用的理解
1. Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
2. Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。
3. Site Lifecycle 生成项目报告,站点,发布站点。
2. Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。
3. Site Lifecycle 生成项目报告,站点,发布站点。
Gradle
Nginx
俄罗斯人编写,优点
第一:它可以支持5W高并发连接
第二:内存消耗少
第三:相比F5等硬件设备,Nginx免费开源。
第二:内存消耗少
第三:相比F5等硬件设备,Nginx免费开源。
使用场景
docker + nginx 快速配置使用
1、反向代理
理解正向代理跟反向代理的区别:正向代理代理的对象是客户端,反向代理代理的对象是服务端
2、负载均衡 Load Balance
ABC三台机器提供相同对外服务形成集群,Nginx是这个集群的代理,将用户请求通过制定算法分发到具体的机器上,最终实现Load Balance
1、轮询:轮询方式是Nginx负载默认的方式
2、权重:指定每个服务的权重比例,weight和访问比率成正比
3、IPHash:根据访问ip的hash结果分配
4、least_conn 最少链接:将请求分配到连接数最少的服务上
5、fair(第三方) 响应时间:按后端服务器的响应时间来分配请求,响应时间短的优先分配。
3、缓存配置
1、缓存文件放在哪儿?
2、如何指定哪些请求被缓存?
3、缓存的有效期是多久?
4、如何指定哪些请求不被缓存?
2、如何指定哪些请求被缓存?
3、缓存的有效期是多久?
4、如何指定哪些请求不被缓存?
4、静态资源服务
静态资源: html、js、css、图片、音乐、视频等。
动态资源: 我的理解就是我们所说的接口
动态资源: 我的理解就是我们所说的接口
实现动静分离的两种方法: 通过不同域名来拦截和location来拦截。
不同域名来拦截: 用大白话来说就是,动态请求和静态请求使用不同的域名。比如所有的静态资源都使用static.tuesdayma.com域名来访问,所有的接口都使用www.tuesdayma.com来请求。
5、Keealived + nginx 实现高可用
6、Nginx 解决跨域问题
啥是跨域
指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制
跨域形象说:域名,协议,端口均相同
如何解决
子主题
Nginx 为什么快
apache + Tomcat 使用了C/C++ 编写的 select
select/poll模式 :饭店服务员(内核)告诉饭店老板(用户程序):”现在有客人结账“ 但是这个服务员没人明确告诉老板,哪几桌的客人结帐。老板得自儿一个一个桌子去问:请问是你要结帐?
跨平台的Web服务器软件
nginx使用了C 编写的 epoll
epoll是基于JAVA NIO的同步非阻塞开发,在高并发情况下能支持更多的连接!nginx是事件驱动的,一个主进程跟多个工作进程组成的工作模式,主线程负责循环分配事件,多个工作线程负责事件的处理!
epoll模式:饭店服务员(内核)告诉饭店老板(用户程序):”1,2,5号客人结账“ 老板就可以直接去1,2,5号桌收钱了
穿透内网工具
目的:让别人可以通过网络访问你本地部署的web程序
ngrok
花生壳
绘图工具
http://le5le.com/
processon
Xmind
draw.io
收藏
收藏
0 条评论
下一页