大厂资深架构师专家技术树(11w字)强烈建议下载本地编辑
2022-08-26 13:24:57 2 举报
AI智能生成
大厂资深架构师专家技术树(11w字)
作者其他创作
大纲/内容
分别以统计量的置信上限和置信下限为上下界构成的区间
置信区间
区间估计
Α α:阿尔法 Alpha2
Β β:贝塔 Beta3
Γ γ:伽玛 Gamma4
Δ δ:德尔塔 Delte5
Ε ε:艾普西龙 Epsilon6
Ζ ζ :捷塔 Zeta7
Ε η:依塔 Eta8
Θ θ:西塔 Theta9
Ι ι:艾欧塔 Iota10
Κ κ:喀帕 Kappa11
∧ λ:拉姆达 Lambda12
Μ μ:缪 Mu13
Ν ν:拗 Nu14
Ξ ξ:克西 Xi15
Ο ο:欧麦克轮 Omicron16
∏ π:派 Pi17
Ρ ρ:柔 Rho18
∑ σ:西格玛 Sigma19
Τ τ:套 Tau20
Υ υ:宇普西龙 Upsilon21
Φ φ:fai Phi22
Ψ ψ:普赛 Psi24
Ω ω:欧米伽 Omega
变量
古典概率
概率分布
"逆概"问题
自然语言的二义性
子主题 3
贝叶斯
假设检验
联合概率分布
概率论
矩阵和向量
加法和标量乘法
矩阵向量乘法
矩阵乘法
矩阵乘法特征
逆和转置
矩阵的特征值与特征向量
线性代数
极限与积分
导数和二阶导数
方向导数与梯度
凸函数与极值
最优化方法
微积分
统计学
是只有大小,没有方向的量,如1,2,3等
0 维度张量(Tensor)
标量(Scalar)
向量(Vector)
矩阵(Matrix)
张量(Tensor)
量
连续傅里叶变换&分数傅里叶变换
将一个用系数表示的多项式转换成它的点值表示的算法
概念
如果模型足够平滑,它们会得到光谱收敛,这意味着误差呈指数递减
赫尔德条件
(快速)傅里叶变换(FFT)可用于从均匀间隔的数据中快速计算傅里叶级数
快速傅里叶变换(FFT)
离散傅里叶变换(DFT)
切比雪夫变换 / 切比雪夫多项式
从时域到频域的变化
拉普拉斯
无限数量的正弦曲线的组合从能量的角度可以非常无限逼近带有棱角的信号。
正弦曲线无法组合成一个带有棱角的信号
拉格朗日
这些正弦波的频率符合一个规律:是某个频率的整数倍。这个频率,就称为基波频率
基波频率
其它频率称为谐波频率
谐波的频率是基波频率的N倍,就称为N次谐波
直流分量的频率为零,是基波频率的零倍,也可称零次谐波
谐波频率
任意周期信号可以分解为直流分量和一组不同幅值、频率、相位的正弦波。分解的方法就是傅里叶变换。
电参量测量
任何连续周期信号可以由一组适当的正弦曲线组合而成
通过示波器观测实时波形获取,称为时域分析法
傅里叶变换是一种信号分析方法,让我们对信号的构成和特点进行深入的、定量的研究。把信号通过频谱的方式(包括幅值谱、相位谱和功率谱)进行准确的、定量的描述。
正弦波输入至线性系统,不会产生新的频率成分(非线性系统如变频器,就会产生新的频率成分,称为谐波)
STFT在傅里叶变换的基础上加窗,分段做FFT变换
短时傅里叶变换STFT
傅里叶变换
换基
小波变换
函数逼近
函数论
数学
先序
中序
后序
public ArrayList<ArrayList<Integer>> levelOrder (TreeNode root) { ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); if(root != null){ Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while(!queue.isEmpty()){ int size = queue.size(); ArrayList<Integer> newLevel = new ArrayList<Integer>(); for(int i=0; i<size; i++){ TreeNode temp = queue.poll(); newLevel.add(temp.val); if(temp.left != null) queue.offer(temp.left); if(temp.right != null) queue.offer(temp.right); } result.add(newLevel); } } return result; }
二叉树层序遍历
层序
遍历
翻转
二叉树
红黑树是一棵平衡二叉搜索树,其中序遍历单调不减
节点是红色或黑色。
根节点是黑色。
每个叶节点(也有称外部节点的,目的是将红黑树变为真二叉树,即NULL节点,空节点)是黑色的。
每个红色节点的两个子节点都是黑色。(换句话说,从每个叶子到根的所有路径上不能有两个连续的红色节点)
从根节点到每个叶子的所有路径都包含相同数目的黑色节点(这个数值叫做黑高度)。
红黑树
B-树
b+树
b树
利用顺序写来提高写性能,但因为分层(此处分层是指的分为内存和文件两部分)的设计会稍微降低读性能,但是通过牺牲小部分读性能换来高性能写,使得LSM树成为非常流行的存储结构。
LSM树(Log-Structured-Merge-Tree)正如它的名字一样,LSM树会将所有的数据插入、修改、删除等操作记录(注意是操作记录)保存在内存之中,当此类操作达到一定的数据量后,再批量地顺序写入到磁盘当中。
内存中的数据结构,用于保存最近更新的数据,会按照Key有序地组织这些数据,
MemTable
Immutable MemTable
有序键值对集合,是LSM树组在磁盘中的数据结构。为了加快SSTable的读取,可以通过建立key的索引以及布隆过滤器来加快key的查找。
SSTable(Sorted String Table)
LSM树
树
数组
链表
添加 add会抛出异常
offer,add
删除对头元素
poll,remove
查询头部元素
peek,element
Queue
队列
栈
堆
邻接表无法快速判断两个节点是否相邻
邻接表
邻接矩阵
存储
图
数据结构
归纳演绎法
插入排序
选择排序
希尔排序
快速排序
归并排序
堆排序
排序算法
多叉树的延伸
广度优先
全排列
N皇后
问题
路径:也就是已经做出的选择。
选择列表:也就是你当前可以做的选择。
结束条件:也就是到达决策树底层,无法再做选择的条件。
解决一个回溯问题,实际上就是一个决策树的遍历过程
回溯算法
深度优先
弗洛伊德算法(多源最短路径)
迪杰斯特拉算法
Bellman-Ford算法(解决负权边)
图的最短路径
找到一个岛屿、用海水淹没他
岛屿问题
图算法
暴力算法BF算法
哈希值进行比较的RK算法
最长可匹配后缀子串
最长可匹配前缀子串
数组的下标代表了“已匹配前缀的下一个位置“
元素的值则是“最长可匹配前缀子串的下一个位置”
next数组
KMP
字符串匹配
动态规划的穷举有点特别,因为这类问题存在「重叠子问题」,如果暴力穷举的话效率会极其低下,所以需要「备忘录」或者「DP table」来优化穷举过程,避免不必要的计算
动态规划的核心问题是穷举
通项公式
递推公式
备忘录
DP table
状态转移方程
重叠子问题
最优子结构
三要素
斐波那契数列
凑零钱问题
最长递增子序列
最小编辑距离
01背包
背包9讲
动态规划
朴素的算法
解决分布式系统中负载均衡的问题,使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡的作用。
伸缩性很差
新增或者下线服务器机器时候,用户id与服务器的映射关系会大量失效
余数hash
各个服务器使用Hash进行一个哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置
数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。
hash环
虚拟节点机制
数据倾斜
一致性hash
分布式算法
对称
加密算法
输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
一个MD5理论上的确是可能对应无数多个原文的,因为MD5是有限多个的而原文可以是无数多个
3.4*10^38
MD5目前最有效的攻击方式就是彩虹表
不同的输入得到的不同的结果(唯一性);
超损压缩
MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
MD5
把3个8 bit 的二进制数转换成4个6字节的二进制数,而这4个6字节数,都是可见打印字符
BASE64
编码
public ListNode ReverseList(ListNode head) { ListNode pre = null; ListNode cur = head; ListNode next = null; while(cur!=null) { next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; }
反转链表(单链表)
设计LRU缓存结构
最长无重复数组
public String solve (String str) { char[] ans = str.toCharArray(); int len = str.length(); for(int i = 0 ; i < len ;i++) { ans[i] = str.charAt(len-1-i); } return new String(ans); }
翻转字符串
public boolean hasCycle(ListNode head) { if (head == null) return false; //快慢两个指针 ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; } return false; }
判断链表是否有环
环形链表
合并两个有序链表
删除链表的倒数第N节点
相交链表
链表的中间节点
双指针
技巧
算法
工业相机
单反
桶形畸变
枕型畸变
线性畸变
畸变
相机
1+1=2 加法器
先行进位&并行进位
电控开关
计算机系统底层架构
实时时钟 RTC ,用于长时间存放系统时间的设备,即使关机也可以依靠主板中的电池继续计时。Linux 启动的时候会从 RTC 中读取时间和日期作为初始值,之后在运行期间通过其他计时器去维护系统时间。
可编程间隔定时器 PIT ,该计数器会有一个初始值,每过一个时钟周期,该初始值会减1,当该初始值被减到0时,就通过导线向 CPU 发送一个时钟中断, CPU 就可以执行对应的中断程序,也就是回调对应的任务
时间戳计数器 TSC , 所有的 Intel8086 CPU 中都包含一个时间戳计数器对应的寄存器,该寄存器的值会在每次 CPU 收到一个时钟周期的中断信号后就会加 1 。他比 PIT 精度高,但是不能编程,只能读取。
硬件计时器在多长时间内产生时钟脉冲,而时钟周期频率为1秒内产生时钟脉冲的个数。目前通常为1193180。
时钟周期
当PIT中的初始值减到0的时候,就会产生一次时钟中断,这个初始值由编程的时候指定。
时钟滴答
时钟的实现
对称多处理器结构
这样的架构对软件层面来说非常容易,总线模型保证所有的内存访问是一致的,即每个处理器核心共享相同的内存地址空间
UMA(Uniform Memory Access),直译为“统一内存访问”
对称多处理器结构(SMP:Symmetric Multi-Processor)
不同的内存器件和CPU核心从属不同的 Node,每个 Node 都有自己的集成内存控制器(IMC,Integrated Memory Controller)。
不同的 Node 间通过QPI(Quick Path Interconnect)进行通信
QPI的延迟要高于IMC Bus,也就是说CPU访问内存有了远近(remote/local)之别,而且实验分析来看,这个差别非常明显。
非一致存储访问结构(NUMA:Non-Uniform Memory Access)
海量并行处理结构(MPP:Massive Parallel Processing)。
服务器
均匀存储器存取(Uniform-Memory-Access,简称UMA)模型
非均匀存储器存取(Nonuniform-Memory-Access,简称NUMA)模型
存储器共享方式
ARM公司: arm架构
intel公司: intelx86系列架构
AMD公司: amdx86系列架构
品牌
架构
提供了8个64bit的寄存器进行SIMD操作
通过复用CPU内部x87浮点单元的寄存器来实现SIMD的,所以与运行浮点运算的x87指令集有冲突,两者不能交叉使用,必须先进行切换
只支持整数操作
MMX(Multi Media eXtension,多媒体扩展指令集)
SSE系列提供了8个128bit的寄存器进行SIMD操作
引入新的独立寄存器解决了与浮点运算间的冲突问题
SSE(Stream SIMD Extentions,数据流单指令多数据扩展)
Intel AVX指令集,在单指令多数据流计算性能增强的同时也沿用了的MMX/SSE指令集。不过和MMX/SSE的不同点在于增强的AVX指令,从指令的格式上就发生了很大的变化。x86(IA-32/Intel 64)架构的基础上增加了prefix(Prefix),所以实现了新的命令,也使更加复杂的指令得以实现,从而提升了x86 CPU的性能。
AVX指令集
AVX2
512-bit
AVX512
SIMD(Single Instruction Multiple Data)是单指令多数据技术
SIMD
指令指针寄存器
寄存器
存放指令
指令寄存器(IR )
从主存获取操作数放入高速缓存L1
操作用户程序 数据段
指令译码器( ID )
从用户程序取指令,放入寄存器
操作控制器( OC )
控制器
运算器
组成
内存屏障(Memory Barrier)
该变量超过一个缓存行的大小,缓存一致性协议是针对单个缓存行进行加锁,此时,缓存一致性协议无法再对该变量进行加锁,只能改用总线加锁的方式。
1、CPU1从内存中将变量a加载到缓存中,并将变量a的状态改为E(独享),并通过总线嗅探机制对内存中变量a的操作进行嗅探
2、此时,CPU2读取变量a,总线嗅探机制会将CPU1中的变量a的状态置为S(共享),并将变量a加载到CPU2的缓存中,状态为S
3、CPU1对变量a进行修改操作,此时CPU1中的变量a会被置为M(修改)状态,而CPU2中的变量a会被通知,改为I(无效)状态,此时CPU2中的变量a做的任何修改都不会被写回内存中(高并发情况下可能出现两个CPU同时修改变量a,并同时向总线发出将各自的缓存行更改为M状态的情况,此时总线会采用相应的裁决机制进行裁决,将其中一个置为M状态,另一个置为I状态,且I状态的缓存行修改无效)
4、CPU1将修改后的数据写回内存,并将变量a置为E(独占)状态
5、此时,CPU2通过总线嗅探机制得知变量a已被修改,会重新去内存中加载变量a,同时CPU1和CPU2中的变量a都改为S状态
modified(修改)、exclusive(互斥)、share(共享)、invalid(无效)
Intel 的MESI协议
缓存一致性协议
总线
L1 data cache(DL1)
L1 instruction cache(IL1)
32KB
L1
256KB
L2
3MB
L3
1B(字节)=8b(位)
这个缓存行可以被许多线程访问。如果其中一个修改了v2,那么会导致Thread1和Thread2都会重新加载整个缓存行。你可能会疑惑为什么修改了v2会导致Thread1和Thread2重新加载该缓存行,毕竟只是修改了v2的值啊。虽然说这些修改逻辑上是互相独立的,但同一缓存行上的数据是统一维护的,一致性的粒度并非体现在单个元素上。这种不必要的数据共享就称之为“伪共享”(False Sharing)。
伪共享”(False Sharing)
多级缓存
其中段基地址在程序启动的时候确认,尽管这个段基地址还是绝对的物理地址,但终究可以同时运行多个程序了, CPU 采用这种方式访问内存,就需要段基址寄存器和段内偏移地址寄存器来存储地址,最终将两个地址相加送上地址总线。
伟大的计算机前辈设计出,让 CPU 采用 段基址 + 段内偏移地址 的方式访问内存
连续的地址空间
内存分页
内存分段,相当于每个进程都会分配一个内存段,而且这个内存段需要是一块连续的空间,主存里维护着多个内存段,当某个进程需要更多内存,并且超出物理内存的时候,就需要将某个不常用的内存段换到硬盘上,等有充足内存的时候在从硬盘加载进来,也就是 swap 。每次交换都需要操作整个段的数据。
内存分段
虚拟存储
正常情况下,我们读取文件的流程为,系统调用从磁盘读取数据,存入操作系统的内核缓冲区,然后在从内核缓冲区拷贝到用户空间
内存映射,是将磁盘文件直接映射到用户的虚拟存储空间中,通过页表维护虚拟地址到磁盘的映射,通过内存映射的方式读取文件的好处有,因为减少了从内核缓冲区到用户空间的拷贝,直接从磁盘读取数据到内存,减少了系统调用的开销,对用户而言,仿佛直接操作的磁盘上的文件,另外由于使用了虚拟存储,所以不需要连续的主存空间来存储数据。
内存映射
当程序被加载进内存后,指令就在内存中了,这个时候说的内存是独立于 CPU 外的主存设备,也就是 PC 机中的内存条,指令指针寄存器IP 指向内存中下一条待执行指令的地址,控制单元根据 IP寄存器的指向,将主存中的指令装载到指令寄存器。
这个指令寄存器也是一个存储设备,不过他集成在 CPU 内部,指令从主存到达 CPU 后只是一串 010101 的二进制串,还需要通过译码器解码,分析出操作码是什么,操作数在哪,之后就是具体的运算单元进行算术运算(加减乘除),逻辑运算(比较,位移)。而 CPU 指令执行过程大致为:取址(去主存获取指令放到寄存器),译码(从主存获取操作数放入高速缓存 L1 ),执行(运算)。
程序运行
cpu
NVIDIA推出的运算平台
它包含了CUDA指令集架构(ISA)以及GPU内部的并行计算引擎
CUDA
GPU
功能强大的CPU
MPU (Micro Processor Unit),叫微处理器(不是微控制器)
计算机的CPU、RAM、ROM、定时计数器和多种I/O接口集成在一片芯片上,形成芯片级的芯片
MCU(Micro Control Unit)
组成原理
树莓派
计算机
硬件
业务逻辑问题(死循环)
jstat -gc pid 1000命令来对 gc 分代变化情况进行观察
频繁 gc
cs(context switch)一列则代表了上下文切换的次数。
vmstat 1
使用vmstat命令
pidstat -w pid命令,cswch 和 nvcswch 表示自愿及非自愿切换。
对特定的 pid 进行监控
上下文切换过多
top -H -p pid
找到 CPU 使用率比较高的一些线程
printf '%x\'
然后将占用最高的 pid 转换为 16 进制
jstack pid |grep 'nid' -C5 –color
jstack 中找到相应的堆栈信息
关注 WAITING 、TIMED_WAITING、BLOCKED
方案
CPU
df -hl
查看文件系统状态
最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及wrqm/s分别表示读写速度
iostatiostat -d -k -x
磁盘性能
拿到的是 tid,我们要转换成 pid,可以通过 readlink 来找到 pidreadlink -f /proc/*/task/tid/../..。
iotop
cat /proc/pid/io
定位文件读写的来源
lsof -p pid
具体的文件读写情况
磁盘
比如说每次请求都 new 对象,导致大量重复创建对象;进行文件流操作但未正确关闭;手动不当触发 gc;ByteBuffer 缓存分配不合理等都会造成代码 OOM。
线程池代码写的有问题,比如说忘记 shutdown
JVM 方面可以通过指定Xss来减少单个 thread stack 的大小
可以在系统层面,可以通过修改/etc/security/limits.confnofile 和 nproc 来增大 os 对线程的限制
没有足够的内存空间给线程分配 Java 栈
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
jstack+jmap
堆的内存占用已经达到-Xmx 设置的最大值
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
元数据区的内存占用已经达到XX:MaxMetaspaceSize设置的最大值
Caused by: java.lang.OutOfMemoryError: Meta space
-XX:+HeapDumpOnOutOfMemoryError来保存 OOM 时的 dump 文件。
OOM
表示线程栈需要的内存大于 Xss 值,
Exception in thread "main" java.lang.StackOverflowError
Stack Overflo
youngGC 或者 fullGC 次数是不是太多;EU、OU 等指标增长是不是异常。
使用 jstat 来查看分代变化情况
通过pstreee -p pid |wc -l。
/proc/pid/task
看下总体线程
GC 问题
往往是和 NIO
OutOfMemoryError: Direct buffer memory。
DirectByteBuffer
OutOfDirectMemoryError
查看对应 pid 倒序前 30 大的内存段
pmap -x pid | sort -rn -k3 | head -30
pmap 来查看下进程占用的内存
gdb --batch --pid {pid} -ex "dump memory filename.dump {内存起始地址} {内存起始地址+内存块大小}"
gdb
堆外内存
内存
网络
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。
火焰图
线上调优
grep 'core id' /proc/cpuinfo |wc -l 查看核心数
cpu过高
查看资源占用过高的线程
top -Hp
top 查看附载
系统查看
tar -zxvf 解压
:set nu 显示行号
vim
ln -s 软链接
检查当前VPS的数据硬盘情况
fdisk -l
挂载盘
mount /dev/vdb1 /mnt/data
scp -r test root@107.172.27.254:/home 上传目录
SSH
命令
fd
所有的文件都是通过文件描述符引用
文件描述符
文件的打开
int close(int fd) 关闭一个文件会释放上面所有的记录锁。一个进程终止后,内核会自动关闭它打开的所有文件。
文件的关闭
文件定位
空洞文件
文件写入
文件读取
文件属性编辑
文件内核API
进程中的文件表,记录着进程打开的所有文件。每个文件用了一个fd标志和一个文件指针(指向2)表示。
内核为每个进程打开的每个文件创建了一个文件表,包含了文件状态标志、文件偏移量和i-node指针。需要注意不同进程打开了不同文件的时候,会有两个文件表。所以并发读取是安全的。
每个文件都对应一个i-node。这个node里包含了文件的磁盘块位置、文件的长度、拥有者、权限等。这些信息会被读入内存。
多线程读取
磁盘划分为数据区和i-node区,一般每4个块(一个块为4K,8个扇区)就会有一个i-node,占地256字节。每创建一个文件,系统就分配一个i-node给文件,并把文件名和i-node编号关联起来。查找的时候,根据文件名找到编号,再找到文件的磁盘位置。
每次write完毕,内核文件表2中的文件偏移量都会增加写入的字节数。如果此时文件的偏移量大于i-node记录的文件长度,i-node记录会被更新为这个偏移量(文件变长)。
O_APPEND会被写内核文件表2中的文件状态标志中。这样,每次write发现这个标志,都会先把文件偏移量设置为i-node的文件长度,这样就可以写到尾端。
文件系统
缓冲区缓存 Buffer Cache
页缓存 Page Cache
buffer和cache在Linux 2.4之前是两种缓存,也就是说同一份数据有两份内容在内核中。这两份数据的同步和维护其实带来了一些麻烦。在Linux 2.4之后,人们想到了统一这两种缓存,就把buffer指向了cache,使得数据只剩下一份实体。对于为什么是cache作为了主要的语义
buffer和cache区别
Linux2.4 内核做了优化,取而代之的是只包含关于数据的位置和长度的信息的描述符被追加到了socket buffer 缓冲区中。DMA引擎直接把数据从内核缓冲区传输到协议引擎(protocol engine)
零拷贝
保存了实际在网络中传输的数据;
报文数据
供内核处理报文的额外数据,这些数据构成了协议之间交换的控制信息
管理数据
socket buffer
虚拟内存的磁盘空间
swap分区
IO事件处理机制 select 到 Epoll 的进化过程
O(1)利用FD的回调
同时支持 block 和 non-block socket
如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。
LT(level triggered,水平触发模式)
只支持no-block socket
当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,,并且不会再为那个文件描述符发送更多的就绪通知
ET(edge-triggered,边缘触发模式)
两种模型
构建epoll对象
有连接接入时,会插入到epoll对象中,epoll对象里实际是一个红黑树+双向链表,fd插入到红黑树中,通过红黑树查找到是否重复
一旦fd就绪,会触发回调把fd的插入到就绪链表中,并唤醒等待队列中的线程
调用epoll_wait方法时只需要检查就绪链表,如有则返回给用户程序,如没有进入等待队列
路径
epoll把fd管理起来,不需要每次都重复传入,而且只返回就绪的fd,因此减少了用户空间和内核空间的相互拷贝,在fd数量庞大的时候更加高效。
epoll
poll使用链表保存文件描述符,因此没有了监视文件数量的限制
poll
单个进程能够监视的文件描述符的数量存在最大限制
select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差
select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件
select
IO事件处理机制
内核的I/O Cache
有如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。
如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,如果回收内存工作结束后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了触发 OOM (Out of Memory)机制。
是否有空闲的物理内存
触发缺页中断函数
如果虚拟地址对应的物理地址不在物理内存中,则产生缺页中断
只读段
全局变量、静态变量
数据段
动态内存
动态库
文件映射区
维护函数调用的上线文空间
页表
内核虚拟空间
有低地址到高地址
32 位操作系统,进程最多只能申请 3 GB 大小的虚拟内存空间
32 位系统的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间;
64 位操作系统,进程可以使用 128 TB 大小的虚拟内存空间,所以进程申请 8GB 内存是没问题的,因为进程申请内存是申请虚拟内存,只要不读写这个虚拟内存,操作系统就不会分配物理内存。
64 位系统的内核空间和用户空间都是 128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。
虚拟地址空间
虚拟地址
每一个进程都有独立的虚拟地址空间,进程访问的不是真正的物理地址
虚拟内存
Linux上 的线程模型为 NPTL ( Native POSIX Thread Library),他使用一对一模式,兼容 POSIX 标准,没有使用管理线程,可以更好地在多核 CPU 上运行。
linux
让执行线程不再在同一个共享变量上自旋,避免过高频率的缓存同步操作。
MCS在自己的结点的locked域上自旋等待
解决了CLH在NUMA系统架构中获取locked域状态内存过远的问题
MCS
CLH是在前趋结点的locked域上自旋等待
CLH
自旋转锁
锁
异步:内核空间发起IO
同步:用户空间发起IO
同步异步
用户空间执行状态
阻塞非阻塞
阻塞式I/O
非阻塞式I/O
非阻塞是指I/O读写,对应的是recvfrom操作
同步,是因为,这个执行是在一个线程里面执行的
同步非阻塞
NIO只负责对发生在fd描述符上的事件进行通知。事件的获取和通知部分是非阻塞的
I/O复用(select/poll/epoll)
信号驱动式I/O
异步I/O
I/O模型
io
新建(new Thread)
就绪(runnable)
运行(running)
(一)等待堵塞:执行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)同步堵塞:执行的线程在获取对象的同步锁时,若该同步锁被别的线程占用。则JVM会把该线程放入锁池中。
(三)其它堵塞:执行的线程执行sleep()或join()方法,或者发出了I/O请求时。JVM会把该线程置为堵塞状态。
堵塞(blocked)
死亡(dead)
线程的基本状态
线程
操作系统
resin
Nginx创建一个client-fd,与要转发的服务器进行通讯,当目标服务器把数据写回的时候,被Nginx的epoll监听到,然后再处理这个事件!
反向代理
负载均衡
限流
证书配置
为了协调客户端到nginx以及nginx到服务器的速度不同
缓冲区
X-Frame-Options
嵌套Frame
采用的所谓事件驱动往往局限在 TCP 连接建立、关闭事件上,一个连接建立以后,在其关闭之前的所有操作都不再是事件驱动,这时会退化成按顺序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用着系统资源,直到关闭才会释放资源。这种请求占用着服务器资源等待处理的模式会造成服务器资源极大的浪费。
事件驱动架构
根据每个 fd 上面的 callback 函数实现的,
所以 epoll 不会随着监听 fd 数目的增长而降低效率
epoll 模型
Master-Worker模式 多进程单线程模型
sendfile
静态文件
nginx
包含多个服务 service
一个Service只有一个Container,但是可以有多个Connectors
不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的,Http11NioProtocol使用的是NioSocket来连接的。
Coyote通信端点,即通信监听的接口,是具体的Socker接收处理类,是对传输层的抽象。Tomcat并没有Endpoint接口,而是提供了一个抽象类AvstractEndpoint。根据I/O方式的不同,提供了NioEndpoint(NIO)、AprEndpoint(APR)以及Nio2Endpoint(NIO2)三个实现
Endpoint用来处理底层Socket的网络连接
Endpoint
Coyote协议处理接口,负责构造Request和Response对象,并且通过Adapter将其提交到Catalina容器处理,是对应用层的抽象。Processor是单线程的,Tomcat在同一次链接中复用Processor。Tomcat按照协议的不同提供了3个实现类:Http11Processor(HTTP/1.1)、AjpProcessor(AJP)、StreamProcessor(HTTP/2.0)。除此之外,他还提供了两个用于处理内部处理的实现:UpgradeProcessorInternal和UpgradeProcessorExternal,前者用于处理内部支持的升级协议(如HTTP/2.0和WebSocket),后者用于处理外部扩展的升级协议支持。
Processor用于将Endpoint接收到的Socket封装成Request
Processor
Adapter用于将Request交给Container进行具体的处理。
Adapter
ProtocolHandler
Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化
Coyote是Tomcat链接器框架的名称
Coyote将Socket输入转换为Request对象,交由Catalina容器进行处理,处理请求完成之后,Catalina通过Coyote提供的Response对象将结果写入输出流。
Coyote封装了底层的网络通信(Socket请求以及响应处理),为Catalina容器提供了统一的接口
coyote
Connector
Catalina servlet容器
引擎,用来管理多个站点,一个Service最多只能有一个Engine
Engine
代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点;
Host
代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件;
Context
每一Wrapper封装着一个Servlet;
Wrapper
Pipeline-Value是责任链模式
Container处理请求是使用Pipeline-Value管道来处理的!
Container
service
顶层容器 server
BIO
基于JDK的java.nio包
NIO
APR
模式
"common gateway interface",通用网关接口,CGI的存在,可以是用户通过浏览器来访问执行在服务器上的动态程序;CGI是Web服务器与CGI程序间传输数据的标准;
CGI
用一个线程去遍历轮询每一个连接,然后得到需要处理的连接,并对这些连接进行处理。
select 模型
maxThreads
当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
acceptCount
参数
tomcat
•源站内容有更新的时候,源站主动把内容推送到CDN节点。
•常规的CDN都是回源的。即:当有用户访问某一个URL的时候,如果被解析到的那个CDN节点没有缓存响应的内容,或者是缓存已经到期,就会回源站去获取。如果没有人访问,那么CDN节点不会主动去源站拿的。
•回源域名
回源
工作方法
边缘层
中心层
源站
http标准协议
CDN
ActiveMQ
convertAndSend
当确认了所有的消费者都接收成功之后,才触发另一个convertSendAndReceive(…),也就是才会接收下一条消息。RPC调用方式
convertSendAndReceive
事务
持久化
高并发、持久化、支持集群、社区活跃、综合技术
client
basicAck
basicNack
Channel
多个队列 或者单个消费
顺序性
幂等
网络延迟
为什么会重复
防止重复
confirm模式
一套绑定规则,用于告诉Exchange消息应该被存储到哪个Queue。
Binding
直连
DIRECT
广播
FANOUT
一个队列多个模糊匹配多个 routingkey
TOPIC
HEADERS
Exchange交换器
生产者在向Exchange发送消息时,需要指定一个Routing Key来设定该消息的路由规则。 Routing Key需要与Exchange类型及Binding Key联合使用才能生效
Routing Key
由于消费者读取消息之后可能会把消息放回(或者重传)到队列中(例如,处理失败的情况),这样就会导致消息的顺序无法保证。
当开发复杂的软件系统时,我们可能被诱导使用同一个消息平台去实现所有必须的消息用例。但是,从我的经验看,通常同时使用这两个消息平台能够带来更多的好处。例如,在一个事件驱动的架构系统中,我们可以使用RabbitMQ在服务之间发送命令,并且使用Kafka实现业务事件通知。原因是事件通知常常用于事件溯源,批量操作(ETL风格),或者审计目的,因此Kafka的消息留存能力就显得很有价值。相反,命令一般需要在消费者端做额外处理,并且处理可以失败,所以需要高级的容错处理能力。
virtual host
RabbitMQ
RocketMQ
Kafka(见hadoop分支)
解耦、异步、削峰
为什么用
系统可用性降低
系统复杂度提高
一致性问题
缺点
高可用
幂等性
重复消费
amqp的事务机制
AMQP协议
mq
索引
类型
_index
_type
_id
元数据
文档
索引里面最小的存储和查询单元,对于英文来说是一个单词,对于中文来说一般指分词后的一个词。
词条(Term)
或字典,是词条 Term 的集合。搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。
词典(Term Dictionary)
一个文档通常由多个词组成,倒排表记录的是某个词在哪些文档里出现过以及出现的位置。
倒排表(Post list)
所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件被称之为倒排文件,倒排文件是存储倒排索引的物理文件。
倒排文件(Inverted File)
Text 用于索引全文值的字段,例如电子邮件正文或产品说明。这些字段是被分词的,它们通过分词器传递 ,以在被索引之前将字符串转换为单个术语的列表。
分析过程允许 Elasticsearch 搜索单个单词中每个完整的文本字段。文本字段不用于排序,很少用于聚合。
Text
Keyword 用于索引结构化内容的字段,例如电子邮件地址,主机名,状态代码,邮政编码或标签。它们通常用于过滤,排序,和聚合。Keyword 字段只能按其确切值进行搜索。
Keyword
映射是用于定义 ES 对索引中字段的存储类型、分词方式和是否存储等信息,就像数据库中的 Schema ,描述了文档可能具有的字段或属性、每个字段的数据类型。
映射(Mapping)
基本概念
确切值
全文文本
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。
由文档中出现的唯一单词列表以及每个单词在文档中的位置决定
每个文档都有一个对应的文档 ID,文档内容被表示为一系列关键词的集合。例如,文档 1 经过分词,提取了 20 个关键词,每个关键词都会记录它在文档中出现的次数和出现位置。
倒排索引就是关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了关键词。
倒排索引
must
should
must not
搜索
shard = hash(document_id) % (num_of_primary_shards)
客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node(协调节点)
当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;
flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;
flush
当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh;
coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。[路由的算法是?]
写数据
lucene 就是一个 jar 包,里面包含了封装好的各种建立倒排索引的算法代码
全文检索引擎
lucene
更新和删除文档
磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
ES 内部是如何通过一个相同的设置 cluster.name 就能将不同的节点连接到同一个集群的?答案是 Zen Discovery。
发现机制
集群
存储原理
translog flush
refresh_interval
优化
中文分词有特定的难点,不像英文,单词有自然的空格作为分隔,在中文句子中,不能简单地切分成一个个的字,而是需要分成有含义的词
支持自定义词库,支持热更新分词字典
ik
Python 中最流行的分词系统,支持分词和词性标注
jieba
清华大学自然语言处理和社会人文计算实验室的一套中文分词器
THULAC
中文分词
Standard Analyzer - 默认分词器,按词切分,小写处理Simple Analyzer - 按照非字母切分(符号被过滤),小写处理Stop Analyzer - 小写处理,停用词过滤(the ,a,is)Whitespace Analyzer - 按照空格切分,不转小写Keyword Analyzer - 不分词,直接将输入当做输出Pattern Analyzer - 正则表达式,默认 \\W+Language - 提供了 30 多种常见语言的分词器Customer Analyzer - 自定义分词器
分词器
Elasticsearch
Logstash
Kibana
YAML 文件规定了组成一个应用所需的容器和其他资源。Kubernetes 提供了调度、伸缩、服务发现、健康检查、密文管理和配置管理等功能。
容器和集群管理的标准
Kubernetes 集群 是由多个计算机(可以是物理机、云主机或虚拟机)组成的一个独立系统,通过 Kubernetes 容器管理系统,实现部署、运维和伸缩 Docker 容器等功能,它允许您的组织对应用进行自动化运维。
负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
节点代理kubelet
保存了整个集群的状态
etcd
提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
apiserver
负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
controller manager
负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
scheduler
负责为Service提供cluster内部的服务发现和负载均衡
kube-proxy
负责镜像管理以及Pod和容器的真正运行(CRI)
Container runtime
核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
接口层:kubectl命令行工具、客户端SDK以及集群联邦
Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
分层架构
kubeadm
安装方式
kubeflow 机器学习(ML)工作流程
node agent(kubelet)
容器
Pod
不支持GPU的共享和隔离
k8s device plugin
使用GPU
包管理工具
Helm
Kubernetes(k8s)希腊语 船长&领航员 (容器调度平台)
Rancher的核心竞争力在于其强大的多集群管理能力,提供了极其简便的K8s部署及管理能力。同时提供了集成开源监控、日志、Git CI的能力,虽然学习成本较高,但提供了一站式的解决方案,对运维更加友好。
提供Kubernetes、Mesos和Swarm三种调度工具的企业级分发版和商业技术支持的容器管理平台
rancher
KubeSphere定位是以应用为中心的容器平台,提供简单易用的操作界面,一定程度上降低了学习成本,同时集成了原生istio等功能,更加符合开发的使用习惯。
Kubesphere
主流的Kubernetes集群管理平台
基于 K8S 的集成的模型部署方案, 内置了很多通用的例如 tfserving、 sklearn server、mlflow server、triton这样的模型推理服务器(inference server)。除了这些还可以通过自己实现自定义的 inference server 提供一些额外的模型服务支持。
seldon 通过将你的模型部署为K8S上的微服务,来提供 HTTP/GRPC 接口给业务调用。
seldon
容器打包和运行时系统的标准
主要用于管理各个节点上的容器。开发者在 Dockerfiles 中构建容器镜像,上传到镜像仓库中,用户只需从镜像仓库下载该镜像文件,就可以开始使用。
docker
Firecracker
微虚机
faas (function as a service)
serverless
Prometheus 再从这个网关里 Pull 指标
PushGateWay
Exporter
指标暴露
Pull 模型
Push 模型
指标抓取
内置的时序数据库
PromQL
存储和可视化
alertmanageer 基于 promql 来做系统的监控告警
监控告警
静态注册:静态的将服务的 IP 和抓取指标的端口号配置在 Prometheus yaml 文件的 scrape_configs 配置下
动态注册:动态注册就是在 Prometheus yaml 文件的 scrape_configs 配置下配置服务发现的地址和服务名,Prometheus 会去该地址,根据你提供的服务名动态发现实例列表,在 Prometheus 中,支持 consul,DNS,文件,K8s 等多种服务发现机制。
服务注册
配置更新
工作原理
Metric 指标
Prometheus
中间件与容器
计算机网络模型
首先根据目的IP和路由表决定走哪个网卡,再根据网卡的子网掩码地址判断目的IP是否在子网内。如果不在则会通过arp缓存查询IP的网卡地址,不存在的话会通过广播询问目的IP的mac地址,得到后就开始发包了,同时mac地址也会被arp缓存起来。
输入 ping IP 后敲回车,发包前会发生什么?
Client requests connection by sending SYN (synchronize) message to the server.
Server acknowledges by sending SYN-ACK (synchronize-acknowledge) message back to the client.
三次握手
攻击者使用假IP重复发送SYN包到目标服务端的每一个端口。服务端接受这些表面上合法的请求并与之建立链接,接着从自己的端口应答SNY-ACK报文给每一个请求。攻击者此时,要么不接受SNY-ACK(第3步),要么不发送ACK(第3步),此时服务端就会等待攻击者应答自己,但是明显是等不到的(但是还要等一个固定的时间段),那么这个socket就被占用了,网络资源也被浪费了。
Micro blocks --- “疏”,依旧接受SNY请求,但是变相扩大自己的容量。对于SNY请求,不在用大数据结构管理,而采用微型数据结构(16个字节)。
RST cookies --- “堵”+“骗”,服务端不再傻乎乎地走正常套路了,服务端在收到SNY后,会故意发一个错误的SNY-ACK给客户端,这个时候,如果使用正常tcp/ip协议的客户端会发现SNY-ACK内容不对,于是会回一个RST给服务端,告诉服务端“你发的东西不对头啊”。如果客户端收到了这个RST packet,那么可以说明对面那个客户端是合法的,接着进入正常流程。
解决
tcp洪流
TCP/IP协议
HTTP1.0
HTTP1.1
多路复用和首部压缩解决了head of line blocking
server pushing
HTTP2.0
简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等
User-Agent
永久重定向
301
临时重定向
302
状态码
Type(消息类型):数据类型定义的容器,它使用某种类型系统(如 XSD)。
Message(消息):通信数据的抽象类型化定义,它由一个或者多个 part 组成。
WSDL定义了四种操作: 1.单向(one-way):端点接受信息;2.请求-响应(request-response):端点接受消息,然后发送相关消息;3.要求-响应(solicit-response):端点发送消息,然后接受相关消息;4.通知(notification):端点发送消息。
Operation(操作):对服务所支持的操作进行抽象描述
Part:消息参数
Port Type(端口类型):特定端口类型的具体协议和数据格式规范。
Port:定义为绑定和网络地址组合的单个端点。
Service:相关端口的集合,包括其关联的接口、操作、消息等。
描述Web服务发布的XML格式
WSDL(Web服务描述语言,Web Services Description Language)
XML
soap用来描述传递信息的格式
SOAP(简单对象访问协议)
管理,分发,查询webService
UDDI
WebService
报文
HTTP
HTTPS
客户端两次请求
重定向
服务的行为
转发
数据库读写分离负载
跳板机端口映射
四层负载均衡是基于传输层协议包来封装的(如:TCP/IP)
同一时间只占用一个 CPU,只能有一个指令在运行,并行读写是不存在的。
单线程
缓存、限流、分布式锁、分布式session
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象string类型是Redis最基本的数据类型,一个键最大能存储512MB。
String(字符串)
用户信息
时间轴、简单队列
List类型是按照插入顺序排序的字符串链表(所以它这里的list指的相当于java中的linkesdlist)
List类型
赞、踩
Set类型看作为没有排序的字符集合。如果多次添加相同元素,Set中将仅保留该元素的一份拷贝。
set类型
排行榜
Sorted-Sets中的每一个成员都会有一个分数(score)与之关联
Sorted-Sets类型
位图
HLL
GEO(地理信息)
字典
跳跃表
设计
跳跃列表(skipList)
底层结构
xadd mystream * name zhangsan age 18;
添加消息
xgroup create mystream cg1 $
创建消费组
xreadgroup group cg1 godconsumer streams mystream >
消费消息
xack mystream cg1 1636612497298-0
ACK
XPENDING mystream cg1
查询未消费消息
消费组,使用 XGROUP CREATE 命令创建,一个消费组有多个消费者(Consumer)。
Consumer Group
游标,每个消费组会有个游标 last_delivered_id,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。
last_delivered_id
消费者(Consumer)的状态变量,作用是维护消费者的未确认的 id。 pending_ids 记录了当前已经被客户端读取的消息,但是还没有 ack (Acknowledge character:确认字符)。
pending_ids
stream
处理客户端请求时,不会阻塞主线程;Redis 单纯执行(大多数指令)一个指令不到 1 微秒[4],如此,单核 CPU 一秒就能处理 1 百万个指令(大概对应着几十万个请求吧),用不着实现多线程(网络才是瓶颈)。
多路 I/O 复用机制
使用操作系统提供的虚拟内存来存储数据
这段时间,redis 是无法处理请求的。
RDB 全量持久化
AOF 增量持久化
纯文本格式(redis文本协议的流水记录),加载的过程相当于历史重放
aof(日志文件)
二进制格式,直接进行加载,所以一般情况下rdb数据加载会比aof加载快
rdb(快照文件)
replication(主从复制)
数据迁移方式
First In First Out,先进先出。
FIFO 淘汰最早数据
Least Recently Used,最近最少使用。
LRU 剔除最近最少使用
Least Frequently Used,最不经常使用。
LFU 剔除最近使用频率最低
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
指令
淘汰策略
redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
定期删除
定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈
惰性删除
删除策略
Sentine l哨兵
异步补偿
数据不一致
跳表
事务提供了一种将多个命令请求打包
批量操作在发送 EXEC 命令前被放入队列缓存
Lua脚本
不保证原子性
没有隔离级别
且没有回滚
multi : 标记一个事务块的开始( queued )
exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard : 取消事务,放弃事务块中的所有命令
unwatch : 取消watch对所有key的监控
多套环境
redlock 红锁
redis
Tair
neo4j
nosql
Binary log
sql解释器 优化器
服务层
核心服务
引擎事务日志
存储引擎
系统文件层
逻辑架构
主键ID
事务ID
回滚指针
系统内部字段
trx_sys->mysql_trx_list 包含了所有用户线程的事务对象,即使是未开启的事务对象,只要还没被回收到trx_pool中,都被放在该链表上。当session断开时,事务对象从链表上摘取,并被回收到trx_pool中,以待重用。
trx_sys->rw_trx_list 读写事务链表,当开启一个读写事务,或者事务模式转换成读写模式时,会将当前事务加入到读写事务链表中,链表上的事务是按照trx_t::id有序的;在事务提交阶段将其从读写事务链表上移除。
trx_sys->serialisation_list 序列化事务链表,在事务提交阶段,需要先将事务的undo状态设置为完成,在这之前,获得一个全局序列号trx->no,从trx_sys->max_trx_id中分配,并将当前事务加入到该链表中。随后更新undo等一系列操作后,因此进入提交阶段的事务并不是trx->id有序的,而是根据trx->no排序。当完成undo更新等操作后,再将事务对象同时从serialisation_list和rw_trx_list上移除。
trx_sys->rw_trx_ids 记录了当前活跃的读写事务ID集合,主要用于构建ReadView时快速拷贝一个快照
trx_sys->rw_trx_set 这是的映射集合,根据trx_id排序,用于通过trx_id快速获得对应的事务对象。一个主要的用途就是用于隐式锁转换,需要为记录中的事务id所对应的事务对象创建记录锁,通过该集合可以快速获得事务对象
集合
事务链表和集合
trx_sys
在Innodb中,每次开启一个事务时,都会为该session分配一个事务对象。而为了对全局所有的事务进行控制和协调,有一个全局对象trx_sys,对trx_sys相关成员的操作需要trx_sys->mutex锁。
Innodb使用一种称做ReadView(视图)的对象来判断事务的可见性(也就是ACID中的隔离性)
对于普通的读写事务,总是为其指定一个回滚段(默认128个回滚段)。而对于只读事务,如果使用到了InnoDB临时表,则为其分配(1~32)号回滚段。(回滚段指定参阅函数trx_assign_rseg_low)
当为事务指定了回滚段后,后续在事务需要写undo页时,就从该回滚段上分别分配两个slot,一个用于update_undo,一个用于insert_undo。分别处理的原因是事务提交后,update_undo需要purge线程来进行回收,而insert_undo则可以直接被重利用。
事务回滚段
Innodb的多版本数据使用UNDO来维护的,例如聚集索引记录(1) =>(2)=>(3),从1更新成2,再更新成3,就会产生两条undo记录。
原理
事务锁 维护在不同的Isolation level下数据库的Atomicity和Consistency两大基本特性。
一类是mutex,实现内存结构的串行化访问
一类是rw lock,实现读写阻塞,读读并发的访问的读写锁
内存锁 为了维护内存结构的一致性,比如Dictionary cache、sync array、trx system等结构。 InnoDB并没有直接使用glibc提供的库,而是自己封装了两类:
undo log
Atomicity(原子性)
保证数据页的准确性
doublewrite buffer
保证恢复时能够将所有的变更apply到数据
崩溃恢复时存在还未提交的事务,那么根据XA规则提交或者回滚事务
crash recovery
Consistency (一致性)
事务A读到了事务B还没有提交的数据
READ-UNCOMMITTED 在该隔离级别下会读到未提交事务所产生的数据更改,这意味着可以读到脏数据,实际上你可以从函数row_search_mvcc中发现,当从btree读到一条记录后,如果隔离级别设置成READ-UNCOMMITTED,根本不会去检查可见性或是查看老版本。这意味着,即使在同一条SQL中,也可能读到不一致的数据。
脏读
读未提交(READ UNCOMMITED)
在一个事务里面读取了两次某个数据,读出来的数据不一致
每次select都生成一个快照读
READ-COMMITTED 在该隔离级别下,可以在SQL级别做到一致性读,当事务中的SQL执行完成时,ReadView被立刻释放了,在执行下一条SQL时再重建ReadView。这意味着如果两次查询之间有别的事务提交了,是可以读到不一致的数据的。
不可重复读
读已提交(READ COMMITTED)
事务后第一个select语句才是快照读的地方
并发的事务中有事务发生了插入、删除操作
在一个事务里面的操作中发现了未被操作的数据
行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。
幻读(当前读)
REPEATABLE-READ 可重复读和READ-COMMITTED的不同之处在于,当第一次创建ReadView后(例如事务内执行的第一条SEELCT语句),这个视图就会一直维持到事务结束。也就是说,在事务执行期间的可见性判断不会发生变化,从而实现了事务内的可重复读。
可重复读(REPEATABLE READ)
序列化(SERIALIZABLE)
Isolation(隔离性)
WAL(Write-Ahead Logging)的原则
Redo log和数据页都做了checksum校验
为了解决半写的问题,即写一半数据页时实例crash,这时候数据页是损坏的。InnoDB使用double write buffer来解决这个问题,在写数据页到用户表空间之前,总是先持久化到double write buffer,这样即使没有完整写页,我们也可以从double write buffer中将其恢复出来。你可以通过innodb_doublewrite选项来开启或者关闭该特性。
Durability(持久性)
事务特性
只读事务
读写事务
读写事务并不意味着一定在引擎层就被认定为读写事务了,5.7版本InnoDB里总是默认一个事务开启时的状态为只读的。举个简单的例子,如果你事务的第一条SQL是只读查询,那么在InnoDB层,它的事务状态就是只读的,如果第二条SQL是更新操作,就将事务转换成读写模式。
BEGIN
当Server层接受到任何数据更改的SQL时,都会直接拒绝请求,返回错误码ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION,不会进入引擎层。
这个选项可以强约束一个事务为只读的,而只读事务在引擎层可以走优化过的逻辑,相比读写事务的开销更小,例如不用分配事务id、不用分配回滚段、不用维护到全局事务链表中。
START TRANSACTION READ ONLY
START TRANSACTION READ WRITE
innobase_start_trx_and_assign_read_view
这里会进入InnoDB层,调用函数innobase_start_trx_and_assign_read_view,注意只有你的隔离级别设置成REPEATABLE READ(可重复读)时,才会显式开启一个Read View
START TRANSACTION WITH CONSISTENT SNAPSHOT
无需显式开启事务,如果你执行多条SQL但不显式的调用COMMIT(或者执行会引起隐式提交的SQL)进行提交,事务将一直存在
AUTOCOMMIT = 0
事务开启
事务类型
回滚的方式是提取undo日志,做逆向操作
InnoDB的undo是单独写在表空间中的,本质上和普通的数据页是一样的。如果在事务回滚时,undo页已经被从内存淘汰,回滚操作(特别是大事务变更回滚)就可能伴随大量的磁盘IO。因此InnoDB的回滚效率非常低
事务回滚
Server层的MDL锁模块,维持了一个事务过程中所有涉及到的表级锁对象。通过MDL锁,可以堵塞DDL,避免DDL和DML记录binlog乱序
InnoDB的trx_sys子系统,维持了所有的事务状态,包括活跃事务、非活跃事务对象、读写事务链表、负责分配事务id、回滚段、Readview等信息,是事务系统的总控模块;
InnoDB的lock_sys子系统,维护事务锁信息,用于对修改数据操作做并发控制,保证了在一个事务中被修改的记录,不可以被另外一个事务修改;
InnoDB的log_sys子系统,负责事务redo日志管理模块;
InnoDB的purge_sys子系统,则主要用于在事务提交后,进行垃圾回收,以及数据页的无效数据清理。
在InnoDB中一直维持了一个不断递增的整数,存储在trx_sys->max_trx_id中;每次开启一个新的读写事务时,都将该ID分配给事务,同时递增全局计数。事务ID可以看做一个事务的唯一标识。
事务管理执行
回滚段undo是实现InnoDB MVCC的根基。每次修改聚集索引页上的记录时,变更之前的记录都会写到undo日志中。回滚段指针包括undo log所在的回滚段ID、日志所在的page no、以及page内的偏移量,可以据此找到最近一次修改之前的undo记录,而每条Undo记录又能再次找到之前的变更。
undo日志
ReadView::id 创建该视图的事务ID;
ReadView::m_ids 创建ReadView时,活跃的读写事务ID数组,有序存储;
ReadView::m_low_limit_id 设置为当前最大事务ID;
ReadView::m_up_limit_id m_ids集合中的最小值,如果m_ids集合为空,表示当前没有活跃读写事务,则设置为当前最大事务ID。
Readview
快照读
加排他锁
间隙锁
1.对主键或唯一索引,如果当前读时,where条件全部精确命中(=或者in),这种场景本身就不会出现幻读,所以只会加行记录锁。
2.没有索引的列,当前读操作时,会加全表gap锁,生产环境要注意
3.非唯一索引列,如果where条件部分命中(>、<、like等)或者全未命中,则会加附近Gap间隙锁。
update ... (更新操作)
delete ... (删除操作)
insert ... (插入操作)
select ... lock in share mode (共享读锁)
select ... for update (写锁)
场景
当前读
聚集索引
二级索引
可见性判断
多版本并发控制MVCC
512字节
不管是log buffer中还是os buffer中以及redo log file on disk中,都是这样以512字节的块存储的
12字节
日志头
492字节
日志体
8字节
日志尾
redo log block
redo
undo log segment
rollback segment称为回滚段,每个回滚段中有1024个undo log segment。
回滚
undo
备库通过二进制日志重放主库提交的事务,而主库binlog写入在commit之前,如果写完binlog主库crash,再次启动时会回滚事务。但此时从库已经执行,则会造成主备数据不一致
–自动为每个事务分配一个唯一的ID(XID)–COMMIT会被自动的分成Prepare和Commit两个阶段。– Binlog会被当做事务协调者(Transaction Coordinator),Binlog Event会被当做协调者日志。
若关闭了binlog,且存在不止一种事务引擎时,则XA控制对象为tc_log_mmap;
若打开binlog,且使用了事务引擎,则XA控制对象为mysql_bin_log;
其他情况,使用tc_log_dummy,这种场景下就没有什么XA可言了,无需任何协调者来进行XA。
隐式XA
显式XA
XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口
两种XA事务方式
XA事务(内部分布式事物)
崩溃提交或回退
binlog 与 事务日志一致性
prepare_commit_mutex锁以串行的方式来保证MySQL数据库上层二进制日志和Innodb存储引擎层的事务提交顺序一致,然后会导致组提交(group commit)特性无法生效
5.6前
引入队列机制保证Innodb commit顺序与binlog落盘顺序一致,并将事务分组,组内的binlog刷盘动作交给一个事务进行,实现组提交目的。在MySQL数据库上层进行提交时首先按顺序将其放入一个队列中,队列中的第一个事务称为leader,其他事务称为follow,leader控制着follow的行为。
BLGC(Binary Log Group Commit),并把事务提交过程分成三个阶段,Flush stage、Sync stage、Commit stage。
5.6
binlog与 redolog的顺序性
事务日志
数据库事务
LOCK_X(排他锁)
LOCK_S(共享锁)
意向共享锁( Intent share lock,简称IS锁)
意向排它锁( Intent Exclusive lock,简称IX锁)
表锁
锁带上这个 FLAG 时,表示这个锁对象只是单纯的锁在记录上,不会锁记录之前的 GAP。在 RC 隔离级别下一般加的都是该类型的记录锁(但唯一二级索引上的 duplicate key 检查除外,总是加 LOCK_ORDINARY 类型的锁)。
LOCK_REC_NOT_GAP
记录锁(RK)
表示只锁住一段范围,不锁记录本身,通常表示两个索引记录之间,或者索引上的第一条记录之前,或者最后一条记录之后的锁。可以理解为一种区间锁,一般在RR隔离级别下会使用到GAP锁。
间隙锁(GK) LOCK_GAP
而NEXT-KEY LOCK正是为了解决RR隔离级别下的幻读问题。所谓幻读就是一个事务内执行相同的查询,会看到不同的行记录。在RR隔离级别下这是不允许的。
LOCK_ORDINARY(Next-Key Lock)
插入意向锁(IK) LOCK_INSERT_INTENTION(插入意向锁)
行锁
InnoDB 通常对插入操作无需加锁,而是通过一种“隐式锁”的方式来解决冲突。聚集索引记录中存储了事务id,如果另外有个session查询到了这条记录,会去判断该记录对应的事务id是否属于一个活跃的事务,并协助这个事务创建一个记录锁,然后将自己置于等待队列中。该设计的思路是基于大多数情况下新插入的记录不会立刻被别的线程并发修改,而创建锁的开销是比较昂贵的,涉及到全局资源的竞争。
隐式锁
16k
Row size too large (> 8126).
每个page至少要包含两个记录
页
最小存储单元
自增ID可以保证每次插入时B+索引是从右边扩展的,可以避免B+树和频繁合并和分裂(对比使用UUID)。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差。
推荐自增 id 做主键
6字节
指针
一般情况下,当我们需要读入一个Page时,首先根据space id 和page no找到对应的buffer pool instance。然后查询page hash,如果page hash中没有,则表示需要从磁盘读取。在读盘前首先我们需要为即将读入内存的数据页分配一个空闲的block。当free list上存在空闲的block时,可以直接从free list上摘取;
我们知道InnoDB使用buffer pool来缓存从磁盘读取到内存的数据页。buffer pool通常由数个内存块加上一组控制结构体对象组成
768字节的时候,剩余部分会保存在另外的页面(off-page)
64K
如果我们想在创建的表的时候,保证创建的表中的text字段都能安全的达到64k上限(而不是等插入的时候才发现),那么需要将默认为OFF的innodb_strict_mode设置为ON,这样在建表时会先做判断
text
刷新binlog_cache中的信息到磁盘由os决定。
0
每N次事务提交刷新binlog_cache中的信息到磁盘。
n
sync_binlog
它控制了重做日志(redo log)的写盘和落盘策略
事务安全
事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中
1
OS crash可能丢失
每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk
2
mysqld 进程crash可能丢失
事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中
innodb_flush_log_at_trx_commit
“双1”,是保证CrashSafe的根本。
InnoDB
MyISAM
InnoDB支持事务,MyISAM不支持
InnoDB支持外键,而MyISAM不支持
InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
InnoDB表必须有主键(用户没有指定的话会自己找或生产一个主键),而Myisam可以没有
InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
因为InnoDB的事务特性
InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件);
InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁
区别
引擎
from
Simple Nested-Loop Join
驱动表会根据关联字段的索引进行查找,当在索引上找到了符合的值,再回表进行查询,也就是只有当匹配到索引以后才会进行回表。至于驱动表的选择,MySQL优化器一般情况下是会选择记录数少的作为驱动表,但是当SQL特别复杂的时候不排除会出现错误选择。在索引嵌套链接的方式下,如果非驱动表的关联键是主键的话,这样来说性能就会非常的高,如果不是主键的话,关联起来如果返回的行数很多的话,效率就会特别的低,因为要多次的回表操作。先关联索引,然后根据二级索引的主键ID进行回表的操作。这样来说的话性能相对就会很差。
Index Nested-Loop Join
最先介绍的Simple Nested-Loop Join算法,而是会优先使用Block Nested-Loop Join的算法。Block Nested-Loop Join对比Simple Nested-Loop Join多了一个中间处理的过程,也就是join buffer,使用join buffer将驱动表的查询JOIN相关列都给缓冲到了JOIN BUFFER当中,然后批量与非驱动表进行比较,这也来实现的话,可以将多次比较合并到一次,降低了非驱动表的访问频率。也就是只需要访问一次S表。这样来说的话,就不会出现多次访问非驱动表的情况了,也只有这种情况下才会访问join buffer。在MySQL当中,我们可以通过参数join_buffer_size来设置join buffer的值,然后再进行操作。默认情况下join_buffer_size=256K,在查找的时候MySQL会将所有的需要的列缓存到join buffer当中,包括select的列,而不是仅仅只缓存关联列。在一个有N个JOIN关联的SQL当中会在执行时候分配N-1个join buffer。
Block Nested-Loop Join
Nested-Loop Join(嵌套循环链接),不像其他商业数据库可以支持哈希链接和合并连接
Sort Merge Join
Hash Join
join
where
order by
limit
GROUP BY 条件字段需要符合最左前缀索引;
在使用GROUP BY 的同时,只能使用 MAX 和 MIN 这两个聚合函数;
如果引用到了该索引中 GROUP BY 条件之外的字段条件的时候,必须以常量形式存在;
使用松散(Loose)索引扫描实现 GROUP BY
使用紧凑(Tight)索引扫描实现 GROUP BY
使用临时表实现 GROUP BY
group by
having
CTE的主要思想就先生成临时结果集,以方便后面的使用;与临时表不同的是这个结果集的作用域不是当前session而是当前语句,对!不是 session级是语句级别的
MySQL8的新特性CTE
with
parttion by
并发死锁
Replace into
sql
指定表中的一列或者几列的组合的值在表中不能出现空值和重复值
主键约束
某一列或多个列不能有相同的两行或者两行以上的数据存在,和主键约束不同,唯一约束允许为NULL,只是只能有一行
唯一约束
非空约束
外键约束
约束
可以为NULL
唯一索引
Sending data
Writing to net
state
show processlist
修改配置
用户量小
修改数据库连接池check连接时间
用户量大
8小时问题
wait_timeout
select * from information_schema.INNODB_TRX
查看事务
状态检查
•表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
•尽量使用INT而非BIGINT,如果非负则加上UNSIGNED(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
•使用枚举或整数代替字符串类型
•尽量使用TIMESTAMP而非DATETIME
•单表不要有太多字段,建议在20以内
•用整型来存IP
•应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描
•字符字段只建前缀索引
•字符字段最好不要做主键
•不用外键,由程序保证约束
•尽量不用UNIQUE,由程序保证约束
•使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引
选择数据结构
当where条件和order by同时出现时,如果where中字段A使用了索引,而order by的字段是B,查看执行计划时,就会出现filesort文件排序
建立一个包含 WHERE 和 ORDER BY 条件的混合索引
速度2倍
parttion
窗口函数
隐藏索引
降序索引
在复杂的查询中使用嵌入式表时,使用 CTE 使得查询语句更清晰。
通用表表达式(Common Table Expressions CTE)
InnoDB 现在支持表 DDL 的原子性
可靠性
8.X新特性
旧版本数据读取
purge时扫描undo log record
崩溃恢复时扫描undo log record
大事务回滚
Undo Log
日志
分页对sql要求高
流式查询
mysql
tidb
交叉连接,计算笛卡儿积
CROSS JOIN
内连接,返回满足条件的记录
INNER JOIN
返回左表所有行,右表不存在补NULL
LEFT
返回右表所有行,左边不存在补NULL
RIGHT
返回左表和右表的并集,不存在一边补NULL
FULL
OUTER JOIN
自连接,将表查询时候命名不同的别名。
SELF JOIN
JOIN
SQL
数据库
它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入的恶意html代码会被执行,从而达到恶意用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常呼略其危害性。但是随着前端技术的不断进步富客户端的应用越来越多,这方面的问题越来越受关注。举个简单例子 : 假如你现在是sns站点上一个用户,发布信息的功能存在漏洞可以执行js 你在 此刻输入一个 恶意脚本,那么当前所有看到你新信息的人的浏览器都会执行这个脚本弹出提示框 (很爽吧 弹出广告 :)),如果你做一些更为激进行为呢 后果难以想象。
XSS (Cross Site Script) ,跨站脚本攻击
让用户以自己的身份来完成攻击者需要达到的一些目的。csrf 的攻击不同于xss csrf 需要被攻击者的主动行为触发。这样听来似乎是有“被钓鱼”的嫌疑哈哈。
CSRF(Cross Site Request Forgery),跨站点伪造请求
cc
tcp
udp
DoS
DDoS
文件上传漏洞
触发数据库返回错误。
N/ A
'
永真,返回所有行。
') OR ('1' = '1
' OR '1' = '1
空,不影响返回结果。
value') OR ('1' = '2
value' OR '1' = '2
永假,返回空。
') AND ('1' = '2
' AND '1' = '2
字符串连接,结果同永真。
') OR ('ab' = 'a''b
' OR 'ab' = 'a''b
内联注入
SELECT * FROM usertable WHERE username = '' OR 1 = 1 OR '1' = '1' AND password = ''
' OR 1 = 1 OR '1' = '1
用户名输入
SELECT * FROM usertable WHERE username = '' AND password = '' OR '1' = '1'
密码输入
实例
攻击
前端工程化
webapp
vetur
插件
v-bind绑定一个value属性
子主题 2
v-model
slot-scope=“scope“语义更加明确,相当于一行的数据
slot(插槽)
model
ref
inline
label-width
el-input
el-form-item
el-autocomplete(输入建议输入框)
el-form
default-active
<el-menu>
<el-submenu>(一级菜单)
激活的路由
index
<el-menu-item>(二级菜单)
该row内元素之间的间隙,也就是col占6,gutter占6中的20px
gutter
每个元素所占此row比例,一行是24
span
el-输入框
输入建议
el-autocomplete
el-drawer
即修改
el-switch
Element UI组件
渲染的组件是你使用vue-router指定
router-view
created
mounted
钩子
vue
向外暴露成员
export default
export
返回值一定是一个Promise
async异步函数可以捕获错误处理,不会终止后续代码执行
使用then方法添加回调函数
async异步函数
普通函数
const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改
const
let出的变量作用域是 块作用域
let
var
关键字
js
Axios (ajax封装库)
template
style
div
html
小程序
执行自身工厂的preinstall
指的是dependencies和devDependencies中配置的模块
确定首层依赖模块
获取模块
模块扁平化
更新工程中的node_modules
安装模块
npm install
Node Package Manager
前端
泛型作用只在编译期
泛型的擦除保证了有泛型和没有泛型产生的代码(class文件)是一致的。
泛型类
泛型接口
泛型方法
使用
泛型
native
如果引用为基本数据类型,则该引用为常量,该值无法修改
如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改
如果引用时类的成员变量,则必须当场赋值,否则编译会报错
修饰一个引用
final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。
修饰一个方法
该类成为最终类,无法被继承
修饰类
final
== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
==指引用是否相同, equals()指的是值是否相同
==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
equals和==
次方
^
4 >> 1,结果是2;-4 >> 1,结果是-2。-2 >> 1,结果是-1。
>>:带符号右移。正数右移高位补0,负数右移高位补1
>>>:无符号右移。无论是正数还是负数,高位通通补0。
>> and >>>
位移操作
~(按位非)
| (按位或)
&(按位与)
^ (按 位异或)
位运算
强引用
SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用
软引用(SoftReference )
弱引用(WeakReference )
PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。
虚引用(PhantomReference )
引用
应为抽象类中有抽象方法,抽象方法没有方法体,抽象类不是完整的类,因此不能实例化。
不能实例化的类
抽象类
接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到 API 定义和实现分离的目的。
不能包含任何非常量成员
单继承和多继承
\t接口中只能有抽象方法和常量,在JDK8.0之后可以有Static和default方法
实例成员、类成员、抽象方法
extends和implements
接口中只能定义常量(使用public static final修饰)
抽象类中既可以定义常量也可以定义变量
\t抽象类中可以有构造方法
接口
accept(T t)
Consumer
get()
Supplier
Function.apply
Function
test(T t)
Predicate
函数编程
无法删除和无法修改数据
foreach
循环
语法
常量池
String str="i"
堆内存
String str=new String(“i”)
length()
getByte()
toCharArray()
split(String)
equals()
equalsIsIgnoreCase(String)
contains(String)
startsWith(String)
endsWith(String)
方法
String
数据类型
哈希码
GC 信息
持有的锁信息
64 位
数组对象包含数组长度
标记字段
指向该对象的类 Class
类型指针
对象头 (object header)
对象
classloader
class是已经初始化完成的
Class.forName
运行时获取实例
通过属性相对对象起始地址的偏移量,来读取和写入属性的值
通过 UnSafe 类
反射
synchronized(lockObject){}
public synchornized void test()
作用
对象头
32/64bit\tClass Metadata Address\t指向对象类型数据的指针
实例数据
填充数据
对象的结构
锁标志位 01
无锁
当有一个线程访问同步块
锁膨胀(无锁-偏向锁)
当线程执行到临界区(critical section)时
该锁没有被其他线程所获取,没有其他线程来竞争该锁,那么持有偏向锁的线程将永远不需要进行同步操作。
锁标志位01
偏向锁
有锁竞争
线程在自己的栈桢中创建锁记录 LockRecord。
将锁对象的对象头中的MarkWord复制到线程的刚刚创建的锁记录中。
锁记录中的Owner指针指向锁对象。
将锁对象的对象头的MarkWord替换为指向锁记录的指针。
锁膨胀(偏向锁-轻量级锁)
自旋锁
自适应自旋锁
指向LockRecord的指针 锁标志位 00
轻量级锁也被称为非阻塞同步、乐观锁,因为这个过程并没有把线程阻塞挂起,而是让线程空循环等待,串行执行。
轻量级锁(自旋锁)
自旋转十次失败
锁膨胀(轻量级锁-重量级锁)
指向Mutex的指针\t10
monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁。
当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
锁标志位11
重量级锁
锁的升级
对于Entry Set:如果线程A已经持有了对象锁,此时如果有其他线程也想获得该对象锁的话,它只能进入Entry Set,并且处于线程的BLOCKED状态。
对于Entry Set中的线程,当对象锁被释放的时候,JVM会唤醒处于Entry Set中的某一个线程,这个线程的状态就从BLOCKED转变为RUNNABLE。
锁池
Entry Set
对于Wait Set:如果线程A调用了wait()方法,那么线程A会释放该对象的锁,进入到Wait Set,并且处于线程的WAITING状态。
对于Wait Set中的线程,当对象的notify()方法被调用时,JVM会唤醒处于Wait Set中的某一个线程,这个线程的状态就从WAITING转变为RUNNABLE;或者当notifyAll()方法被调用时,Wait Set中的全部线程会转变为RUNNABLE状态。所有Wait Set中被唤醒的线程会被转移到Entry Set中。
等待池
Wait Set
底层支持
synchronized
JDBC
JCE
JNDI
JAXP
JBI
SPI 的本质是将接口的实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载对应接口的实现类。这样就可以在运行时,获取接口的实现类。
Java SPI 是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。
SPI ( Service Provider Interface),是一种服务发现机制。
SPI
基础
数组 链表 红黑树
扩容 容量 因子
调用对象的hashcode()方法获取hashcode 寻找 bucket位置 来存储Entry对象
A线程覆盖B线程PUT 同一个桶 覆盖
新增一个节点 在链表末尾 覆盖
put
table 已经指向newtable
无法删除元素
正好执行到 table = newTab
resize扩容
多个线程都发现hashmap需要扩容的时候 ,同时调整hashmap的大小。调整时会出现尾部遍历,导致死锁。
resize copy链表 指针错误 引起死锁
1.7JDK
线程不安全
16
1 << 4
DEFAULT_INITIAL_CAPACITY 初始容量
1<<30
MAXIMUM_CAPACITY最大容量
0.75f
DEFAULT_LOAD_FACTOR 加载因子
6
使用树形阈值
TREEIFY_THRESHOLD
高16位跟低16位进行异或运算,这样目的是使结果更加随机性,尽可能使数据均匀分布
hash算法
懒加载
在第一次put的时候进行初始化 table
根据hash跟(新数组的容量-1)异或计算节点在新数组的位置
resize()
hashmap
1.判断elementData数组容量是否满足需求
2.在elementData对应位置上设置值
add
数组越界
覆盖 一个为空
elementData[size++] = e不是原子操作
扩容
arrayList
treemap
NavigableMap(接口)
SortedMap
用数组实现的有界阻塞队列,FIFO先进先出,支持公平所和非公平锁
ArrayBlockQueue
链表结构阻塞队列,FIFO,默认长度为Intger.MAX_VALUE,默认有容量风险
LinkedBlockingQueue
支持线程优先级排序的队列,可自定义ComparaTo()方法来指定元素排序,不保证同优先级顺序
PriorityBlockQueue
实现PriorityBlockQueue的无界队列,创建元素时,可以指定多久从才能从队列中获取当前元素。
DalayQueue
不存储元素的队列,一个put元素必须等待一个take操作
SynchronousQueue
LinkedTransferQueue
双向阻塞队列,多线程并发时,降低锁的竞争
LinkedBlockDeque
BlockQueue
queue
集合类
URL
URLConnection
HttpURLConnection
java.net
SecurityManager
getSecurityManager
System
获取运行时类
getClass
toString()
如果两个对象equals相等,那么这两个对象的HashCode一定也相同
如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置
equals方法重写的话,建议也一起重写hashcode方法
String 类型 Aa 和 BB hascode相同 2112
Park-Miller RNG的随机数生成策略
内存地址
自增变量
3
4
状态值进行异或(XOR)运算得到的一个 hash 值
5(默认)
hashCode
对象复制
clone
notify
之所以我们应该尽量使用notifyAll()的原因就是,notify()非常容易导致死锁
notifyAll
释放占有的锁
超时和非超时
需要另一个线程使用Object.notify()唤醒
被唤醒不一定立即执行原代码
有中断异常
需要在synchronized中执行
wait
当垃圾回收器将要回收对象所占内存之前被调用
此方法有很大的不确定性(不保证方法中的任务执行完)而且运行代价较高。
finalize
Object
表示此方法已废弃、暂时可用
@Deprecated
compareTo方法对比
Comparable
不会释放占有的锁
超时版本
自己醒来
一定会继续执行后续代码
sleep
Thread
函数调用栈太深了
StackOverflowError
OutOfMemoryError
VirtualMachineError
Error
Exception
Throwable
RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
RetentionPolicy
@Target(ElementType.TYPE) //接口、类、枚举、注解 @Target(ElementType.FIELD) //字段、枚举的常量 @Target(ElementType.METHOD) //方法 @Target(ElementType.PARAMETER) //方法参数 @Target(ElementType.CONSTRUCTOR) //构造函数 @Target(ElementType.LOCAL_VARIABLE)//局部变量 @Target(ElementType.ANNOTATION_TYPE)//注解 @Target(ElementType.PACKAGE) ///包
Target
annotation
java.lang
Date
AQS是一个同步器,设计模式是模板模式。
双向链表 + state(锁状态)
CAS
AQS(AbstractQueuedSynchronizer)
atomic
同步队列
LockSupport.park()
条件队列
AbstractQueuedSynchronizer
避免了使线程进入内核态的阻塞状态
ReentrantLock
ReentrantReadWriteLock
StampedLock
任意处执行
一定继续执行源代码
park
LockSupport
释放锁资源
不一定继续执行原代码
在lock.lock()和lock.unlock之间
await
signal()
Condition
Locks
TimeUnit
这个类使一个线程等待其他线程各自执行完毕后再执行
CountDownLatch
它的作用就是会让所有线程都等待完成后才会继续下一步行动。
循环栅栏
CyclicBarrier
Semaphore
转移时使用数组
nextTable
正在转移
MOVED
返回节点数组的指定位置的节点的原子操作
tabAt
cas原子操作,在指定位置设定值
casTabAt
原子操作,在指定位置设定值
setTabAt
·在ConcurrentHashMap中,同步处理主要是通过Synchronized和unsafe两种方式来完成的。 ·在取得sizeCtl、某个位置的Node的时候,使用的都是unsafe的方法,来达到并发安全的目的 ·当需要在某个位置设置节点的时候,则会通过Synchronized的同步机制来锁定该位置的节点。 ·在数组扩容的时候,则通过处理的步长和fwd节点来达到并发安全的目的,通过设置hash值为MOVED ·当把某个位置的节点复制到扩张后的table的时候,也通过Synchronized的同步机制来保证现程安全
ConcurrentHashMap
ArrayBlockingQueue
双向链表的队列
LinkedBlockingDeque
支持延时获取元素的无界阻塞队列
PriorityQueue
DelayQueue
支持优先级的无界阻塞队列
PriorityBlockingQueue
阻塞队列
线程执行结果
outcome
callable
implements RunnableFuture
FutureTask
DelayedFutureTask
DelayedWorkQueue
ScheduledThreadPoolExecutor
避免了处理任务时创建销毁线程开销的代价
避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
池化思想管理线程
将任务提交和任务执行进行解耦
Executor
ExecutorService
扩充执行任务的能力,补充可以为一个或一批异步任务生成 Future 的方法
提供了管控线程池的方法,比如停止线程池的运行
abstractExecutorService
线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。
用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。
ctl共包括32位。其中,高3位表示"线程池状态",低29位表示"线程池中的任务数量"。
运行状态 (runState) 和线程数量 (work\u0002erCount)
(01) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!
高3位值是111
RUNNING
(01) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(02) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN
对应的高3位值是000
SHUTDOWN
(01) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(02) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
高3位值是001
STOP
(01) 状态说明:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(02) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
高3位值是010
TIDYING
高3位值是011
(01) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(02) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
TERMINATED
状态
生命周期管理
shutdown()
RejectedExecutionException
当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常
AbortPolicy
当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
CallerRunsPolicy
当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardOldestPolicy
当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务
DiscardPolicy
拒绝策略
(1)并行执行子任务,提高响应速度。这种情况下,应该使用同步队列,没有什么任务应该被缓存下来,而是应该立即执行
(2)并行执行大批次任务,提升吞吐量。这种情况下,应该使用有界队列,使用队列去缓冲大批量的任务,队列容量必须声明,防止任务无限制堆积。
extends AbstractQueuedSynchronizer implements Runnable
Worker
丢弃策略
DiscardPolicy
DiscardOldestPolicy
不是立刻就被关闭,因为这时线程池中可能还有任务正在执行,或是任务队列中有正在等待的任务,它会等待正在执行的任务和队列中等待的任务执行完毕后才彻底关闭。注意:调用 shutdown() 方法后如果还有新的任务被提交,线程池则会根据拒绝策略直接拒绝提交的任务。
shutdown
shutdownNow() 表示立刻关闭的意思。在执行 shutdownNow 方法之后,首先会给所有线程池中的线程发送 interrupt 中断信号尝试中断这些任务的执行,然后将任务队列中的任务转移到一个 List 中并返回注意:即便我们调用了 shutdownNow 方法,如果被中断的线程对于中断信号不理不睬,那么依然有可能导致任务不会停止。所以我们自己编写的线程应当具有响应中断信号的能力。
shutdownNow
isTerminated()方法可以检测线程池是否真正“终结”了,这不仅代表线程池已关闭,同时代表线程池中的所有任务都已经都执行完毕了。
isTerminated()
关闭线程池
ThreadPoolExecutor
线程池
concurrent
Iterator
过滤流中的某些元素
filter
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
map
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
flatMap
如同于 map,能得到流中的每一个元素。但 map 接收的是一个 Function 表达式,有返回值;而 peek 接收的是 Consumer 表达式,没有返回值。
peek
指元素的处理不受之前元素的影响
无状态
通过流中元素的 hashCode() 和 equals() 去除重复元素
distinct
获取 n 个元素
跳过 n 元素,配合 limit(n) 可实现分页
skip
自然排序,流中元素需实现 Comparable 接口
sorted
定制排序,自定义 Comparator 排序器
sorted(Comparator com)
指该操作只有拿到所有元素之后才能继续下去
有状态
中间操作
接收一个 Collector 实例,将流中元素收集成另外一个数据结构。
collect
规约操作
reduce
返回流中元素最大值
max
返回流中元素最小值
min
返回流中元素的总个数
count
指必须处理所有元素才能得到最终结果
非短路操作
接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回 true,否则返回 false
anyMatch
接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回 true,否则返回 false
noneMatch
接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回 true,否则返回 false
allMatch
返回流中第一个元素
findFirst
返回流中的任意元素
findAny
指遇到某些符合条件的元素就可以得到最终结果
短路操作
结果操作
使用 Arrays 中的 stream() 方法,将数组转成流:
常用创建方法
Stream
线程集合和阻塞队列
双精度快速排序+插入排序
sort源码
BitSet
java.util
Contended
vm
jdk.internal
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
PostConstruct
@PreConstruct修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前
PreConstruct
javax.annotation
PreparedStatement
Statement
java.sql
unpark
挂起
Unsafe
sun.misc
FileChannel, 从文件中读写数据。
DatagramChannel,通过UDP读写网络中的数据。
SocketChannel,通过TCP读写网络中的数据。
ServerSocketChannel,可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
Channel通道类似流
数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性
Buffer用于和NIO通道进行交互。数据从通道读入到缓冲区,从缓冲区写入到通道中
Selector(选择区)用于监听多个通道的事件
reactor
Selector
NIO(new IO)
ByteArrayInputStream
inputStream
对byte类型数据进行写入的类 相当于一个中间缓冲层,将类写入到文件等其他outputStream。它是对字节进行操作,属于内存操作流
ByteArrayOutputStream
outputStream
字节流
InputStreamReader类是从字节流到字符流的桥接器:它使用指定的字符集读取字节并将它们解码为字符
每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节。 为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节。为了获得最高效率,请考虑在BufferedReader中包装InputStreamReader
InputStreamReader
Reader
Writer
字符流
Old IO
NIO处理数据是以数据块为单位,而传统IO流是以字节为单位
NIO 非阻塞
NIO 选择器
IO
源码
Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以互相类比,但此处仅是虚拟机内存的一部分)。
主内存
每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成
工作内存
•lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
•unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
•read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
•load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
•use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
•assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
•store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
•write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
内存间的交互
java内存模型
程序计数器是一块很小的内存空间,用于记录下一条要运行的指令。每个线程都需要一个程序计数器,各个线程之中的计数器相互独立,是线程中私有的内存空间
•程序计数器(PC)
java虚拟机栈也是线程私有的内存空间,它和java线程同一时间创建,保存了局部变量、部分结果,并参与方法的调用和返回
•java虚拟机栈
本地方法栈和java虚拟机栈的功能相似,java虚拟机栈用于管理Java函数的调用,而本地方法栈用于管理本地方法的调用,但不是由Java实现的,而是由C实现的
•本地方法栈
•java堆
也被称为永久区,与堆空间相似,被JVM中所有的线程共享。方法区主要保存的信息是类的元数据,方法区中最为重要的是类的类型信息、常量池、域信息、方法信息,其中运行时常量池就在方法区,对永久区的GC回收,一是GC对永久区常量池的回收;二是永久区对元数据的回收
•方法区
VM stack
native stack
program counter stack
stack
eden
s1
s2
Young
tenured
Humongous
old
symbolc Relerence
Literal
runtime constant pool
heap
compile code
comprssed class space
feild&Method data
meta
JNI Memory
Direct Memory
stack Memory
JIT Compile
JIT code
code cache
non-heap
内存结构
内存模型JMM
当应用程序请求分配内存时,GC 负责提供内存。提供内存的过程应尽可能快
GC 检测应用程序不再使用的内存。这个操作也应当十分高效,不应消耗太多时间。这种不再使用的内存称为“垃圾”;
GC 将同一块内存再次提供给应用程序,最好是“实时”,也就是要快。
基本功能
JDK 8 以及更早版本的默认回收期
版本
吞吐量
专注
多线程的stop-the-world压缩和分代回收
Parallel GC 是 JDK 8 以及更早版本的默认回收期。它专注于吞吐量,尽快完成工作,而很少考虑延迟(暂停)。
Parallel GC 会在 STW(全局暂停)期间,以更紧凑的方式,将正在使用中的内存移动(复制)到堆中的其他位置,从而制造出大片的空闲内存区域。当内存分配请求无法满足时就会发生 STW 暂停,然后JVM完全停止应用程序运行,投入尽可能多的处理器线程,让垃圾回收算法执行内存压缩工作,然后分配请求的内存,最后恢复应用程序执行。
解释
Parallel GC
ParNew收集器是Serial收集器的多线程版本
ParNew
Concurrent Mark Sweep 并发、使用标记-清除算法的gc。
CMS以获取最小停顿时间为目的。在一些对响应时间有很高要求的应用或网站中,用户程序不能有长时间的停顿,CMS 可以用于此场景。
进行可达性分析,标记GC ROOT能直接关联到的对象。注意是直接关联间接关联的对象在下一阶段标记。
1 初始标记(STW)
该阶段进行GC ROOT TRACING,在第一个阶段被暂停的线程重新开始运行。由前阶段标记过的对象出发,所有可到达的对象都在本阶段中标记。
2 并发标记
CMS是以获取最短停顿时间为目的的GC。重标记需要STW(Stop The World),因此重标记的工作尽可能多的在并发阶段完成来减少STW的时间。此阶段标记从新生代晋升的对象、新分配到老年代的对象以及在并发阶段被修改了的对象。
3 并发预清理
4 重标记(STW)
并发清理。用户线程被重新激活,同时清理那些无效的对象。
5 并发清理
CMS清除内部状态,为下次回收做准备。
6 重置
步骤
CMS + ParNew
1.CMS只能回收老年代
2.CMS在old gc的时候会回收整个Old区
CMS
JDK6~ 9
平衡
多线程的stop-the-world压缩、并发活跃分代回收
G1 的长时间操作会与应用程序并行进行,即通过多线程方式,在应用程序运行时执行。这样可以大幅度减少暂停,代价是整体的吞吐量会降低一点。
G1收集器的总体效果是好于CMS的,有更好的自我调节能力而G1从JDK9开始才是默认垃圾回收器。所以JDK8的情况下,最好主动设置G1垃圾回收器:-XX:+UseG1GC
1.G1同时回收老年代和年轻代
-XX:MaxGCPauseMillis
-XX:InitiatingHeapOccupancyPercent
-XX:ParallelGCThreads
XX:G1ReservePercent
2.G1的分代更多是逻辑上的概念,G1将内存分成多个等大小的region,Eden/ Survivor/Old分别是一部分region的逻辑集合,物理上内存地址并不连续。
G1
JDK 15
延迟
ZGC:JDK11 中推出的一款低延迟垃圾回收器,适用于大内存低延迟服务的内存管理和回收,SPECjbb 2015 基准测试,在 128G 的大堆下,最大停顿时间才 1.68 ms,停顿时间远胜于 G1 和 CMS。
多重映射
子主题 1
染色指针
读屏障
ZGC
JDK 12
Shenandoah GC
垃圾回收器
年轻代引用老年代的这种跨代不需要单独处理。但是老年代引用年轻代的会影响young gc
这种跨代需要处理。为了避免在回收年轻代的时候扫描整个老年代,需要记录老年代对年轻代的引用,young gc的时候只要扫描这个记录。CMS和G1都用到了Card Table
跨代引用
并发过程的对象变化
Promotion Failure和Concurrent Mode Failure年轻代晋升的时候老年代没有足够的连续空间容纳,很有可能是内存碎片导致的;后者是在并发过程中jvm觉得在并发过程结束前堆就会满了,需要提前触发Full GC
CMS Full GC
1. Evacuation的时候没有足够的to-space来存放晋升的对象;2. 并发处理过程完成之前空间耗尽。这两个原因跟CMS类似。
G1 Full GC
Full GC
Thread Local Allocation Buffer 的简写,基于 CAS 的独享线程(Mutator Threads)可以优先将对象分配在 Eden 中的一块内存,因为是Java 线程独享的内存区没有锁竞争,所以分配速度更快,每个 TLAB 都是一个线程独享的
TLAB
中文翻译为卡表,主要是用来标记卡页的状态,每个卡表项对应一个卡页。当卡页中一个对象引用有写操作时,写屏障将会标记对象所在的卡表状态改为 dirty,卡表的本质是用来解决跨代引用的问题。
Card Table
GC
1.将转移到老年代的对象数量降低到最小;
2.减少full GC的执行时间;
目的
减少使用全局变量和大对象;
调整新生代的大小到最合适;
设置老年代的大小为最合适;
选择合适的GC收集器
措施
Minor GC执行时间不到50ms;Minor GC执行不频繁(约10s一次);Full GC执行时间不到1s;Full GC执行不算频繁(不低于10m一次)
指标
性能调优
Bootstrap ClassLoader
Extention ClassLoader
Appclass Loader
编写一个类继承自ClassLoader抽象类。
复写它的findClass()方法。
在findClass()方法中调用defineClass()。
子主题 4
自定义ClassLoader
ClassLoader
装载:(loading)找到class对应的字节码文件。
连接:(linking)将对应的字节码文件读入到JVM中。
初始化:(initializing)对class做相应的初始化动作
类加载步骤
空闲链表(free list):通过额外的存储记录空闲的地址,将随机 IO 变为顺序IO,但带来了额外的空间消耗。
碰撞指针(bump pointer):通过一个指针作为分界点,需要分配内存时,仅需把指针往空闲的一端移动与对象大小相等的距离,分配效率较高,但使用场景有限
Java 中对象地址操作主要使用 Unsafe 调用了 C 的 allocate 和 free 两个方法
分配对象
零地址指令,它们依赖操作数栈进行工作
Java 字节码的操作指令(OpCode)被固定为一个字节
Java 代码必须通过 Java 编译器将其转换成虚拟机所能识别的指令序列,也称为 Java 字节码
字节码
解释器是软件来实现的,主要是为了实现同一份 Java 字节码可以在不同的硬件平台上运行
将汇编指令转换成机器指令由硬件直接实现,这一步速度是很快的,当然 JVM 为了提高运行效率也可以将某些热点代码(一个方法内的代码)一次全部编译成机器指令后然后在执行,也就是和解释执行对应的即时编译(JIT), JVM 启动的时候可以通过 -Xint 和 -Xcomp 来控制执行模式。
JVM 通过类加载器加载 class 文件里的字节码后,会通过解释器解释成汇编指令,最终再转译成 CPU 可以识别的机器指令
编译
引用计数法(Reference Counting):对每个对象的引用进行计数,每当有一个地方引用它时计数器 +1、引用失效则 -1,引用的计数放到对象头中,大于 0 的对象被认为是存活对象。虽然循环引用的问题可通过 Recycler 算法解决,但是在多线程环境下,引用计数变更也要进行昂贵的同步操作,性能较低,早期的编程语言会采用此算法
引用计数法
可达性分析,又称引用链法(Tracing GC):从 GC Root 开始进行对象搜索,可以被搜索到的对象即为可达对象,此时还不足以判断对象是否存活 / 死亡,需要经过多次标记才能更加准确地确定,整个连通图之外的对象便可以作为垃圾被回收掉。目前 Java 中主流的虚拟机均采用此算法。
可达性分析
识别垃圾
Mark-Sweep(标记 - 清除):回收过程主要分为两个阶段,第一阶段为追踪(Tracing)阶段,即从 GC Root 开始遍历对象图,并标记(Mark)所遇到的每个对象,第二阶段为清除(Sweep)阶段,即回收器检查堆中每一个对象,并将所有未被标记的对象进行回收,整个过程不会发生对象移动。整个算法在不同的实现中会使用三色抽象(Tricolour Abstraction)、位图标记(BitMap)等技术来提高算法的效率,存活对象较多时较高效
Mark-Sweep(标记 - 清除)
Mark-Compact(标记 - 整理):这个算法的主要目的就是解决在非移动式回收器中都会存在的碎片化问题,也分为两个阶段,第一阶段与 Mark-Sweep类似,第二阶段则会对存活对象按照整理顺序(Compaction Order)进行整理。主要实现有双指针(Two-Finger)回收算法、滑动回收(Lisp2)算法和引线整理(Threaded Compaction)算法等
Mark-Compact(标记 - 整理)
Copying(复制):将空间分为两个大小相同的 From 和 To 两个半区,同一时间只会使用其中一个,每次进行回收时将一个半区的存活对象通过复制的方式转移到另一个半区。有递归(Robert R. Fenichel 和 Jerome C. Yochelson提出)和迭代(Cheney 提出)算法,以及解决了前两者递归栈、缓存行等问题的近似优先搜索算法。复制算法可以通过碰撞指针的方式进行快速地分配内存,但是也存在着空间利用率不高的缺点,另外就是存活对象比较大时复制的成本比较高
Copying(复制)
收集算法
ParNew:一款多线程的收集器,采用复制算法,主要工作在 Young 区,可以通过 -XX:ParallelGCThreads 参数来控制收集的线程数,整个过程都是STW 的,常与 CMS 组合使用
CMS:以获取最短回收停顿时间为目标,采用“标记 - 清除”算法,分 4 大步进行垃圾收集,其中初始标记和重新标记会 STW ,多数应用于互联网站或者 B/S 系统的服务器端上,JDK9 被标记弃用,JDK14 被删除,详情可见
分代收集器
G1:一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能地满足垃圾收集暂停时间的要求
Shenandoah:由 Red Hat 的 一 个 团 队 负 责 开 发, 与 G1 类 似, 基 于Region 设计的垃圾收集器,但不需要 Remember Set 或者 Card Table 来记录跨 Region 引用,停顿时间和堆的大小没有任何关系。停顿时间与 ZGC接近
分区收集器
收集对象
Invokevirtual:根据虚方法表调用虚方法。
invokeinteface:调用接口方法
invokestatic:调用静态方法
invokedynamic 动态语言支持 lamda
JVM
jconsole
jstack(查看线程)、
接 top -hp 查看线程的16进制
jstack -l
得到运行java程序的内存分配的详细情况。
通过 mat(Eclipse Memory Analysis Tools)导入 dump 文件进行分析,内存泄漏问题一般我们直接选 Leak Suspects 即可
jmap -histo pid(查看实例)
jmap(查看内存)
可以观察到classloader,compiler,gc相关信息
jstat(性能分析)
运行
休眠
等待
驻留
监视
监控线程状态
JDK tools
Lambda
函数式编程
Optional
Streams
MetaSpace代替了永久代
接口可以添加默认方法和静态方法
方法引用
8
不用 char[] 来存储啦,改成了 byte[] 加上编码标记
String存储结构
G1为JVM默认垃圾收集器
接口方法可以使用private来修饰
支持http2.0的API
9
并行Full GC,来优化G1的延迟
局部变量类型推断,类似JS可以通过var来修饰局部变量,编译之后会推断出值的真实类型
10
基于OS、JVM和JDK的事件产生的数据收集框架
Flight Recorder(飞行记录器)
11
Shenandoah GC
Switch 表达式表达式
12
17
版本升级特性
Linux进行OOM-killer机制
查看系统资源情况
/var/log/message
是否触发OOM
dump
当jvm出现致命错误时,会生成一个错误文件 hs_err_pid.log。
进程挂了
问题排查
java
c#
2.x
3.x
列表
元组
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等
classmethod
HTTP 请求库
request
从网页抓取数据,将 html 解析为对象进行处理
BeautifulSoup
通过正则表达式是用来匹配处理字符串
re
time
pandas
对大数组的数据进行高效处理。优化包括NumPy是在一个连续的内存块中存储数据,独立于其他Python内置对象,如此便可以加速数据索引的速度。
NumPy调用了大量的用C语言编写的算法库,使得其可以直接操作内存,不必进行Python动态语言特性所含有的前期类型检查工作,从而大大提高了运算速度。
NumPy所有独有的可以在整个数组上执行复杂的计算也能够大幅提高运算效率(基于NumPy的算法要比纯Python快10到100倍,甚至会快更多)
秩,即轴的数量或维度的数量
.dim
对象的尺度,对于矩阵来说,即n行m列
.shape
对象的个数,即n*m的值
.size
对象的类型
.dtype
根据起始值等间距的填充数据,形成数组
类似range函数,放回ndarray类型,元素从0到n-1
np.arange(n)
根据shape生成一个全1数组,shape是元组类型
np.ones(shape)
根据shape生成一个全0数组
np.zeros(shape)
根据shape生成一个数组,每个元素值都为val
创建一个正方的n*n单位矩阵,对角线全为1其余为0
np.eye(n)
数组的创建方法
Numpy
使用面向对象的编程方式来表示文件系统路径
pathlib
Union
类型检查
typing
logging
模块
爬虫
djingo
flask
web框架
文件作为脚本直接执行
main方法写在 声明方法后. 无法像java 可能是因为编译过程
if __name__ == '__main__':
conda install --channel https://conda.anaconda.org/conda-forge mnelab
channel
Anaconda
conda是包及其依赖项和环境的管理工具。
conda
pip是用于安装和管理软件包的包管理器。
pip
python
MFC
c++
后端
编程语言
用户是谁
单点突破
差异点
产品的定位
多个不同的角色互相依赖、和谐共处、不断进化
商业变现的机制
利益反馈
不同角色的“权责利”
生态
“最简单、最直观”的体验
产品体验
到具体的场景中去设计和检验产品
埋点
从用户的行为而不仅是口中获得产品反馈
品味和颜值
产品自传播
眼里不要只有“产品功能”,更要多多考虑“人”
产品
商业
机械电子工程
思维,计划和中央执行职能;运动执行;
额叶
体感知觉,视觉和体空间信息的整合
顶叶
语言功能和听觉感知,参与长期记忆和情感;
颞叶
视觉感知和处理
枕叶
布罗德曼分区
脑科学
生物学
PPG血容量脉搏
EDA皮肤电数据
RESP呼吸数据
20毫伏
ECG心电数据
SKT皮温数据
ACC人体姿态数据
spO2血氧饱和度
MEG脑磁数据
毫伏
肌电图
每当数千个神经元同时放电时,它们就会产生一个强大的电场,足以穿过组织、骨骼和头骨。最终,它可以在头部表面测量
大脑的生物电活动
金标准/湿电极脑电设备--NeurOne系统
人在想象自己肢体(或肌肉)运动但没有实际运动输出时,人的特定脑区仍会有激活,目前常见的运动想象部位为:左右,右手,双脚和舌头。通过分析脑电信号,检测识别不同脑区的激活效果来判断用户意图,进而实现人脑与外部设备之间的直接通信与控制。
运动想象数据
情绪识别数据
误差相关电位(ErrP)
视觉诱发电位(VEPs)
事件相关电位(ERPs)
慢皮质电位(SCPs)
休息状态音乐与EEG
眨眼/眼动Miscellaneous
临床脑电图
脑电信号
睡眠实验室中,Delta波被用来评估睡眠深度。节奏越强,睡眠越深。Delta波功率的增加(增加的增量波记录数量)被发现与内部工作记忆任务的注意力增加有关
Delta(1-4 Hz)
Theta与广泛的认知处理相关,例如记忆编码和检索以及认知工作量[2]。每当我们遇到困难的任务时(例如,从100开始倒数,或者当回忆起下班回家的路时),Theta波就会变得更加突出。Theta也与疲劳程度增加有关
Theta(4 – 7 Hz)
Alpha(7–12 Hz)
在运动区域内,随着我们计划或执行任何身体部位的运动,Beta频率会变得更强[5]。有趣的是,随着我们观察其他人的身体运动,Beta波的这种增加也很明显。我们的大脑似乎模仿了他们的肢体运动,这表明我们大脑中存在着一个复杂的“镜像神经元系统”,该系统可能与Beta频率协调
Beta(12 – 30 Hz)
一些研究人员认为,Gamma波反映了注意力的集中,并作为载波频率来促进大脑区域之间的数据交换。其他人则将伽玛与快速的眼球运动(所谓的微扫视)联系起来,它们被认为是感觉处理和信息吸收的组成部分
Gamma(> 30 Hz,通常为40 Hz)
脑电仪
SSVEP
P300
运动想象
情感识别
图灵脑机测试
BCI脑控机器人大赛
BCI脑机接口
《10-20国际标准导联系统》
eeglab中默认的文件是 standard-10-5-cap385,指的是按照国际10-5系统排布的一共有385个电极点信息的模板
1. 定位通道数据
2.删除无用数据
高通滤波
低通滤波
带通滤波
凹陷滤波
3.滤波
只是被试在接受到某个刺激,或者做出某个反应时那段事件的信号,因此,我们要根据我们打上的mark,讲数据切分
4分段
相对平静的状态,此时的脑电活动,代表了一个平静状态下的脑电活动。所以我们将这段时间内的脑电活动当成一个基线
5.基线校正
鼻尖参考
cz或头顶中央参考
乳突就是耳朵后面一小块突起的区域
单侧乳突参考
双侧乳突平均参考
排除眼电数据
全脑平均参考
参考电极
数值为通道和参考通道的电位差
6. 重参考
因为降低采样率会使我们丢失高频信息,使高频信息变得扭曲,所以最好在保留了我们感兴趣波段之后再去降低采样率,这样可以保证信号最大程度不会失真
降低采样率要在滤波之后
我们的采样率必须是我们想要分析的波段的两倍,比如我们想要分析60Hz的波,那数据采样率为120Hz就足够了。实际上,建议采样率最好在分析波段的三到四倍。
采样定理
7. 降低采样率
对通道进行校正操作,对数据不好的导联进行插值处理
横向操作
挑出数据不好的trials,删除掉。建议是先横向处理后再纵向剔除,尽量保留下更多的trials数
纵向
去除伪迹
以某通道周围几个通道的数据的平均值,来替代这个通道的数据,这种直接通过代码实现即可
8. 插值坏导
我们记录到的是头皮脑电,脑电帽上划出了一个个的点来表示位置,但是我们在FPz点记录到的数据,就真的是FPz点这个位置头皮下方的区域所产生的电活动?不一定。做过实验的都知道,眨眼会产生影响,左右看会产生影响,帽子戴得太紧导致肌肉紧张也会产生影响,还有左右的FP1,FP2处产生的电活动也会影响到FPz,甚至在离它最远的Oz点下方产生的电活动,也有可能对前方FPz点产生微弱的影响。因为真正的电活动产生于头皮下方颅骨内部,它经过了这么一层又一层的传播之后,不同源的电活动肯定会相互影响,从而导致某个记录点记录到的数据,混合了很多不同成分的电活动。而独立主成分分析,就是要对记录点的数据做一个逆运算,把每个记录点的数据, 分解成一个又一个的成分组成。然后我们再从中剔除掉伪迹成分,比如眨眼的成分,肌肉紧张的成分等,从而得到一个相对干净的数据。
9. 独立主成分分析
10 剔除坏段
EEG脑电数据预处理
EEG脑电数据处理
数十微伏
主要用于睡眠和梦境研究以及抑郁症和睡眠呼吸暂停综合征的诊断
EEG脑电数据
人因记录仪
复杂生理信号的研究资源
PhysioNet
人因工程
Geoffrey Hinton和他的学生Alex Krizhevsky
2012年. AlexNet在ImageNet比赛的胜出,他证明了深度学习在图像识别方面比其他计算机视觉的方法具备更大的优势
历史
当激活函数接近饱和区时,变化太缓慢,导数接近0,根据后向传递的数学依据是微积分求导的链式法则,当前导数需要之前各层导数的乘积,几个比较小的数相乘,导数结果很接近0,从而无法完成深层网络的训练
容易出现梯度消失(gradient vanishing)的现象
这会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响。以 f=sigmoid(wx+b)为例, 假设输入均为正数(或负数),那么对w的导数总是正数(或负数),这样在反向传播过程中要么都往正方向更新,要么都往负方向更新,导致有一种捆绑效果,使得收敛缓慢。
Sigmoid的输出不是0均值(zero-centered)
幂运算相对耗时
Sigmoid(S 形)
Tanh(双曲正切)
函数
激活函数
反向传播
调整参数的梯度
神经网络
多层感知器
基于图像任务的平移不变性(图像识别的对象在不同位置有相同的含义)设计的,擅长应用于图像处理等任务
卷积、池化、全连接
全卷积神经网络
AlexNet、VGG、Res-Net
Inception-Net、MobileNet
CNN调参技巧
卷积神经网络(CNN)
深度残差网络
自组织映射神经网络
受限玻尔兹曼机
前馈神经网络
能够将之前的信息与当前的任务联系起来,例如我们可以通过之前的视频帧了解当前帧的内容。 tip:有点实时计算引擎开窗的感觉..
双向网络
随着跨度的增加,RNN 变得难以对信息之间的关联进行有效学习
具有对长期依赖进行学习的能力
第一步是决定从上一个细胞状态中丢弃什么信息
遗忘门
决定在细胞状态中存储哪些新信息
输入门
输出门
增加或删除细胞状态中的信息
sigmoid 神经网络层
点乘运算
门结构
当前时刻输入
上一时刻隐藏层状态
上一时刻细胞状态
长短期记忆模型
循环神经网络RNN
递归神经网络
深度学习优化技巧
强化学习
反卷积
对抗损失
对抗神经网络网络GAN
胶囊网络结构
方向传递
计算量开销大,计算速度慢,不支持在线学习
遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度
批梯度下降
这个方法速度比较快,但是收敛性能不太好
每看一个数据就算一下损失函数,然后求梯度更新参数
随机梯度下降
batch_size
小批的梯度下降
batch
向前和向后传播中所有批次的单次训练迭代
训练过程中数据将被“轮”多少次
epoch
1个iteration等于使用batchsize个样本训练一次
iterations
深度学习
从经验E中,解决某一任务T,进行某一性能度量P
假设函数(hypothesis) h
训练集(training set)
标注
损失函数
分类
回归
序列标注
按数据分类
模式识别
函数回归
概率密度估计
由样本定义经验风险
学习的目标、期望风险最小化
机器学习问题
一元
多元
线性回归
非线性回归
回归问题
\tPerceptron\t感知器算法
Neural Network\t神经网络
Random Forests\t随机森林
Restricted Boltzmann Machines\t有限波尔兹曼机
朴素贝叶斯
KNN(K Nearest Neighbors)
分类算法
SVM\t支持向量机
计算机如何处理无穷特征
监督学习(supervise learning)
聚类算法
svd(奇异值分解)
鸡尾酒会算法
无监督学习(unsupervise learning)
增强学习
半监督学习
按监督分类
生成式模型
判别式模型
按模型分类
基本概念和分类
Logistic Regression\t逻辑回归
松弛变量
核函数
惩罚因子
支持向量机
决策树
经典算法
对丢失数据不是很敏感,算法比较简单,经常用于文本分类。
小规模数据上表现良好
Bayesian\t贝叶斯
隐马尔科夫
最大熵
条件随机场
概率图模型
监督学习模型
层次聚类
KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类别来判断x属于哪个类别
从选取一个较小的K值开始,不断增加K的值,然后计算验证集合的方差,最终找到一个比较合适的K值。
交叉验证
非参的意思并不是说这个算法不需要参数,而是意味着这个模型不会对数据做出任何的假设,与之相对的是线性回归(我们总会假设线性回归是一条直线)。也就是说KNN建立的模型结构是根据数据来决定的,这也比较符合现实的情况,毕竟在现实中的情况往往与理论上的假设是不相符的。
惰性又是什么意思呢?想想看,同样是分类算法,逻辑回归需要先对数据进行大量训练(tranning),最后才会得到一个算法模型。而KNN算法却不需要,它没有明确的训练数据的过程,或者说这个过程很快。
非参的,惰性的算法模型
K均值聚类
高斯混合模型
主题模型
非监督学习模型
Bagging
Boosting
随机森林
GBDT
集成学习
降维算法 &降维/维约简
采样
策略求值函数
根据值函数来更新策略
经典机器学习模型
特征离散与归一化
特征组合
特征选择
词迁入表示
特征工程
OCTAVE
MATLAB
工具
正则化
EM算法
主要思想是利用弱分类器(决策树)迭代训练以得到最优模型 采用加法模型(即基函数的线性组合),以及不断减小训练过程产生的残差来达到将数据分类或者回归的算法。
点击率预测,搜索排序
解决 GBDT 在海量数据遇到的问题
直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图
基于 Histogram 的决策树算法
带深度限制的 Leaf-wise 的叶子生长策略
直方图做差加速
直接支持类别特征(Categorical Feature)
Cache 命中率优化
基于直方图的稀疏特征优化
多线程优化
LightGBM
对所有特征都按照特征的数值进行预排序
遍历分割点的时候用O(#data)的代价找到一个特征上的最好分割点
找到一个特征的分割点后,将数据分裂成左右子节点
决策树算法
XGBoost
GBDT (Gradient Boosting Decision Tree)(梯度下降树)
梯度下降
梯度验证
Momentum
AdaGrad
Adam
优化算法
计算机视觉
自然语言处理
推荐系统
计算广告
智能游戏
应用
局部极小
维数灾难
过拟合
模型优化点
机器学习
neo4j(见数据库分支)
图数据库
知识图谱
为了解决因维数过大而导致计算能力不足的缺陷,其实质是特征向量内积的平方在机器学习中
在许多情况下,我们希望我们的模型学习非线性模型。通常的方法是选择函数φ(x)将X映射到另一个空间。这里的核心是如何选择φ(x)
内核功能核函数
中文自动分词
词性标注
句法分析
文本分类
自然语言生成(NLG)
信息检索
信息抽取
文字校对
问答系统
机器翻译
自动摘要
文字蕴涵
语言是没有规律的,或者说规律是错综复杂的。
语言是可以自由组合的,可以组合复杂的语言表达。
语言是一个开放集合,我们可以任意的发明创造一些新的表达方式。
语言需要联系到实践知识,有一定的知识依赖。
语言的使用要基于环境和上下文。
难点
分词
词干提取
词形还原
命名实体识别
分块
英文
去停用词
中文
语料预处理
单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个
词集
在词集的基础上如果一个单词在文档中出现不止一次,统计其出现的次数(频数)
词袋
用以评估某一字词对于一个文件集或一个语料库的重要程度
TF * IDF。TF表示词条在文档d中出现的频率。IDF(inverse document frequency,逆向文件频率)的主要思想是:如果包含词条t的文档越少,也就是n越小,IDF越大,则说明词条t具有很好的类别区分能力
TF-IDF模型(term frequency–inverse document frequency,词频与逆向文件频率)
一个池塘有10条鱼和20只小龙虾,渔夫撒网打鱼,结果捞上来8条鱼12只小龙虾,那么准确率为8/(8+12)=40%,召回率为8/10=80%。
准确率
召回率
全体预测正确占全部样本的比例
准确度
F1-Score也是对准确率和召回率的一个均衡评价
F1-Score
ROC(Receiver Operating Characteristic Curve)受试者工作特征曲线,以真阳性率为纵坐标,假阳性率为横坐标绘制的曲线,是反映灵敏性和特效性连续变量的综合指标。一般认为ROC越光滑说明分类算法过拟合的概率越低,越接近左上角说明分类性能越好。
ROC
AUC(Area Under the Receiver Operating Characteristic Curve)就是量化衡量ROC分类性能的指标
AUC
预测真 实际真
预测假 实际假
预测真 实际假
混淆矩阵
Bilingual evaluation understudy
分数越接近1,说明翻译的质量越高
基于精确率(Precision)
BLEU
基于召回率
在 N-gram 上计算召回率
ROUGE-N
考虑了机器译文和参考译文之间的最长公共子序列
ROUGE-L
改进了ROUGE-L,用加权的方法计算最长公共子序列
ROUGE-W
ROUGE
评定标准
选择分类器
传统
CBOW模型能够根据输入周围n-1个词来预测出这个词本身
CBOW(Continuous Bag-Of-Words)
Skip-gram模型能够根据词本身来预测周围有哪些词
Skip-Gram
可以把对文本内容的处理简化为K维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度
Word2Vec模型
词表征为实数值向量
设计模型
模型训练
深度学习NLP
流程
transformer
NLP
基于特征的预测
基于提议的预测
Transformer模型
预测附近车辆的下一步轨迹,对于自动驾驶汽车系统了解周围环境并做出信息决策至关重要
多模态预测问题
预测一个物体与相机接触的时间
TTC
场景深度估计
光流估计方法
自动驾驶
CNN
Vision Transformer(ViT)
人工智能应用
CNN中没有可用的空间信息
一种新的网络
将神经元替换为胶囊
包括空间几何信息
根据图像(渲染结果)反推出物体的信息
逆渲染
胶囊网络
行业专家经验很重要,否则就要做 EDA 和 UBA 分析
将行业专家的知识经验转化为特征工程,也许比上亿数据训练更有效果。
训练模型的目的是为了预测更精准,而不是覆盖所有历史数据
分布式训练框架
流式处理增量处理
大数据量
Cross Entropy Loss Function(交叉熵损失函数)
Classification Error(分类错误率)
模型一开始训练时,学习速率非常慢
Mean Squared Error (均方误差)
通过使用低精度算术降低了所需的资源
半精度浮点格式(FP16)使用16位,而单精度(FP32)使用32位。 降低所需的内存可以训练更大的模型或训练时使用更大的batch size
减少所需的内存量
计算的次数或者数据存储的存储十分影响算法的执行时间。半精度使用单精度的一半内存访问,从而降低了在存储层方面所花费的时间。 与单精度相比,NVIDIA GPU的半精度算术吞吐量最多提高了8倍,从而加快了数学受限层的速度。
缩短训练或推理时间
混合精度训练
经验
人工智能
霍夫变换
图像识别
目标检测
图像到数字
sobel算子
roberts算子
prewitt算子
log算子
cannny算子
边缘检测
图像切割
灰度级矫正
灰度变换
直方图均衡化、规定化
点运算
邻域平均法
中值滤波
降噪消除法
梯度倒数加权
选择掩膜平滑
图像平滑
梯度化
拉普拉斯算子
掩膜匹配法
统值插值法
图像锐化
邻域增强
空域
同态滤波
带阻滤波
频域
双边滤波
导向滤波
图像增强
图像降噪
图像翻译
图像聚类
图像到图像
R - CNN
残差网络
判别模型
GAN
自编码机、波尔兹曼机
生成模型
输入图像中一个小区域中像素加权平均后成为输出图像中的每个对应像素,其中权值由一个函数定义,这个函数称为卷积核
卷积核
图像处理
HA集群
环境搭建
Maps input key/value pairs to a set of intermediate key/value pairs. 将输入键/值对映射到一组中间键/值对。
The Hadoop Map-Reduce framework spawns one map task for each {@link InputSplit} generated by the {@link InputFormat} for the job. Hadoop Map-Reduce框架为作业的InputFormat生成的每个InputSplit生成一个map作业
Comparator
Mapper.java
combiner
hadoop-mapreduce-client-core
hadoop-mapreduce-client
hadoop-mapreduce-project
fs.trash.interval 垃圾箱清理文件间隔
dfs.namenode.handle.count hadoop启动任务线程数
mapreduce.tasktraker.http.threads map和reduce之间通过http传输数据 传输的并行线程数
core-site.xml
配置优化
系统优化
combiner的个数尽量同reduce相同,数据类型保持一直,可以减少拆包和封包进度
代码优化
namenode挂了。先分析宕机后的损失,宕机后直接导致client无法访问,内存中的元数据丢失,但是硬盘中的元数据应该还存在,如果只是节点挂了,重启即可,如果是机器挂了,重启机器后看节点是否能重启,不能重启就要找到原因修复了。但是最终的解决方案应该是在设计集群的初期就考虑到这个问题,做namenode的HA。
namenode节点
Datanode宕机了后,如果是短暂的宕机,可以实现写好脚本监控,将它启动起来。如果是长时间宕机了,那么datanode上的数据应该已经被备份到其他机器了,
datanode节点
每隔一段时间,会由secondary namenode将namenode上积累的所有edits和一个最新的fsimage下载到本地
sn的主要职责是执行checkpoint操作
secondarynamenode
客户端
HDFS协议(RPC协议、流式接口协议:HTTP和TCP)
核心模块
HDFS
第一阶段是把输入文件按照一定的标准分片(InputSplit),每个输入片的大小是固定的。默认情况下,输入片(InputSplit)的大小与数据块(Block)的大小是相同的。如果数据块(Block)的大小是默认值64MB,输入文件有两个,一个是32MB,一个是72MB。那么小的文件是一个输入片,大文件会分为两个数据块,那么是两个输入片。一共产生三个输入片。每一个输入片由一个Mapper进程处理。这里的三个输入片,会有三个Mapper进程处理。
第二阶段是对输入片中的记录按照一定的规则解析成键值对。有个默认规则是把每一行文本内容解析成键值对。“键”是每一行的起始位置(单位是字节),“值”是本行的文本内容。
第三阶段是调用Mapper类中的map方法。第二阶段中解析出来的每一个键值对,调用一次map方法。如果有1000个键值对,就会调用1000次map方法。每一次调用map方法会输出零个或者多个键值对。
第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。分区是基于键进行的。比如我们的键表示省份(如北京、上海、山东等),那么就可以按照不同省份进行分区,同一个省份的键值对划分到一个区中。默认是只有一个区。分区的数量就是Reducer任务运行的数量。默认只有一个Reducer任务。
第六阶段Reducer任务过程
决定Mapper的数量HDFS中数据的存储是以块的形式存储的,数据块的切分是物理切分,而split是在Block的基础上进行的逻辑切分。每一个split对应着一个Mapper进程。每个Split中的每条记录调用一次map方法。一个文件被切分成多少个split就有多少个Mapper进程。
Mapper任务过程
每个Reducer任务是一个java进程。Reducer任务接收Mapper任务的输出,归约处理后写入到HDFS中,可以分为如下图所示的几个阶段。
第一阶段是Reducer任务会主动从Mapper任务复制其输出的键值对,Mapper任务可能会有很多,因此Reducer会复制多个Mapper的输出。
第二阶段是把复制到Reducer本地数据,全部进行合并,即把分散的数据合并成一个大的数据,再对合并后的数据排序。
第三阶段是对排序后的键值对调用reduce方法,键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到HDFS文件中。
决定Reducer的数量如: 10个key可以有1个reducer,但是这个reducer只能一次处理一个key,也就是说处理10次 10个key可以有大于10个reducer ,只不过有的reduce不进行key的处理。 10个key有10个reducer,这是最合理的分配,达到并行计算。相同的key如何识别到指定的reducer进行计算呢?对输出的key、value进行分区。总结:Mapper阶段是并行读取处理的它的数量是由切片的数量决定的;Reducer阶段可以不并行,他的数量的是通过key进行规划,由人来决定。
Reducer任务过程
将maptask输出的处理结果数据,分发给reducetask,并在分发的过程中,对数据按key进行了分区和排序;
shuffle
mapreduce
资源调度
ResourceManager
ApplicationMaster
NodeManager
container
yarn
datanode首次加入cluster时候 版本号不一致
问题处理
hadooop
hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行。
用户接口/界面
元存储
HiveQL处理引擎
执行引擎
HDFS 或 HBASE
内部表
外部表
分区表
分桶表
视图
hive
Row Key(行键)
Column Family(列族)
Column Qualifier(列限定符)
Column(列)
Cell
核心概念
HMaster
Region Servers
集群架构
Key-Value存储 会覆盖
唯一原则
HBase的Rowkey是按照ASCII有序设计的
排序原则
Reverse反转
Salt加盐
Mod
Region热点问题
散列原则
Rowkey是一个二进制,Rowkey的长度被很多开发者建议说设计在10~100个字节,建议是越短越好
长度原则
Row Key 设计原则
hbase
分布式的,开放源码的分布式应用程序协调服务文件系统+监听通知机制
这个属性表示心跳时间间隔,Leader用来监听Follower的心跳,一旦挂掉一半以上Follower,会通过Zab(消息原子广播)协议使集群所有个体状态转变为Looking(等待选举新任Leader)。此属性除了用来监听之外,还对后面的同步时间和初始化时间有影响,系统默认为2000毫秒。
tickTime
集群中的follower服务器与Leader服务器之间初始连接时能容忍的最多心跳数(tickTime的数量),系统默认为10。
initLimit
集群中Follower服务器跟Leader服务器之间的请求和答应最多能容忍的心跳数(tickTime的数量),系统默认为5。
syncLimit
此属性表示一个路径,用来存放数据快照(即zookeeper目录)、myid文件和日志文件夹,系统默认为/tmp/data,根据自己zk安装路径进行修改并创建data目录。
dataDir
zk客户端端口号,默认2181。
clientPort
用来存放zk写日志。
dataLogDir
此处是关于zk集群的配置,index代表自定义的各服务器序号(对应写在各自机器的myid中),IP即各服务器IP,A表示集群服务器之间通讯组件的端口,B表示选举组件的端口。如果配成集群,则zk状态(mode)为leader或follower;如果只配一个单机,mode为standalone。
server.index=IP:A:B
zoo.cfg配置
安装部署
leader
follower
observer
各节点角色
永久节点
临时节点
有序节点
ZNODE
Permissions
id
Schemes
ACL权限
curator
会话
Watcher
崩溃恢复
消息广播
ZAB协议
paxos算法
发布订阅
分布式锁
master选举
核心原理
zookeeper
topic通过文件存储,partition是目录
分区在存储层面可以看作一个可追加的日志(Log)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。
消息是顺序append log方式存储
消息冗余多台beoker存储,多台机器就叫一个replica集合
replica集合中,需要选出1个leader,剩下的是follower。也就是master/slave。
消息会存放一个星期,才会被删除。并且在一个partion里面,消息是按序号递增的顺序存放的,因此消费者可以回退到某一个历史的offset,进行重新消费。
offset 是消息在分区中的唯一标识,Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,Kafka 保证的是分区有序性而不是主题有序性。
offset回退机制
一个topic只用一个partition,但这样很显然限制了灵活性。
所有发送的消息,用同一个key,这样同样的key会落在一个partition里面。
轮询分区策略
随机分区策略
之所以要分成多个partition,是为了提高并发度,多个partition并行的进行发送/消费,但这却没有办法保证消息的顺序问题
操作系统本身是有page cache的。即使我们用无缓冲的io,消息也不会立即落到磁盘上,而是在操作系统的page cache里面。操作系统会控制page cache里面的内容,什么时候写回到磁盘。在应用层,对应的就是fsync函数。我们可以指定每条消息都调用一次fsync存盘,但这会较低性能,也增大了磁盘IO。也可以让操作系统去控制存盘。
消息刷盘
Topics And Partition(主题与分区)
消费者去broker pull消息
同步发送 异步发送
RecordAccimator
Metadata
Producer 端压缩
Broker 端保持
Consumer 端解压缩
生产者压缩算法
Producer 发送消息的过程如下图所示,需要经过拦截器,序列化器和分区器,最终由累加器批量发送至 Broker。
配置客户端,创建消费者
订阅主题
拉去消息并消费
提交消费位移
关闭消费者实例
Kafka Consumer 线程不安全,单线程消费,多线程处理
Kafka 有消费组的概念,每个消费者只能消费所分配到的分区的消息,每一个分区只能被一个消费组中的一个消费者所消费,所以同一个消费组中消费者的数量如果超过了分区的数量,将会出现有些消费者分配不到消费的分区
Producers And Consumers(生产者与消费者)
组成员发生变更(新 consumer 加入组、已有 consumer 主动离开组或已有 consumer 崩溃了——这两者的区别后面会谈到)
订阅主题数发生变更
订阅主题的分区数发生变更
rebalance 本质上是一种协议,规定了一个 consumer group 下的所有 consumer 如何达成一致来分配订阅 topic 的每个分区。比如某个 group 下有 20 个 consumer,它订阅了一个具有 100 个分区的 topic。正常情况下,Kafka 平均会为每个 consumer 分配 5 个分区。这个分配的过程就叫 rebalance。
Kafka 默认提供了两种分配策略:Range 和 Round-Robin。当然 Kafka 采用了可插拔式的分配策略,你可以创建自己的分配器以实现不同的分配策略。
rebalance
broke依赖zk,生产消费者并不依赖
Brokers And Clusters
Messages And Batches(消息与批次)
消息不会重复存储
消息不会重复消费
不会丢失存储
不会丢失消费
Exactly Once
高水位值 (High watermark)。这是控制消费者可读取消息范围的重要字段。一 个普通消费者只能“看到”Leader 副本上介于 Log Start Offset 和 HW(不含)之间的 所有消息。水位以上的消息是对消费者不可见的。
异步刷盘
页缓存到file时系统挂掉
设置 unclean.leader.election.enable = false。这是 Broker 端的参数,它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
设置 replication.factor >= 3。这也是 Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。
设置 min.insync.replicas > 1。这依然是 Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在实际环境中千万不要使用默认值 1。
确保 replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成 replication.factor = min.insync.replicas +1
broker
设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
异步发送消息-丢失消息
Producer
自动提交
确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。就像前面说的,这对于单 Consumer 多线程处理的场景而言是至关重要的。
Consumer Group:一个消费者组可以包含一个或多个消费者。使用多分区 + 多消费者方式可以极大提高数据下游的处理速度,同一消费组中的消费者不会重复消费消息,同样的,不同消费组中的消费者消息消息时互不影响。Kafka 就是通过消费组的方式来实现消息 P2P 模式和广播模式。
Consumer
消息丢失
TransactionalID
Kafka 生产者在同一个事务内提交到多个分区的消息,要么同时成功,要么同时失败。
epoch 机制
找到 Kafka 集群负责管理当前事务的事务协调者( TransactionCoordinator ),向其申请 ProducerID 资源
initTransactions 方法初始化事务上下文
Kafka 事务机制
PID:每个新的 Producer 在初始化的时候会被分配一个唯一的 PID,这个PID 对用户完全是透明的。
Producer ID(即PID)
Sequence Number
enable.idempotence
配置
数据的一致性
AR:Assigned Replicas。AR 是主题被创建后,分区创建时被分配的副本集合,副本个 数由副本因子决定。
在 AR 中的副本可能不在 ISR 中,但 Leader 副本天然就包含在 ISR 中。关于 ISR,还有一个常见的面试题目是如何判断副本是否应该属于 ISR。目前的判断 依据是:Follower 副本的 LEO 落后 Leader LEO 的时间,是否超过了 Broker 端参数 replica.lag.time.max.ms 值。如果超过了,副本就会被从 ISR 中移除。
ISR:In-Sync Replicas。Kafka 中特别重要的概念,指代的是 AR 中那些与 Leader 保 持同步的副本集合。
Kafka 在所有分配的副本 (AR) 中维护一个可用的副本列表 (ISR),Producer 向 Broker 发送消息时会根据ack配置来确定需要等待几个副本已经同步了消息才相应成功,Broker 内部会ReplicaManager服务来管理 flower 与 leader 之间的数据同步。
ISR数据同步
一方面,由于不同 Partition 可位于不同机器,因此可以充分利用集群优势,实现机器间的并行处理。另一方面,由于 Partition 在物理上对应一个文件夹,即使多个 Partition 位于同一个节点,也可通过配置让同一节点上的不同 Partition 置于不同的 disk drive 上,从而实现磁盘间的并行处理,充分发挥多磁盘的优势。
Partition 并发
400M/s
使用 Filesystem Cache PageCache 缓存来减少与磁盘的交互
使用 Zero-copy 和 MMAP 来减少内存交换
使用批量,以流的方式进行交互,直顶网卡上限
使用拉模式进行消息的获取消费,与消费端处理能力相符
顺序读写
性能
kafka
单分区
时间分区
维度分筒
复合分区
FrontEnd DorisDB的前端节点,负责管理元数据,管理客户端连接,进行查询规划,查询调度等工作。
FE
BackEnd DorisDB的后端节点,负责数据存储,计算执行,以及compaction,副本管理等工作。
BE
DorisDB中和外部HDFS/对象存储等外部数据对接的中转服务,辅助提供导入导出功能。
Broker
数据被水平划分为若干个数据分片(Tablet,也称作数据分桶)。每个 Tablet 包含若干数据行。各个 Tablet 之间的数据没有交集,并且在物理上是独立存储的。
Tablet
DorisDB 管理工具,提供DorisDB集群管理、在线查询、故障查询、监控报警的可视化工具。
DorisManager
部署
多个 Tablet 在逻辑上归属于不同的分区(Partition)。一个 Tablet 只属于一个 Partition。而一个 Partition 包含若干个 Tablet。因为 Tablet 在物理上是独立存储的,所以可以视为 Partition 在物理上也是独立。Tablet 是数据移动、复制等操作的最小物理存储单元。
Partition
OLAP_SCAN_NODE
OlapScanner
SegmentIterator
对一个tablet数据读取操作整体的封装
负责了对一个Rowset的读取
RowsetReader
RowwiseIterator
提供了一个Rowset中所有Segment的统一访问的Iterator功能
对应了一个Segment的数据读取,Segment的读取会根据查询条件与索引进行计算找到读取的对应行号信息
SegmentIterator
物化视图
MySQL客户端执行DQL SQL命令。
其他BE调用transimit_data将中间结果发送给BE coordinator节点汇总为最终结果。
FE调用fetch_data获取最终结果。
FE将最终结果发送给MySQL client。
查询
数据流和控制流
稀疏索引
Bloom Filter(布隆过滤器)是用于判断某个元素是否在一个集合中的数据结构,优点是空间效率和时间效率都比较高,缺点是有一定的误判率。
Bloom Filter 索引
Bloom Filter(布隆过滤器)
建立在枚举值列
Bitmap索引
简单理解为就是消除程序循环的优化。
列式存储
向量化执行引擎可以减少节点间的调度,提高CPU的利用率
因为列存数据,同一列的数据放在一起,导致向量化执行引擎在执行的时候拥有了更多的机会能够利用的当前硬件与编译的新优化特征
因为列存数据存储将同类型的类似数据放在一起使得压缩比能够达到更高,这样可以拉近一些磁盘IO能力与计算能力的差距
优势
向量化执行
shuffle join
broadcast join
Doris
以Hive或者Kafka作为数据源,里面保存着真实表,而Kylin做的就是将数据进行抽象,通过引擎实现Cube的构建。将Hbase作为数据的仓库,存放Cube。因为Hbase的直接读取比较复杂,所以Kylin提供了近似SQL和HQL的形式,满足了数据读取的基本需求。对外提供了RestApi和JDBC/ODBC方便操作。
预加载
kylin.cube.algorithm.auto.threshold 默认7作为阈值
充分利用MR
逐层构建算法
一个reduce 写三维度
快速构建算法
核心算法
Cube
REST Server服务层
查询引擎
路由层
cube构建引擎
kylin
可扩展并行度的 ETL、数据分析以及事件驱动
Batch(DataSet API)
Streaming(DataStream API)
Tables API & SQL
在 Flink 中,应用程序由用户自定义算子转换而来的流式 dataflows 所组成。这些流式 dataflows 形成了有向图,以一个或多个源(source)开始,并以一个或多个汇(sink)结束。
从 Streaming 到 Batch 的一个桥梁,将无界数据划分成有界数据。我们通过定义一个窗口,收集一批数据,并对这个窗口内的数据进行聚合类的计算。
如果关心事件实际发生时间,则必须基于事件的事件时间,而不是处理时间
事件时间与处理时间
时间
用来决定某个元素被分配到哪个/哪些窗口中去。
Window Assigner
触发器。决定了一个窗口何时能够被计算或清除,每个窗口都会拥有一个自己的Trigger。
Trigger
“驱逐者”。在Trigger触发之后,在窗口被处理之前,Evictor(如果有Evictor的话)会用来剔除窗口中不需要的元素,相当于一个filter,如countTime 中evictor(size) ,其中size 为 保留的元素个数
Evictor
窗口组件
处理数据从source、transform、sink中的背压和消息乱序问题---延迟触发window计算
引入watermark机制则会等待晚到的数据一段时间,等待时间到则触发计算,如果数据延迟很大,通常也会被丢弃或者另外处理。
watermark
窗口
Flink 基于 Chandy-Lamport 算法实现了自己的分布式快照算法,利用 state 和 checkpoint 机制实现了 streaming system 的 exactly-once 语义
一个Checkpoint记录着数据流中某个时刻所有operators对应的状态
Flink Checkpoint的核心元素就是数据流Barrier,Barrier会被注入到数据流中,作为数据流的一部分向前流动。Barrier将数据流中的数据切分为进入当前Checkpoint的部分和进入下一次Checkpoint的部分,每个Barrier都携带对应Checkpoint的ID。Barrier是非常轻量级的,不会中断数据流的处理。
Checkpoint Barrier
Job Manager中的Checkpoint Coordinator向所有source端发送触发Checkpoint的通知,并在source端注入barrier事件。
Source端向下游传递barrier,并将自己的状态异步地写入到持久化存储中。
Operator接收到source端传递的barrier之后,会对operator的输入流进行对齐barrier,然后向输出流传递barrier,并将自己的状态异步的写入到持久化存储中。
当sink端接收到所有输入流传递过来的barrier之后,就会向Checkpoint Coordinator通知,此次Checkpoint执行完成。
执行过程
checkpoint 机制
在缺乏类似全局时钟或者全局时钟不可靠的分布式系统中来确定一种全局状态
分布式快照算法应用到流式系统中就是确定一个 Global 的 Snapshot
将分布式系统简化成有限个进程和进程之间的 channel 组成,也就是一个有向图:节点是进程,边是 channel。因为是分布式系统,也就是说,这些进程是运行在不同的物理机器上的。那么一个分布式系统的全局状态就是有进程的状态和 channel 中的 message 组成
Flink 在 2015 发布了一篇论文 Lightweight asynchronous snapshots for distributed dataflows
Chandy-Lamport 算法
checkpoint 与 state
Event Processing(CEP)
Graphs:Gelly
Machine Learning
扩展库
处理 Job 提交、 Job 监控以及资源管理
JobManager
运行 worker 进程, 负责实际任务 Tasks 的执行,而这些任务共同组成了一个 Flink Job
Flink TaskManager
Back Pressure背压
BroadcastPartitioner:广播分区器,将数据发往下游的所有节点CustomPartitionerWrapper:自定义分区器,可以自定义分区的规则ForwardPartitioner:转发分区器,将数据转发给在本地运行下游的operaterShufflePartitioner: 洗牌分区器,将数据在所有output chancel随机选择一个输出GlobalPartitioner:全局分区器:默认会选择索引为0的channel进行输出KeyGroupStreamPartitioner:键组分区器,通过记录数据的值获取到分区key:keyGroupId * parallelism / maxParallelism;RebalancePartitioner:轮询分区器,适用于数据倾斜RescalePartitioner:可扩展的分区器,通过轮询的方式将数据向下游输出
分区
OperatorChain
operators.setup()
task specific init()
initialize-operator-states()
open-operators()
run()
finish-operators()
close-operators()
common cleanup
task specific cleanup()
invoke
StreamTask
tasks
优化的逻辑执行计划(Web UI中看到的就是这个)
JobGraph
jobgraph
物理执行计划
ExecutionGraph
executiongraph
runtime
原始逻辑执行计划
StreamGraph
graph
api
streaming
org.apache.flink
flink
spark基于local
spark基于standalone
spark基于yarn
spark基于metsos
DataSet
DataFrame
spark sql
Spark Streaming 流处理框架
MLlib机器学习库
Graphx图形处理库
spark core
master
运行在worker节点上的一个进程,负责运行某些task,并将数据存在内存或者磁盘上。
在executor进程中执行任务的工作单元
每个job被划分为多个stage,一个stage中包含一个taskset
stage
task
Executer
Cache
Work Node
Cluster manager
main()函数,创建SparkContext,由SparkContext进行资源申请,任务的分配和监控等。程序执行完毕后关闭SparkContext。
Driver
groupByKey
算子
一个 RDD 由一个或者多个分区(Partitions)组成。对于 RDD 来说,每个分区会被一个计算任务所处理,用户可以在创建 RDD 时指定其分区个数,如果没有指定,则默认采用程序所分配到的 CPU 的核心数;
RDD 拥有一个用于计算分区的函数 compute
RDD 会保存彼此间的依赖关系,RDD 的每次转换都会生成一个新的依赖关系,这种 RDD 之间的依赖关系就像流水线一样。在部分分区数据丢失后,可以通过这种依赖关系重新计算丢失的分区数据,而不是对 RDD 的所有分区进行重新计算;
Key-Value 型的 RDD 还拥有 Partitioner(分区器),用于决定数据被存储在哪个分区中,目前 Spark 中支持 HashPartitioner(按照哈希分区) 和 RangeParationer(按照范围进行分区);
transformations(转换,从现有数据集创建新数据集)
actions(在数据集上运行计算后将值返回到驱动程序)
操作RDD
窄依赖 (narrow dependency):父 RDDs 的一个分区最多被子 RDDs 一个分区所依赖;
宽依赖 (wide dependency):父 RDDs 的一个分区可以被子 RDDs 的多个子分区所依赖。
首先,窄依赖允许在一个集群节点上以流水线的方式(pipeline)对父分区数据进行计算,例如先执行 map 操作,然后执行 filter 操作。而宽依赖则需要计算好所有父分区的数据,然后再在节点之间进行 Shuffle,这与 MapReduce 类似。窄依赖能够更有效地进行数据恢复,因为只需重新对丢失分区的父分区进行计算,且不同节点之间可以并行计算;而对于宽依赖而言,如果数据丢失,则需要对所有父分区数据进行计算并再次 Shuffle。
宽依赖和窄依赖
弹性式数据集RDDs
基于内存计算,减少低效的磁盘交互
高效的调度算法,基于DAG
容错机制Linage,精华部分就是DAG和Lingae
防止不必要的jar包分发,提高数据的本地性,选择高效的存储格式如parquet
过滤操作符的优化降低过多小任务
降低单条记录的资源开销
处理数据倾斜
复用RDD进行缓存
作业并行化执行
启用高效的序列化方法如kyro
增大off head
将 mapper(Spark 里是 ShuffleMapTask)的输出进行 partition
不同的 partition 送到不同的 reducer(Spark 里 reducer 可能是下一个 stage 里的 ShuffleMapTask,也可能是 ResultTask)
Reducer 以内存作缓冲区,边 shuffle 边 aggregate 数据,等到数据 aggregate 好以后进行 reduce() (Spark 里可能是后续的一系列操作)
Hadoop MapReduce 是 sort-based,进入 combine() 和 reduce() 的 records 必须先 sort
Spark
Topology
spout
bolt
Nimbus
Supervisors
Storm
Hadoop Upserts anD Incrementals
在hadoop兼容的存储之上存储大量数据
Update/Delete记录:Hudi使用细粒度的文件/记录级别索引来支持Update/Delete记录,同时还提供写操作的事务保证。查询会处理最后一个提交的快照,并基于此输出结果。
变更流:Hudi对获取数据变更提供了一流的支持:可以从给定的时间点获取给定表中已updated/inserted/deleted的所有记录的增量流,并解锁新的查询姿势(类别)
两种原语
Hudi
提供统一批处理和流处理的编程范式
Beam
Webserver
ExecutorServer
job
Flow1.0
Flow2.0
Flow
Azkaban
dolphinscheduler
分布式任务调度框架
sqoop数据迁移工具
Event 数据传输的基本单元
Source 数据源
Channel 临时存储数据的管道
Sink 数据处理单元
Agent
flume数据收集工具
事实表
维度表
数据仓库
cboard
davinci
DVAAS(Data Visualization as a Service)平台解决方案
数据可视化
MOLAP以Cube为表现形式,但计算与管理成本较高
MOLAP(Multidimensional OLAP)
随着分布式、并行化技术成熟应用,MPP引擎逐渐表现出强大的高吞吐、低时延计算能力,号称“亿级秒开”的引擎不在少数,ROLAP模式可以得到更好的延伸。
ROLAP需要强大的关系型DB引擎支撑
ROLAP(Relational OLAP)
\t关系型数据库
HOLAP(Hybrid OLAP)
ROLLUP(上卷)
olap
依赖 kafka connect
消息体内容太多,对消息队列压力较大
每张表对应一个topic,管理起来不够方便(canal,maxwell则可以使用正则来处理)
支持快照模式(snapshot.mode)全量同步
debezium
Canal
不支持truncate命令同步
Maxwell
CDC
大数据
选
有挑战的工作
团队知识分享
育
okr、kpi
用
市场薪资调研
分支主题
留
和平的分手
离
二八原则
点评日报予以肯定
软实力
人员
任务拆分
燃尽图
deadline
项目
不断的重新定义岗位角色
领导力转型
领导梯队
金字塔原理
思维导图
SMART原则
定目标
STAR原则
汇报
研发性岗位
OKR适用于实现目标方法不是特别清晰不太成熟的岗位
(1)全员设定。公司、部门、管理者和员工均从战略开始设定自己的年度目标和季度目标。
(2)从上至下。目标的设立顺序应该是公司到部门到组到个人。
(3)目标共识。目标必须是在管理者与员工直接充分沟通后的共识
(4)目标具体可衡量。
(5)有野心的目标。
(6)目标不能过多,员工通常每季度制定4到6个
1.设定目标
(1)目标要有年度KRs,也有季度KRs。年度KRs统领全年,但并非固定不变,而是可以及时调整,调整要经过批准;季度KRs则是一旦确定就不能轻易改变的。
(2)可以调整的是KRs,目标不能调整。措施和方法(KRs)可以不断完善,且KRs的设定也应是管理者与员工直接充分沟通后的共识,以员工确认为主。
(3)KR是须具备的特点:必须是能直接实现目标的;必须具有进取心、敢创新的,可以不是常规的;必须是以产出或者结果为基础的、可衡量的,并设定评分标准;不能太多,一般每个目标的KR不超过4个;必须是和时间相联系的。
2、针对每个目标设定其KR(关键结果)
(1)每项关键结果就会派生出一系列的任务,交给不同的同事负责。
(2)关键结果负责人就成了名符其实的项目经理,来组织协调大伙。
(3)关键结果的项目经理是团队非常重要的成员,他们应能够调度和影响企业资源。如果他还不具备这个权力,就把这个权力给他;
3、推进执行(从关键结果到“行动计划“)
4、OKR回顾,与绩效评估
OKR
制造业的一线操作岗位
KPI适用于一些工作目标和措施都比较明确和成熟的岗位
选了错误的KPI
无法制订KPI
没有人对最终结果负责
KPI
人员管理
戴明环
团队管理
管理
kettle
ETL
idea
eclipse
IDE
Jenkins
ansible
package
更新本地仓库
install
更新远程和本地仓库
deploy
生命周期
dependency
dependencies
dependencyManagement
如果在同一pom.xml文件中有2个相同的依赖;后面声明的会覆盖前面的依赖
同一个pom
在不同pom.xml中有2个相同的依赖;则先声明的依赖,会覆盖后面生命的依赖
不同pom
路径长度相同
存在两个项目依赖同个类型但不同版本的jar包,这个时候会优先选择路径短的
路径最短优先原则
当依赖的scope为compile的时候,那么当前这个依赖的包,会在编译的时候被加入进来,并且在打包(mvn package)的时候也会被加入进来。编译范围有效,在编译与打包时都会加入进去。
compile 默认
在编译时我们需要依赖servlet-api.jar,但是在运行时我们不需要该 jar包,因为这个jar 包已由web服务器提供,如果在打包时又被加入进去,那么就可能产生冲突。此时我们就可以使用 provided 进行范围修饰。
当依赖的scope为provided的时候,在编译和测试的时候有效,在执行(mvn package)进行打包时不会加入。
provided
<systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath>
与provided相同,不过被依赖项不会从maven仓库获取,而是从本地文件系统拿,需要配合systemPath属性使用。
system
当依赖的scope为runtime的时候,在运行的时候才会依赖,在编译的时候不会依赖。比如,在编译的时候我们不需要JDBC API的jar包,而在运行的时候我们才需要JDBC驱动包。就可以使用runtime修饰。
当依赖的scope为test的时候,指的的是在测试范围有效,在编译与打包的时候都不会使用这个依赖。
test
依赖上的多重继承
import
scope 域
依赖
plugins 下的 plugin 是真实使用的,而 pluginManagement 下的 plugins 下的 plugin 则仅仅是一种声明
pluginManagement
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin>
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> </plugin>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.glodon.scm.integrate.platform.service.IntergrateApplication</mainClass> </manifest> </archive> </configuration> </plugin>
plugins
build
maven
运维&CI
jmeter
压测
postman
测试
tapd
禅道
jira
项目管理
yapi
swagger
接口管理
rebase
merge
git
svn
代码版本控制
监控指标
SkyWalking
链路追踪
听云
zabbix
探针
APM
监控
phantomjs
页面截图
批处理层(Batch Layer)
速度处理层(Speed Layer)
服务层(Serving Layer)
Lambda
不适用于批处理和流处理代码逻辑不一致的场景
Kappa
Spark Streaming内置的Kafka Direct API (KafkaUtils.createDirectStream),实现精确Exactly-Once一致性
可靠的数据源端(数据可重复读取、不丢失)
基于Spark Streaming的Receiver模式,在Executor持续拉取kafka数据流kafka数据存储到Executor内存和WAL(预写日志)中WAL(预先日志)写入完成后,自动更新offset至zookeeper上
Spark Streaming 基于Receiver的Kafka高级API,实现At least Once语义
(1) 高级消费者API需要启用Receiver线程消费Kafka数据,相较于第一种增加了开销,且无法直接实现并行读取,需要使用多个Kafka Dtstream 消费同一组然后union。(2) 高级消费API在Executor本地和WAL存储两份数据<开启WAL不丢失机制>,而第一种Direct API仅在Executor中存储数据<offset存储到checkpoint中>(3) 基于Kafka Direct API的方式,因Spark集成Kafka API直接管理offset,同时依托于Kafka自身特性,实现了Exactly-Once一致性语义。因此在生产中建议使用此种方式!!
可靠的消费端(Spark内部精确一次消费)
基于RDD内存模型,启用多种一致性策略,实现Exactly-Once一致性。
序代码去重如果实时流进入到Spark消费端已经存在重复数据,可以编写Spark程序代码进行去重操作,实现Exactly-Once一致性。(1) 内存去重。采用Hashset等数据结构,读取数据中类似主键等唯一性标识字段,在内存中存储并进行去重(2) 使用Redis Key去重。借助Redis的Hset等特殊数据类型,自动完成Key去重。(3) DataFrame/SQL场景,使用group by/ over() window开窗等SQL函数去重(4) 利用groupByKey等聚合算子去重(5) 其他方法。。
幂等写入
事务写入
输出端
可靠的输出端(幂等性、事务)
Apache Spark的Exactly-Once机制
Apache Flink的Exactly-Once机制
分布式系统天生具有跨网络、多节点、高并发、高可用等特性,难免会出现节点异常、线程死亡、网络传输失败、并发阻塞等非可控情况,从而导致数据丢失、重复发送、多次处理等异常接踵而至。如何保持系统高效运行且数据仅被精确处理一次是很大的挑战。
当任意条数据流转到某分布式系统中,如果系统在整个处理过程中对该任意条数据都仅精确处理一次,且处理结果正确,则被认为该系统满足Exactly-Once一致性
正好一次exactly-once
至少一次at-least-once
最多一次at-most-once
Exactly-Once一致性语义
在自然环境中,数据的产生原本就是流式的。
低延迟、低容错
流计算系统在计算过程中,或是出现故障恢复计算后,流系统的内部状态和外部输出的数据应该处在一致的状态
定义
常见的非确定性计算包括使用了随机数、使用系统时间、字符串拼接等
流计算中的确定性指的是,给定相同的一组数据,重复运行多次或者打乱数据进入引擎的顺序,计算完成后将会输出相同的结果,否则就是非确定性计算。
确定性/非确定性计算
一致性
无重叠
滚动窗口
设置滑动步长、有重叠
滑动窗口
会话窗口
流处理
高延迟、高容错
批处理
流与批处理
Kimball维度建模
IBM-FSDM主题域模型划分
NCR FS-LDM主题域模型划分
雪花模型
星型模型
数仓建模
大数据架构
单体架构
垂直架构
SOA代表面向服务的架构,将应用程序按照不同的职责划分为不同的模块,不同的模块直接通过特定的协议和接口进行交互。这样将整个系统切分成很多单个组件服务来完成请求,当流量过大时通过水平扩展相应的组件来支撑,所有组件通过交互来满足整体的业务需求。服务化架构是一套松耦合的架构,服务的拆分原则是服务内部高内聚,服务之间低耦合。这个阶段一般使用Web Service或者Dubbo来服务治理。
SOA服务化架构
1.微服务架构强调业务系统需要彻底的组件化和服务化,一个组件就是一个产品,可以单独对外提供服务
2.微服务不再强调传统SOA架构里面比较重要的ESB企业服务总线
3.微服务强调每个微服务都有自己独立的运行空间,包括数据库资源。
4.微服务架构本身来源于互联网的思路,因此组件对外发布的服务强调HTTP Rest API的方法进行
5.微服务的切分粒度会更小。
SOA和微服务的区别
应该围绕业务功能而不是数据访问或消息传递等水平层来设计微服务
定义系统的大规模结构
战略
提供一组可用于创建域模型的设计模式
包括实体、聚合和领域服务。 借助这些战术模式,可以设计低耦合高内聚的微服务。
战术(策略)
两个阶段
有助于避免组织边界或技术选择左右你的设计
哪些功能密切相关
哪些功能是业务的核心
哪些功能提供辅助服务
什么是依赖项关系图?
在填充关系图时,可以开始标识离散的子域
构建业务域并创建 域模型
创建的系统的鸟瞰图
1.分析业务领域
2.定义领域的边界上下文
3.在边界上下文中,应用战术 DDD 模式以定义实体、聚合和域服务。
从域模型派生微服务
4.使用前一步骤的结果可以标识应用程序中的微服务
每个服务承担单一责任
服务之间不存在琐碎的调用。
每个服务足够小,独立工作的小团队即可构建它
两个或更多个服务的部署不应该存在相互依赖的关系
服务未紧密耦合,可独立演变
服务边界不会造成数据一致性或完整性方面的问题
验证设计
使用域分析对微服务建模
域建模
充血
贫血
几种类型
外观模式 feignClient接口 包含DTO对象 独立模块
Facade
web层
controller
interface接口层
负责接收web层指令,对领域层的逻辑调用。应用层负责将逻辑流程进行组装。而不负责具体的逻辑处理 可包含日志,消息,邮件等 负责对外转换
可根据CQRS设计拆分cammand与query
ApplicationService
ApplicationServiceImpl
围绕model到外部DTO的转换类
assembler
application应用层
负责逻辑处理
实体逻辑较多的时候,向下拆分值对象
实例的构造逻辑拆分
Factory
实体
构造方法中进行参数验证
值对象
聚合根aggregate
同aggregate对应
规约模式提倡将验证和查询复用同一个逻辑单元
可规约模式(Specification)与CQRS冲突
仓库接口
简化可以没有
domain领域层
围绕model到外部DO的转换类
converter
仓库接口的实现
Repository
DO(data Object)
entity
xml
持久化层
mapper
Infrastructure基础设施
DDD策略
domain driven design DDD
干净架构 - Clean Architecture
洋葱架构
导致制造大量的于数据表对应的类,这些类一般不会有行为,而描述业务逻辑的职责就会到很多service类上
Active Record 设计模式
Saga
Command指的是增加/更新数据的处理。
Query指的是查询数据的处理。它不会造成数据的变更。
CQRS,全称Command Query Responsibility Segregation。直译过来就是命令查询的职责分离。
CQRS
可以用与语言无关的方式处理常见的客户端连接任务,如监视,日志记录,路由和安全性(如 TLS)。
外交官模式(Ambassador)
介于新应用和遗留应用之间,用于确保新应用的设计不受遗留应用的限制。
不共享相同语义的不同子系统之间实施外观或适配器层。 此层转换一个子系统向另一个子系统发出的请求。 使用反腐层(Anti-corruption layer)模式可确保应用程序的设计不受限于对外部子系统的依赖
反腐层(Anti-corruption layer)模式最先由 Eric Evans 在 Domain-Driven Design(域驱动的设计)中描述。
反腐层(Anti-corruption layer)
为不同类型的客户端(如桌面和移动设备)创建单独的后端服务。
BFF 后端服务前端(Backends for Frontends)
隔离了每个工作负载或服务的关键资源,如连接池、内存和 CPU。
轮船底下船舱一个个都是互相隔离的,这样一旦哪个舱壁出现漏水也影响不到其他的船舱
Hystrix线程池舱壁模式
舱壁模式(Bulkhead)
聚合服务 与 BFF有些类似
将对多个单独微服务的请求聚合成单个请求
网关聚合(Gateway Aggregation)
网关卸载(Gateway Offloading)
网关路由(Gateway Routing)
例:给原有php服务 封装一个边车 注册在java注册中心
将应用程序的辅助组件部署为单独的容器或进程以提供隔离和封装。
边车模式(Sidecar)
通过逐步重构单体应用(而不是推到重来的方式),逐渐构建出一个新的应用程序。
绞杀者模式(Strangler)
微服务
多语言技术栈不统一:C++、Java、PHP、Go。Spring Cloud无法提出非Java语言的微服务治理。
Spring Cloud或者Dubbo的成熟框架,直接搞定服务注册,服务发现,负载均衡,熔断等基础功能。然后自己开发服务路由等高级功能, 接入Zipkin等Apm做全链路监控,自己做加密、认证、授权。 想办法搞定灰度方案,用Redis等实现限速、配额。 诸如此类,一大堆的事情, 都需要自己做
服务治理周期长:微服务治理框架与业务耦合,上线周期长,策略调整周期长。
产品能力弱:Spring Cloud缺乏平台化和产品化的能力,可视化能力弱。
进化
Istio 的出现为负责的微服务架构减轻了很多的负担,开发者不用关心服务调用的超时、重试、Rate Limit 的实现,服务之间的安全、授权也自动得到了保证。
为 Envoy 提供了服务发现,流量管理和智能路由(AB 测试、金丝雀发布等),以及错误处理(超时、重试、熔断)功能。 用户通过 Pilot 的 API 管理网络相关的资源对象,Pilot 会根据用户的配置和服务的信息把网络流量管理变成 Envoy 能识别的格式分发到各个 Sidecar 代理中。
Pilot
为整个集群执行访问控制(哪些用户可以访问哪些服务)和 Policy 管理(Rate Limit,Quota 等),并且收集代理观察到的服务之间的流量统计数据。
Mixer
为服务之间提供认证和证书管理,可以让服务自动升级成 TLS 协议。
citadel
Envoy
Istio(服务网格)
应用架构发展
C4
银弹
瀑布
原型
软件工程
服务降级
熔断
防止雪崩
缓存预热是指系统启动后,直接查询热点数据并缓存
缓存预热
先删除缓存,再更新数据库
推荐
先更新数据库,再删除缓存
有多个并发的请求更新数据,你并不能保证更新数据库的顺序和更新缓存的顺序一致,那就会出现数据库中和缓存中数据不一致的情况。所以一般来说考虑删除缓存。
为什么是删除缓存,而不是更新缓存
缓存更新
网络抖动可能导致写缓存响应时间很慢,引起数据库事务阻塞
不要把写缓存操作放在事务中
缓存技巧
缓存策略
存在性检测
BloomFilter
对不存在的用户,在缓存中保存一个空对象进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据。
缓存空值
缓存穿透是指:查询的数据在数据库中不存在,那么缓存中自然也不存在。所以,应用在缓存中查不到,则会去查询数据库。当这样的请求多了后,数据库的压力就会增大。
缓存穿透
使用快速失败的熔断策略,减少 DB 瞬间压力;
使用主从模式和集群模式来尽量保证缓存服务的高可用。
部署 Redis Cluster(主从+哨兵),以实现 Redis 的高可用,避免全盘崩溃
增加缓存系统可用性(事前)
采用多级缓存方案(事中)
避免被流量打死
限流、降级、熔断方案 (事中)
缓存如果支持持久化 ,可以在恢复工作后恢复数据(事后)
针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。
缓存雪崩是指缓存不可用或者大量缓存由于超时时间相同在同一时间段失效,大量请求直接访问数据库,数据库压力过大导致系统雪崩。
缓存雪崩
锁住热点数据的 key,避免大量线程同时访问同一个 key
可以对部分数据采取失效前自动刷新的策略,而不是到期自动淘汰。淘汰其实也是为了数据的时效性,所以采用自动刷新也可以。
定时异步刷新
使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。
缓存击穿是指,热点数据失效瞬间,大量请求直接访问数据库
缓存击穿
L1 进程缓存
L2 分布式缓存
L1 缓存
通过消息队列的发布、订阅机制,可以通知其他应用节点对进程内缓存进行更新。使用这种方案,即使消息队列服务挂了或不可靠,由于先执行了数据库更新,但进程内缓存过期,刷新缓存时,也能保证数据的最终一致性。
多级缓存更新
缓存
横切 取模 提高并发
分库分表
netty
单 Reactor 单线程模型
单 Reactor 多线程模型
mainReactor
subReactor
因为subReactor也会执行一些比较耗时的IO操作,例如消息的读写,使用多个线程去执行,则更加有利于发挥CPU的运算能力,减少IO等待时间。
多线程下的Reactor模式
主从 Reactor 多线程模型
事件驱动
负责响应IO
我们如何知道IO就绪这个事件,谁来充当这个中间人?Reactor模式的答案是:由一个不断等待和循环的单独进程(线程)来做这件事,它接受所有handler的注册,并负责先操作系统查询IO是否就绪,在就绪后就调用指定handler进行处理,这个角色的名字就叫做Reactor。
Reactor反应器线程
Handlers处理器
生产消费模式没有队列缓存IO事件
和生产消费之模式对比
发布订阅模式有多个观察者/订阅者
与观察者模式(发布订阅)对比
模式对比
Reactor
Reactor模式里,操作系统只负责通知IO就绪,具体的IO操作(例如读写)仍然是要在业务进程里阻塞的去做的,而Proactor模式则更进一步,由操作系统将IO操作执行好(例如读取,会将数据直接读到内存buffer中),而handler只负责处理自己的逻辑,真正做到了IO与程序处理异步执行。所以我们一般又说Reactor是同步IO,Proactor是异步IO。
Proactor模式
多路复用
高并发
多进程或者线程去进行处理,但是这样会带来一些进程切换的开销,试想一个进程一个数据读了500ms,期间进程切换到它3次,但是CPU却什么都不能干,就这么切换走了
CPU的处理速度是要远远快于IO速度的,如果CPU为了IO操作(例如从Socket读取一段数据)而阻塞显然是不划算的
回调的方式
构造函数设为私有的
类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。
懒汉
线程安全
类加载时就初始化,浪费内存。
饿汉
synchronized
懒汉线程安全
安全且在多线程情况下能保持高性能。
B b = new B();
双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,
双检锁/双重校验锁(DCL,即 double-checked locking)
登记式/静态内部类
枚举
单例
工厂方法模式
抽象工厂模式
建造者模式
原型模式
创建型模式
实现被适配接口,拥有适配的引用
双向适配
适配器Adapter
装饰器模式
代理模式
外观模式
组合模式
享元模式
桥接模式
结构型模式
策略模式
模板方法模式
观察者模式
迭代子模式
责任链模式
命令模式
备忘录模式
状态模式
访问者模式
中介者模式
解释器模式
行为型模式
设计模式
整个架构具有MQ带来的所有优点:异步解耦、削峰、降低业务复杂度
降低耦合:降低事件生产者和订阅者的耦合性。事件生产者只需关注事件的发生,无需关注事件如何处理以及被分发给哪些订阅者;任何一个环节出现故障,都不会影响其他业务正常运行;异步执行:事件驱动架构适用于异步场景,即便是需求高峰期,收集各种来源的事件后保留在事件总线中,然后逐步分发传递事件,不会造成系统拥塞或资源过剩的情况;可扩展性:事件驱动架构中路由和过滤能力支持划分服务,便于扩展和路由分发;敏捷性:事件驱动架构支持与各种阿里云产品和应用集成,支持事件路由至任何系统服务,提供各种敏捷高效的部署方案。
事件驱动架构(EDA)
TCP 层之上的微服务层
代理变成了分布式的,它常驻在了应用的身边(最常见的就是 Kubernetes Sidecar 模式,每一个应用的 Pod 中都运行着一个代理,负责流量相关的事情)。
Service Mesh
虽然有主从节点之分,但是在恢复模式选举过程中仍可对外提供服务
AP
CP
CAP
RPC
一阶段记录undo-log并提交分支事务。
利用快照回滚
核心
AT 则是应用/驱动层实现的二阶段提交
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
两阶段提交协议的演变
支持本地 ACID 事务 的 关系型数据库
seata 使用全局锁避免脏写
AT
XA一阶段不提交分支事务,锁资源
利用数据库回滚
XA 是数据库层面实现的二阶段提交
分布式强一致性的解决方案,但性能低. 以为锁时间长
XA模式
业务无侵入
Try 预留资源 (如:库存服务的预占库存,支付服务的冻结部分账户余额)
Confirm 如果所有的事务参与者try 操作都执行成功了,就会调用所有事务参与者的confirm操作,确认资源。(如:库存服务的减库存,支付服务的扣减账户余额)
Cancel 如果有事务参与者在try阶段执行失败,就调用所有已执行try阶段成功的参与方的cancel方法,释放try阶段占用的资源(如:库存服务的释放预占库存,支付服务的释放冻结的账户余额)
TCC(Try Confirm Cancel)是应用层的两阶段提交,所以对代码的侵入性强,其核心思想是:针对每个操作,都要实现对应的确认和补偿操作,也就是业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作
如账户操作 : 下账 确认 取消下账操作
高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景
TCC
长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统(第一阶段就操作DB,会存在脏读问题)
confirm 直接执行资源操作,(如:库存服务的减库存,支付服务的扣减账户余额)
cancel 回滚资源操作,这个地方的cancel与TCC中的cancel不一样,准确点说应该是回滚,(如:回滚库存服务的减库存操作,回滚支付服务的扣减账户余额的操作)。当然是业务层面实现的回滚,而非数据库事务层面的回滚
过程
异步更新的场景,并且对数据实时性要求不高的地方
消息发送是一个网络通信的过程,发送消息的过程就有可能出现发送失败、或者超时的情况。超时有可能发送成功了,有可能发送失败了,消息的发送方是无法确定的,所以此时消息发送方无论是提交事务还是回滚事务,都有可能不一致性出现。
事务消息和普通消息的区别在于事务消息发送成功后,处于 prepared 状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游订阅者才可以监听到次消息。
解决问题
1.事务发起者预先发送一个事务消息
2.MQ 系统收到事务消息后,将消息持久化,消息的状态是“待发送”,并给发送者一个 ACK 消息
3.事务发起者如果没有收到 ACK 消息,则取消本地事务的执行;如果收到了 ACK 消息,则执行本地事务,并给 MQ 系统再发送一个消息,通知本地事务的执行情况
4.MQ 系统收到消息通知后,根据本地事务的执行情况更改事务消息的状态,如果成功执行,则将消息更改为“可消费”并择机下发给订阅者;如果事务执行失败,则删除该事务消息
5.本地事务执行完毕后,发给 MQ 的通知消息有可能丢失了。所以支持事务消息的 MQ 系统有一个定时扫描逻辑,扫描出状态仍然是“待发送”状态的消息,并向消息的发送方发起询问,询问这条事务消息的最终状态如何并根据结果更新事务消息的状态。因此事务的发起方需要给 MQ 系统提供一个事务消息状态查询接口
6.如果事务消息的状态是“可发送”,则 MQ 系统向下游参与者推送消息,推送失败会不停重试
7.下游参与者收到消息后,执行本地事务,本地事务如果执行成功,则给 MQ 系统发送 ACK 消息;如果执行失败,则不发送 ACK 消息,MQ 系统会持续推送给消息。
处理流程
解决的是本地事务的执行和发消息这两个动作满足事务的约束
在一次事务中需要发送多个消息的情况,保证多个消息之间的事务约束,即多条消息要么都发送成功,要么都发送失败
Kafka
Kafka 的事务基本上是配合其幂等机制来实现 Exactly Once 语义的,所以说 Kafka 的事务消息不是我们想的那种事务消息 RocketMQ 的才是。
中间件
事务消息
业务侵入
本地消息表
分布式事务
分布式
一个类应该只有一个发生变化的原因
此原则的核心就是解耦和增强内聚性。
单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
产生原因:(职责扩散)因为某种原因,某一职责被分化为颗粒度更细的多个职责了。
软件实体的行为是可扩展的,当需求变更的时候,可以对模块进行扩展,使其满足需求变更的要求
对扩展是开放的
软件实体进行扩展的时候,不需要改动当前的软件实体
对修改是关闭的
开闭原则(Open Closed Principle,OCP)
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
子类中可以增加自己特有的方法
当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
任何基类可以出现的地方,子类一定可以出现。能保证子类完美替换基类
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
里氏代换原则(Liskov Substitution Principle,LSP)
客户端不应该强迫依赖它不需要的接口
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少
依赖几个专用的接口要比依赖一个综合的接口更灵活
接口隔离原则(Interface Segregation Principle,ISP)
从客户端代码调用框架代码,变成框架调用客户端代码。框架来定义接口,客户端来实现。
高层模块不依赖低层模块
抽象不应该依赖细节;细节应该依赖抽象。
TDD开发模式
依赖倒置原则的核心思想是面向接口编程。
依赖倒置原则更多是说,我们应该面向接口编程;好莱坞原则是说,低层组件将自己挂钩到系统上,由系统来主动调用。
“好莱坞原则”(Hollywood principle)
tomcat处理web请求的流程图,请求会经过 connector,coyote,engine,host,context,Servlet,层层传递最终传递到我们的应用程序里面来
实践
依赖倒转原则(Dependency Inversion Principle,DIP)
面向对象五个基本原则(SOLID)
一个软件实体应当尽可能少的与其他实体发生相互作用
门面模式和调停者模式(中介者模式)实际上就是迪米特法则的应用。
第六原则-迪米特原则
尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
第七原则 - 合成 / 聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
我要做什么
声明式
怎么做
命令式
与命令式编程区别
声明式编程
惰性执行
它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象
数据不可变
合理性
可测试性
可移植性
并行代码
纯函数
无状态 、 数据不可变更
将电脑运算视为函数,强调函数计算比指令执行更重要
代码优雅 适合并发
这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
把生产者消费者模式,使用简单的API表示出来,并自动处理背压(backpressure)问题。
变化传递、基于数据流、变化传播
响应式编程是一种面向数据流和变化传播的编程范式
基于发布/订阅模式的数据处理规范 提供非阻塞背压的异步流处理标准的倡议
接口规范
如果生产者发出的信息比消费者能够处理消息最大量还要多,消费者可能会被迫一直在抓消息,耗费越来越多的资源,埋下潜在的崩溃风险。为了防止这一点,需要有一种机制使消费者可以通知生产者,降低消息的生成速度。生产者可以采用多种策略来实现这一要求,这种机制称为背压。
背压(back pressure)
Reactive Stream(响应式流/反应流)
链式代码
使用Netty,能够构建响应式编程的基础,加上类似lambda表达式这样的书写风格,能够完成类似WebFlux这样的响应式框架
响应式编程(Reactive Programming,RP)
编程范式
授权框架
OAuth2
认证协议
JWT
安全设计
秒杀
单点
画布
常用案列
应用架构
由高效的召回规则、算法或简单的模型组成,这让推荐系统能快速从海量的候选集中召回用户可能感兴趣的物品
单策略召回
多路召回
基于Embedding召回
召回层
精排层,是利用排序模型对初筛的候选集进行精排序
排序层
人工智能架构
架构设计
架构与设计模式
Refresh
@SpringBootConfiguration
这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通过将Registrar类导入到容器中,而Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中
@AutoConfigurationPackage
AutoConfigurationImportSelector的作用是导入哪些组件的选择器
2.0.x默认CGlib动态代理
AopAutoConfiguration
getImportGroup
DeferredImportSelector
BeanClassLoaderAware
ResourceLoaderAware
BeanFactoryAware
EnvironmentAware
Ordered
它通过将AutoConfigurationImportSelector类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法实现将配置类信息交给SpringFactory加载器进行一系列的容器创建过程
@Import(AutoConfigurationImportSelector.class)
判断是否开启自动配置
从META-INF/spring-autoconfigure-metadata.properties文件中载入属性配置
获取所有的配置列表
@EnableAutoConfiguration
@ComponentScan
registerBeanDefinition
registerFeignClient
FeignClientsRegistrar.class
@EnableFeignClients
AnnotationConfigServletWebServerApplicationContext
SERVLET
AnnotationConfigReactiveWebServerApplicationContext
REACTIVE
AnnotationConfigApplicationContext
NONE
createApplicationContext
设置java.awt.headless
configureHeadlessProperty()
初始化META-INF\\spring.factories下SpringApplicationRunListener对应的实现类
SimpleApplicationEventMulticaster
EventPublishingRunListener
getSpringFactoriesInstances
getRunListeners(args)
listeners.starting()
multicastEvent
listeners.environmentPrepared
prepareEnvironment
configureIgnoreBeanInfo
printBanner
prepareContext
见 spring bean加载过程
ConfigurableApplicationContext.refresh()
refreshContext
afterRefresh
listeners.started(context)
ApplicationRunner.class
CommandLineRunner.class
执行
callRunners
listeners.running(context)
run
SpringApplication
ApplicationEvent
SpringApplicationRunListener
Set<ApplicationListener<?>> applicationListeners
DefaultListenerRetriever
AbstractApplicationEventMulticaster
event
context
springboot
fast
CGLIB
JDK
动态代理
AOP
Bean容器找到配置文件中Spring Bean的定义
Bean容器利用Java Reflection API创建一个Bean的实例
如果涉及到一些属性值,利用set()方法设置一些属性值
如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字
如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例
与上面的类似,如果实现了其他*Aware接口,就调用相应的方法
如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法
该方法会在对象属性被设置后,即:调用了setter方法之后被调用
如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法
如果Bean在配置文件中的定义包含init-method属性,执行指定的方法
如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法
当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法
当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。
bean的生命周期
变量方式注入非常简洁,没有任何多余代码
变量注入
构造器注入
set方法注入
注入
IOC
Nullable
NonNull
NonNullApi
NonNullFields
lang
控制Bean加载顺序
DependsOn
ClassPathXmlApplicationContext启动的Spring容器,通过ClassPathResource以类路径的方式进行访问
FileSystemXmlApplicationContext启动的Spring容器,通过FileSystemResource以文件系统绝对路径的方式进行访问
XmlWebApplicationContext启动的Spring容器,通过ServletContextResource以相对于Web应用根目录的方式进行访问
与ApplicationContext相同的策略
前缀"classpath:"是指定使用ClassPathResource
前缀"file:"则指定使用UrlResource
ResourceLoader
core
拦截器(Interceptor)
web.servlet
Prepare this context for refreshing.
prepareRefresh()
Tell the subclass to refresh the internal bean factory.
obtainFreshBeanFactory()
Prepare the bean factory for use in this context.
DefaultListableBeanFactory.registerResolvableDependency
这些特殊实例变量直接获取
注册了可解析依赖项
Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory)
Invoke factory processors registered as beans in the context
invokeBeanFactoryPostProcessors(beanFactory)
Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory)
initMessageSource()
initApplicationEventMulticaster()
onRefresh()
registerListeners()
finishBeanFactoryInitialization(beanFactory)
finishRefresh()
refresh ()
AbstractApplicationContext
support
默认接口代理
boolean proxyTargetClass() default false;
EnableAspectJAutoProxy
例如@EnableAspectJAutoProxy
可以在类上使用,也可以作为元注解使用
可以拆分配置类,然后在程序中按需导入相应的配置
例子 @EnableRetry
@Configuration标注的配置类
根据给定的条件(AdviceMode),选择导入哪些配置类
例如@EnableTransactionManagemen
实现ImportSelector接口的类
按需注册额外的BeanDefinition
例如@EnableAspectJAutoProxy注解
实现ImportBeanDefinitionRegistrar接口的类
@component类
导入
@Import
@Autowired能够用在构造方法、成员变量、方法参数以及注解上
容易违背了单一职责原则 使用这种基于 field 注入的方式,添加依赖是很简单的,就算你的类中有十几个依赖你可能都觉得没有什么问题,普通的开发者很可能会无意识地给一个类添加很多的依赖。但是当使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现 something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则(SRP:Single responsibility principle)。
依赖注入与容器本身耦合
基于 field 注入的坏处
强制依赖就用构造器方式
可选、可变的依赖就用 setter 注入
@Autowired
TransactionAspectSupport
TransactionInterceptor
注解从切面进入事务.所以非切面调用事务不生效
获取事务属性
getTransactionAttribute
确定事务管理器
determineTransactionManager(txAttr)
创建事务管理器如果有必要
AbstractPlatformTransactionManager
createTransactionIfNecessary
调用真实方法
retVal = invocation.proceedWithInvocation();
invokeWithinTransaction
interceptor
abstract Object doGetTransaction()
abstract void doCommit(DefaultTransactionStatus status)
abstract void doRollback(DefaultTransactionStatus status)
存储线程开启事务的 数据库连接
TransactionSynchronizationManager
transaction
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
doGetTransaction()
DataSourceTransactionManager extends AbstractPlatformTransactionManager
datasource
jdbc
DefaultListableBeanFactory
factory
beans
org.springframework
spring
RequestMappingHandlerAdapter
doDispatch
doService
前端控制器(DispatcherServlet)
处理器映射器(HandlerMapping)
拦截器
Handler处理器
处理器适配器(HandlerAdapter)
视图解析器(ViewResolver)
视图(View)
1.发起请求到前端控制器(DispatcherServlet)2.前端控制器请求处理器映射器(HandlerMapping)查找Handler(可根据xml配置、注解进行查找)3.处理器映射器(HandlerMapping)向前端控制器返回Handler4.前端控制器调用处理器适配器(HandlerAdapter)执行Handler5.处理器适配器(HandlerAdapter)去执行Handler6.Handler执行完,给适配器返回ModelAndView(Springmvc框架的一个底层对象)7.处理器适配器(HandlerAdapter)向前端控制器返回ModelAndView8.前端控制器(DispatcherServlet)请求视图解析器(ViewResolver)进行视图解析,根据逻辑视图名解析成真正的视图(jsp)9.视图解析器(ViewResolver)向前端控制器(DispatcherServlet)返回View10.前端控制器进行视图渲染,即将模型数据(在ModelAndView对象中)填充到request域11.前端控制器向用户响应结果
springmvc将url和controller方法映射。映射成功后springmvc生成一个Handler对象,对象中只包括了一个method
基于mvc拦截器
spring-mvc
\treturn Flux.fromIterable(this.handlerMappings) \t\t// 1.遍历所有的 handlerMapping
.concatMap(mapping -> mapping.getHandler(exchange)).next() // 2.获取对应的handlerMapping ,比如常用的 RequestMappingHandlerMapping、RoutePredicateHandlerMapping
处理所有过滤器链的方法
LoadBalancerClientFilter
ForwardRoutingFilter
转发路由网关过滤器
基于 Netty 实现的 HttpClient 请求后端 Http 服务
NettyRoutingFilter
将 NettyRoutingFilter 请求后端 Http 服务的响应写回客户端
NettyWriteResponseFilter
WebsocketRoutingFilter
websocket
WebClientHttpRoutingFilter
WebClientWriteResponseFilter
org.springframework.cloud.gateway.filter.WebClient实现的 HttpClient 请求后端 Http 服务
webclient
globalFilters
见gatway
网关适配器
webHandler.handle
SimpleHandlerAdapter
HandlerAdapter
链式调用
handle
DispatcherHandler
AbstractHandlerMethodMapping
AbstractUrlHandlerMapping
RouterFunctions
PropertySourcesPropertyResolver
Backpressure 其实是一种现象:在数据流从上游生产者向下游消费者传输的过程中,上游生产速度大于下游消费速度,导致下游的 Buffer 溢出,这种现象就叫做 Backpressure 出现。
反应式 HTTP
服务器推送事件
WebSocket 的客户端和服务器端的支持
Reactor
spring-webflux
cap原理
DistroConsistencyServiceImpl
服务端注册
心跳
特殊场景
服务端某节点宕机,不回复其他服务端节点的健康检查请求,则会被其他节点从健康节点列表中剔除,其他节点重新分配负责节点,依靠客户端的心跳重新建立完整的服务数据
验证任务startVerifyTask()
同步任务startLoadTask();
纯内存保存
Distro
EphemeralConsistencyService
POST HTTP://{ip:port}/v1/ns/raft/vote : 进行投票请求
POST HTTP://{ip:port}/v1/ns/raft/beat : Leader向Follower发送心跳信息
GET HTTP://{ip:port}/v1/ns/raft/peer : 获取该节点的RaftPeer信息
PUT HTTP://{ip:port}/v1/ns/raft/datum/reload : 重新加载某日志信息
POST HTTP://{ip:port}/v1/ns/raft/datum : Leader接收传来的数据并存入
DELETE HTTP://{ip:port}/v1/ns/raft/datum : Leader接收传来的数据删除操作
GET HTTP://{ip:port}/v1/ns/raft/datum : 获取该节点存储的数据信息
GET HTTP://{ip:port}/v1/ns/raft/state : 获取该节点的状态信息{UP or DOWN}
POST HTTP://{ip:port}/v1/ns/raft/datum/commit : Follower节点接收Leader传来得到数据存入操作
DELETE HTTP://{ip:port}/v1/ns/raft/datum : Follower节点接收Leader传来的数据删除操作
RaftConsistencyServiceImpl
Raft
PersistentConsistencyService
DelegateConsistencyServiceImpl
nacos
consul
ZK是往Leader(主节点)去写数据
zookeeper(见hadoop分支)
注册中心
SPI重写线程池获取ThreadLocal值
线程隔离
信号量隔离
为每一个需要被调用的服务维护了一个独立的线程池,这样每个服务都有自己的使用资源,当某个服务出现问题时,大家互不影响。
信号量隔离我理解为对线程池隔离的一个补充功能,如果开辟专有线程池的开销远远大于该依赖服务正常的访问时间,那么我们就可以使用信号量隔离。
信号量的开销确实是小于线程池的开销的,不过信号量做不到线程池的设置超时和异步访问,所以啊,在确保服务确实靠谱的情况下,再采用信号量哈
hystrix舱壁模式
构建HystrixCommand或HystrixObservableCommand
hystrix
dashboard
统计数据:统计某个资源的访问数据(QPS、RT等信息)
规则判断:判断限流规则、隔离规则、降级规则、熔断规则是否满足
限流、隔离、降级、熔断
基于责任链模式来设计,将不同的功能(限流、降级、系统保护)封装为一个个的Slot,请求进入后逐个执行
StatisticSlot:负责统计实时调用数据,包括运行信息、来源信息等
ClusterBuilderSlot:负责构建某个资源的ClusterNode,ClusterNode可以保存资源的运行信息(响应时间、QPS、block 数目、线程数、异常数等)以及来源信息(origin名称)
NodeSelectorSlot:负责构建簇点链路中的节点(DefaultNode),将这些节点形成链路树
statistic
AuthoritySlot:负责授权规则(来源控制)
SystemSlot:负责系统保护规则
ParamFlowSlot:负责热点参数限流规则
FlowSlot:负责限流规则
DegradeSlot:负责降级规则
rule checking
ProcessorSlotChain
sentinel
熔断限流
actuator
extends AbstractHandlerMapping(spring-webflux)
寻找路由匹配 返回 webHandler
找到yml中配置的所有的路由断言工厂执行
lookupRoute
getHandlerInternal
找到对应的适配器HandlerAdaptor,执行过滤器链
RoutePredicateHandlerMapping
globalFilters转化成GatewayFilterAdapter。 GatewayFilterAdapter在内部集成了GlobalFilter,同时也实现了GatewayFilter,使 globalFilters和gatewayFilters在 适配器 类GatewayFilterAdapter中共存
implements WebHandler(spring-web)
FilteringWebHandler
见 spring
gateway
网关
#feignfeign.httpclient.enabled=truefeign.hystrix.enabled=truefeign.client.config.default.connectTimeout = 30000feign.client.config.default.readTimeout = 30000#ribbonribbon.OkToRetryOnAllOperations=falseribbon.ConnectTimeout=20000ribbon.ReadTimeout=50000ribbon.SocketTimeout=30000ribbon.ServerListRefreshInterval=10 ribbon.MaxAutoRetries=0ribbon.MaxAutoRetriesNextServer= 1#hystrixhystrix.command.default.execution.timeout.enabled=truehystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=20000
springcloud
collection
结果嵌套
SqlSessionFactoryBuilder(构造器)
SqlSessionFactory:依靠工厂来生成SqlSession。
SqlSession:是一个既可以发送SQL去执行并返回结果的,也可以获取Mapper接口,通过Mapper接口查询并封装数据。
SQL Mapper:它是MyBatis新设计的组件,它是由一个Java接口和XML文件(或者注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。
执行流程
异常
xml解析,${} 格式的字符串解析
org.apache.ibatis.parsing
解析
实现java和jdbc中的类型之间转换
类型处理器
绑定
注解
映射
执行器
org.apache.ibatis.transactionorg.apache.ibatis.transaction.jdbcorg.apache.ibatis.transaction.managed
数据源
org.apache.ibatis.sessionorg.apache.ibatis.session.defaults
mapper.xml中的namespace和实际的mapper文件不一致
mapper接口中的方法名和mapper.xml中的id标签不一致
Mapper.xml没有构建进去
配置文件导不出来
mapper-locations 配置 xml
Invalid bound statement (not found)
BindingException
generator
PersistenceException
exceptions
selectList()
defaults
session
SqlSession执行增删改查都是委托给Executor
CachingExecutor用于处理二级缓存,如果缓存中不存在要查询的数据,那么将查询请求委托给其他的Executor。如果是执行SQL的增删改,那么CachingExecutor将清空二级缓存。
CachingExecutor
BaseExecutor是除CachingExecutor之外,其他Executor实现类的基类。该类主要处理一级缓存
BaseExecutor
SimpleExecutor
BatchExecutor
executor
Plugin
MybatisPlusInterceptor(mybaits-plus实现)
PageInterceptor(pagehelper实现)
PaginationInterceptor(mybatis-plus作废的实现)
Interceptor
plugin
apache.ibatis
SqlSessionTemplate
mybatis
org
protected long limit = 500L;
PaginationInterceptor
extension
com.baomidou.mybatisplus
mybatisplus
getConnectionDirect
GetConnectionTimeoutException
DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
initialSize
最大连接池数量
maxActive
最小连接池数量
minIdle
获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
maxWait
是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
poolPreparedStatements
申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
默认 true
testOnBorrow
testOnReturn
默认 false
有两个含义: 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle false
配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis
testWhileIdle
Druid
锁定事务单元(lock)
确认事务模块状态(confirm)
通知事务(notify)
TX-LCN
维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
Transaction Coordinator (TC): 事务协调器
负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
Transaction Manager (TM): 控制全局事务的边界
负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
Resource Manager (RM): 控制分支事务
TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行
Read Uncommitted
隔离级别
TM 要求 TC 开始新的全局事务。TC 生成一个代表全局事务的 XID。
XID 通过微服务的调用链传播
RM将本地事务注册为XID对应的全局事务的一个分支到TC。
TM 要求 TC 提交或回滚 XID 对应的全局事务。
TC 驱动 XID 对应的全局事务下的所有分支事务完成分支提交或回滚。
Seata
注册NioServerSocketChannel
注册NioSocketChannel
管道(SelectableChannel)
选择器(Selector)
选择键(SelectorKey)
Selector(多路复用器)
NioEventLoop
NioEventLoopGroup
一个Channel包含一个ChannelPipeline,所有ChannelHandler都会注册到ChannelPipeline中,并按顺序组织起来。
Channe
客户端发起请求再接受请求,先 outbound再inbound。服务端:先接受请求再发送请求,先inbound再outbound.
NettyInbound
sendUpstream
ChannelSink
NettyOutbound
sendDownstream
一个ChannelEvent并不会主动的"流"经所有的Handler,而是由上一个Handler显式的调用ChannelPipeline.sendUp(Down)stream产生,并交给下一个Handler处理。也就是说,每个Handler接收到一个ChannelEvent,并处理结束后,如果需要继续处理,那么它需要调用sendUp(Down)stream新发起一个事件。如果它不再发起事件,那么处理就到此结束,即使它后面仍然有Handler没有执行。这个机制可以保证最大的灵活性,当然对Handler的先后顺序也有了更严格的要求。
ChannelPipeline
EventLoop
ChannelUpstreamHandler
ChannelDownstreamHandler
ChannelHandler
模型
第一创建与服务端的连接(即OP_CONNECT事件),第二就是进行IO读写、编解码、业务逻辑等操作(即OP_READ事件、OP_WRITE事件)
客户端发出连接请求的同时会自己创建一条NioSocketChannel通道与服务端NioSocketChannel进行互通,连接完之后就是WorkGroup的事了,不需要BossGroup管了
一个客户端连接对应一条服务端NioSocketChannel
ClientGroup
向WorkGroup中的某个Selector注册刚才创建好的NioSocketChannel
ServerBootStrapAcceptor
创建一个NioSocketChannel实例
对客户端的新连接请求进行处理(OP_ACCEPT)
BossGroup
负责处理IO读写、编解码、业务逻辑等(即OP_READ事件、OP_WRITE事件)
WorkGroup
服务端启动的时候会绑定一个端口,作为后续客户端连接入口
服务端
SelectionKey.OP_READ
SelectionKey.OP_WRITE
SelectionKey.OP_ACCEPT
SelectionKey.OP_CONNECT
IO事件类型
对应 mainReactor
对应 subReactor
多线程的Reactor模式
EpollEventLoop
MetricsMonitor
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。在HttpServletResponse到达客户端之前,拦截HttpServletResponse。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
过滤器
servlet
ConnectionImpl
mysql-connect-java
当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
scan
用来设置某一个包或者具体的某一个类的日志打印级别、以及指定 <appender>。<logger> 仅有一个name属性,一个可选的level和一个可选的addtivity属性
<logger>
是负责写日志的组件。该标签负责以适当的格式将日志记录事件输出到适当的输出设备
滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件
RollingFileAppender
常用的 appender 类型
对日志进行格式化
<encoder>
<appender>
<configuration>
标签
logback
log
gRPC (gRPC Remote Procedure Calls) 是 Google 发起的一个开源远程过程调用系统
gRPC (gRPC Remote Procedure Calls)
Dubbo是阿里巴巴开源的基于 Java 的高性能RPC(一种远程调用) 分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
泛化调用
Dubbo
rpc
使用表达式访问Json数据
JSONPath
fastjson
多态类型的序列化及反序列化
JsonTypeInfo
Jackson
json
JSqlParser
Keras
线性内核线性内核是最简单的内核函数。
多项式内核多项式验证非标准内核函数,该函数非常适合于正交归一化的数据。
高斯核这里是经典的鲁棒径向基核,即高斯核函数。
指数核。
拉普拉斯内核。
方差分析内核。
乙状结肠
Wave内核
三角核
日志内核
内核功能
TensorFlow
Raw
通过Raw对象和事件事件点(event times)
通过读取.fif文件数据生成Epoch对象
通过mne.EpochsArray从头创建Epoch对象
创建Epochs对象方式
从连续的脑电图信号中提取一些特定时间窗口的信号,分析事件,这些时间窗口可以称作为epochs.
Epochs
Evoked potential(EP)诱发电
Evoked
sleep_physionet
dataset
mne
PyTorch是使用GPU和CPU优化的深度学习张量库。
含了多维张量的数据结构以及基于其上的多种数学操作。另外,它也提供了多种工具,其中一些可以更有效地对张量和任意类型进行序列化。
张量 Tensors
优化算法的库
对SGD的扩展,可以代替经典的随机梯度下降法来更有效地更新网络权重。
为每一个参数保留一个学习率以提升在稀疏梯度(即自然语言和计算机视觉问题)上的性能。
适应性梯度算法(AdaGrad)
基于权重梯度最近量级的均值为每一个参数适应性地保留学习率。这意味着算法在非稳态和在线问题上有很有优秀的性能。
均方根传播(RMSProp)
Adam 算法同时获得了 AdaGrad 和 RMSProp 算法的优点。Adam 不仅如 RMSProp 算法那样基于一阶矩均值计算适应性参数学习率,它同时还充分利用了梯度的二阶矩均值(即有偏方差/uncentered variance)。具体来说,算法计算了梯度的指数移动均值(exponential moving average),超参数 beta1 和 beta2 控制了这些移动均值的衰减率。移动均值的初始值和 beta1、beta2 值接近于 1(推荐值),因此矩估计的偏差接近于 0。该偏差通过首先计算带偏差的估计而后计算偏差修正后的估计而得到提升。
adam
torch.optim
该接口定义在dataloader.py脚本中,只要是用PyTorch来训练模型基本都会用到该接口,该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入
DataLoader
data
util
多个GPU来加速训练
nn.DataParallel
torch.nn
torch
sklearn是基于python语言的机器学习工具包
广义线性模型
普通最小二乘法
监督学习
pipeline
sklearn
框架组件库
王鹏博的技术树
0 条评论
下一页