java学习路线
2023-03-03 13:45:48 30 举报
AI智能生成
面试
作者其他创作
大纲/内容
职场最重要的几件事
健康工作、开心生活
工作中多和领导沟通,让对方知道你现在的困难点,疑惑点这样有利于自己的快速成长 项目遇到阻力也要第一时间表达出来,按时完成是你和领导共同的kpi 你延期了他可能比你还难受,而他不会丢饭碗,你会
确保当前做的事情是有意义的,能给自己不断带来成长的,职场前几年不要太看重钱,能力上去钱很容易就来了
跳出当前所做事情的局限,带着leader的思维去干好你每一年的工作你才有可能加速让自己成为leader
定期给自己定一些小目标,确保目标是正确的,然后拆分目标一件件落实
埋头苦干之前,先抬头看路
投资最重要的事情
不要碰自己不懂的行业,只知道皮毛不算懂,最好自己从事什么行业,就投资这个行业的头部公司
如果只是投一点点钱玩玩,没必要天天看涨跌或喜或忧的,放那不动,好好工作,最后你会发现还是工作来钱快
因为无知,所以大家不敢重点投资一两家公司,买几个十几个股票的大有人在,因为你还不够了解,如果你足够了解市场和公司的时候就应该只投几家你足够有把我的公司然后等风来。
人生只需要把握一到两次重大的投资 all in进去即可翻身了,几万几千的小打小闹不会改变你的生活,如果没这个认知之前,不如好好工作赚钱买房来的实在,浅显的投资会分散你的精力
投资本身涉及的点不应该局限于眼前,而是未来,行业的未来,公司在这个行业所处的地位,未来几年是否有重大突破,管理层价值观你是否认可等等一系列因素,不要因为你觉得好就买了,这根赌没啥区别
学习方法
我的学习方法比较简单,北极星目标,给自己定下目标之后,不断拆分 每次完成小目标就离大目标进一步
卸载掉哪些信息流软件,他们杀你时间的方式太简单粗暴了
我之前是大数据团队的我们会和算法有很多合作,我知道算法推荐不断完善模型背后的目的,就是杀死用户的时间
我看书学习的时候会把手机丢很远,其实也不会有什么重要的人和事情找你,不要一直看,真的着急他们会打电话的。
坚持
集合
HashMap
1.7
数组+链表
头插
1.8
数组+链表+红黑树
尾插
扩容机制
LoadFactory 默认0.75
创建一个空数组重新Hash
Hash公式跟长度有关
线程不安全
子主题
2的幂次
方便位运算
均匀分布
重写equals必须重写HashCode
ConcurrentHashMap
安全失败
1.7
数组+链表
segment分段锁
继承了reentranLock
子主题
尝试获取锁存在并发竞争 自旋 阻塞
get高效 volatile修饰 不需要加锁
volatile修饰节点指针
HashEntry
1.8
数组+链表+红黑树
CAS+synchronized
cas失败自旋保证成功
再失败就sync保证
node
ArrayList
数组
查找 访问速度快 增删效率低 线程不安全
LinkedList
双向链表
适合插入删除频繁的情况 内部维护了链表的长度
基础
UDP
语音
视频
直播
TCP
三次握手
syn seq
syn ack seq
ack seq
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
四次挥手
fin ack seq
ack seq
fin ack seq
fin ack seq
ack seq
场景
网络会话
文件传输
发送接收邮件
远程登录
如何保证可靠
HTTP
HTTPS
为啥安全
BIO
阻塞等待链接
阻塞等待数据
开线程处理并发
耗资源
NIO
非阻塞IO
epoll
粘包/拆包
在报文末尾增加换行符表明一条完整的消息,这样在接收端可以根据这个换行符来判断消息是否完整。
将消息分为消息头、消息体。可以在消息头中声明消息的长度,根据这个长度来获取报文(比如 808 协议)。
规定好报文长度,不足的空位补齐,取的时候按照长度截取即可。
将消息分为消息头、消息体。可以在消息头中声明消息的长度,根据这个长度来获取报文(比如 808 协议)。
规定好报文长度,不足的空位补齐,取的时候按照长度截取即可。
多路复用
select
单个进程可监视的fd数量被限制,即能监听端口的大小有限。
对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
poll
大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义
poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll
没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。
内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
neetty 非异步 阻塞 response trse id 感觉
序列化
多线程
synchronized
对象
对象头(Header)
Mark Word(存储对象的HashCode,分代年龄和锁标志位信息。)
Klass Point(对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。)
Monitor
EntryList
Owner(会指向持有 Monitor 对象的线程)
WaitSet
实例数据
对其填充
方法
ACC_SYNCHRONIZED
代码块
monitorenter
monitorexit
程序计数器 count
加减
锁膨胀
无锁
偏向锁
mark Word 中有线程信息 cas 比较
轻量级
复制了一份mark work 叫 Lock record 也是cas尝试改变指针
自旋
死循环
重量级
特性保证
有序性
as-if-serial
happens-before
可见性
内存强制刷新
原子性
单一线程持有
可重入性
计数器
重锁
用户态内核态切换
sync 和 Lock的区别
synchronized是关键字,是JVM层面的底层啥都帮我们做了,而Lock是一个接口,是JDK层面的有丰富的API。
synchronized会自动释放锁,而Lock必须手动释放锁。
synchronized是不可中断的,Lock可以中断也可以不中断。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
synchronized能锁住方法和代码块,而Lock只能锁住代码块。
Lock可以使用读锁提高多线程读效率。
synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。
synchronized会自动释放锁,而Lock必须手动释放锁。
synchronized是不可中断的,Lock可以中断也可以不中断。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
synchronized能锁住方法和代码块,而Lock只能锁住代码块。
Lock可以使用读锁提高多线程读效率。
synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。
劣势
锁升级不可逆
ThreadLocal
内存泄露
session
Lock
ReentrantReadWriteLock
ReadLock
WriteLock
ReentrantLock
NonfairSync
tryAcquire
acquireQueued
CAS
FairSync
hasQueuedPredecessors
如果是当前持有锁的线程 可重入
AbstractQueuedSynchronizer
入队 出队
头结点设计
共享和独享的实现
CAS
实际应用
存在的问题
cpu开销
只能保证一个共享变量原子操作
AtomicReference
ABA
标志位 时间戳
StampedLock
volatile
MESI
当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取
锁bus
volitale会一直嗅探 cas 不断循环无效交互 导致带宽达到峰值
总线风暴
Java内存模型JMM
高速缓存
可见性
嗅探机制 强制失效
处理器嗅探总线
有序性
禁止指令重排序
lock 前缀指令 内存屏障
源代码->编译器优化重排序->指令级并行重排序->内存系统重排序->最终执行的指令序列
happens-before
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
as-if-serial
AtomicInteger
跳出死循环
线程池
newFixedThreadPool
newCacheThreadPool
SynchronousQueue
newSIngleTheadExecutor
newScheduledThewadPool
DelayedWorkQueue
newWorkStealingPool
ThreadPoolExecutor
参数意义
核心线程数
默认没线程等任务来了才调用 除非调用了 预创建线程 一个或者全部
最大线程数
空闲时间&单位
没有执行任务多久会终止 当线程池的线程数大于核心线程才会起作用 调用allowCoreThreadTimeOut会起作用
缓冲队列
LinkedBlockingQueue
无界 当心内存溢出
ArrayBlockingQueue
有界队列
加锁保证安全 一直死循环阻塞 队列不满就唤醒
入队
阻塞调用方式 put(e)或 offer(e, timeout, unit)
阻塞调用时,唤醒条件为超时或者队列非满(因此,要求在出队时,要发起一个唤醒操作)
进队成功之后,执行notEmpty.signal()唤起被阻塞的出队线程
阻塞调用时,唤醒条件为超时或者队列非满(因此,要求在出队时,要发起一个唤醒操作)
进队成功之后,执行notEmpty.signal()唤起被阻塞的出队线程
在进行某项业务存储操作时,建议采用offer进行添加,可及时获取boolean进行判断,如用put要考虑阻塞情况(队列的出队操作慢于进队操作),资源占用。
Synchronous
工厂方法
拒绝策略
抛异常
丢弃
重试
丢弃最早提交的
使用Has表维护线程的引用
submit
使用future获取任务的执行结果
实际使用
商品详情界面
批处理
执行过程
核心线程->队列->最大线程->拒绝策略
运行状态
有个Volatile的状态码
running
shutdown
stop
terminated
所有线程销毁
corePoolSize、maximumPoolSize、largestPoolSize 有意思
故障
JUC
常见问题
线程间是怎么进行通信的?
主要可以介绍一下 wait/notify 机制,共享变量的 synchronized 或者 Lock 同步机制等。
volatile
CountDownLatch
CyclicBarrier
ThreadLocal 用来解决什么问题?
可以从尽量减少临界区范围,使用 ThreadLocal,减少线程切换、使用读写锁或 copyonwrite 等机制这些方面来回答。
如何尽可能提高多线程并发性能?
ThreadLocal 是如何实现的?可以重点回答 ThreadLocal 不是用来解决多线程共享变量的问题,而是用来解决线程数据隔离
读写锁适用于什么场景?
可以回答读写锁适合读并发多,写并发少的场景,另外一个解决这种场景的方法是 copyonwrite。
如何实现一个生产者与消费者模型?
可以尝试通过锁、信号量、线程通信、阻塞队列等不同方式实现。
线程状态
线程就绪
start
sleep休眠超时
countDownLatch
只有一个构造方法 只会被赋值一次
没有别的方法可以修改 count
JVM
JVM内存模型
堆
栈
方法区
本地方法栈
程序计数器
类加载机制
加载->验证->准备->解析->初始化->使用->卸载
双亲委派原则
父类加载 不重复加载
分代回收
年轻代
Eden/s1/s2
老年代
永久代/元空间
晋升机制
根据存活时间
垃圾回收机制
标记清除
适用场景
对象存活比较多的时候适用
老年代
缺点
提前GC
碎片空间
扫描了两次
标记存活对象
清除没有标记的对象
标记复制
适合场景
存活对象少 比较高效
扫描了整个空间(标记存活对象并复制异动)
适合年轻代
缺点
需要空闲空间
需要复制移动对象
引用计数
没办法解决循环引用的问题
标记整理
垃圾回收器
CMS
分代
年轻
edan
s1
s2
minor gc
通过阈值晋升
老年
major gc 等价于 full gc
永久
缺点
对cpu资源敏感
无法处理浮动垃圾
基于标记清除算法 大量空间碎片
G1
分区概念 弱化分代
标记整理算法 不会产生空间碎片 分配大对象不会提前full gc
可以设置预设停顿时间
充分利用cpu 多核条件下 缩短stw
收集步骤
初始标记 stw 从gc root 开始直接可达的对象
并发标记 gc root 对对象进行可达性分析 找出存活对象
可达性分析算法
最终标记
筛选回收
根据用户期待的gc停顿时间指定回收计划
回收模式
young gc
回收所有的eden s区
复制一些存活对象到old区s区
mixed gc
GC模式
区别
g1分区域 每个区域是有老年代概念的 但是收集器以整个区域为单位收集
g1回收后马上合并空闲内存 cms 在stw的时候做
内存区域设置
XX:G1HeapRegionSize
复制成活对象到一个区域 暂停所有线程
full gc
老年代写满
system。gc
持久代空间不足
STW
实战
性能调优
设置堆的最大最小值 -xms -xmx
调整老年和年轻代的比例
-XX:newSize设置绝对大小
防止年轻代堆收缩:老年代同理
主要看是否存在更多持久对象和临时对象
观察一段时间 看峰值老年代如何 不影响gc就加大年轻代
配置好的机器可以用 并发收集算法
每个线程默认会开启1M的堆栈 存放栈帧 调用参数 局部变量 太大了 500k够了
原则 就是减少gc stw
FullGC 内存泄露排查
jasvism
dump
监控配置 自动dump
oom种类
逃逸分析
可达性
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量变量引用的对象
本地方法栈中JNI(即一般说的Native方法)引用的对象
活跃线程的引用对象
方法区中类静态属性引用的对象
方法区中常量变量引用的对象
本地方法栈中JNI(即一般说的Native方法)引用的对象
活跃线程的引用对象
JVM调优
OOM
内存泄露
线程死锁
锁争用
Java进程消耗CPU过高
JVM性能检测工具
Jconsole
Jprofiler
jvisualvm
MAT
内存泄露
help dump
生产机 dump
mat
jmap
-helpdump
CPU100%
topc -c
top -Hp pid
jstack
进制转换
cat
Spring
设计模式
单例
工厂
适配器
根据不同商家适配
责任链
继承 process 链路执行
源码
Bean
生命周期
扫描类
invokeBeanFactoryPostProcessors
封装beanDefinition对象 各种信息
放到map
遍历map
验证
能不能实例化 需要实例化么 根据信息来
是否单例等等
判断是不是factory bean
单例池 只是一个ConcurrentHashMap而已
正在创建的 容器
得到 class
推断构造方法
根据注入模型
默认
得到构造方法
反射 实例化这个对象
后置处理器合并beanDefinition
判断是否允许 循环依赖
提前暴露bean工厂对象
填充属性
自动注入
执行部分 aware 接口
继续执行部分 aware 接口 生命周期回调方法
完成代理AOP
beanProstprocessor 的前置方法
实例化为bean
放到单例池
销毁
作用域
单例(singleton)
多例(prototype)
Request
Session
循环依赖
情况
属性注入可以破解
构造器不行
三级缓存没自己 因二级之后去加载B了
三级缓存
去单例池拿
判断是不是正在被创建的
判断是否 支持循环依赖
二级缓存 放到 三级缓存
干掉二级缓存
GC
下次再来直接 三级缓存拿 缓存
缓存 存放
一级缓存 单例Bean
二级缓存 工厂 产生baen
产生bean 复杂
三级缓存 半成品
父子容器
事务实现原理
采用不同的连接器
用AOP 新建立了一个 链接
共享链接
ThreadLocal 当前事务
前提是 关闭AutoCommit
AOP
静态代理
实现类
动态代理
JDK动态代理
实现接口
java反射机制生成一个代理接口的匿名类
调用具体方法的时候调用invokeHandler
cjlib
asm字节码编辑技术动态创建类 基于classLoad装载
修改字节码生成子类去处理
IOC
类加载机制
过程
加载
生成一个class对象
验证
文件格式验证
元数据验证
字节码验证
符号引用验证
准备
默认值
static会分配内存
解析
解析具体类的信息
引用等
初始化
父类没初始化 先初始化父类
使用
卸载
加载方式
main()
class。forName
ClassLoader。loadClass
类加载器
Appclass Loade
Extention ClassLoader
Bootstrap ClassLoader
双亲委派原则
可以避免重复加载
安全
Redis
数据结构
String
Hash
set
zset
score
随机层数
只需要调整前后节点指针
不止比较score
还会比较value
场景
成绩
积分
排行榜
List
分页的坑
HyperLogLog
Geo
Pub/Sub
BitMap
底层
SDS
键值的底层都是SDS
AOF缓存区
记录本身长度 C需要遍历
修改字符减少内存重新分配
空间预支配
惰性空间释放
二进制安全
C只能保存文本数据 无法保存图片等二进制数据
sds是使用长度去判断
杜绝缓冲区溢出
兼容部分C字符串函数
链表
保存多个客户端的状态信息
列表订阅发布 慢查询 监视器
字典
数据库 哈希键
Hash表节点
hash冲突用单向链表解决
渐进式 rehash
会逐渐rehash 新的键值对全部放到新的hash表
每个字典带 两个hash表
一个平时用 一个rehash时候用
压缩列表
整数集合
常见命令
Keys
setnx
exprie
高可用
持久化
RDB
5分钟一次
冷备
恢复的时候比较快
快照文件生成时间久,消耗cpu
AOF
appendOnly
数据齐全
回复慢文件大
数据初始化
从节点发送命令主节点做bgsave同时开启buffer
数据同步机制
主从同步
指令流
offset
快照同步
RDB
缓冲区
哨兵
集群监控
消息通知
故障转移
配置中心
脑裂
集群
链表
多主
横向扩容
分片
常见问题
缓存雪崩
加随机值
集群部署
缓存击穿
互斥锁
热点数据不失效
缓存穿透
布隆过滤器
双写一致性
延时双删
并发竞争
分布式锁
大Key
bigkey命令 找到干掉
Redis 4.0引入了memory usage命令和lazyfree机制
热点key
缓存时间不失效
多级缓存
布隆过滤器
读写分离
过期策略
定时删除
消耗内存
创建一个定时器
惰性删除
可能存在大量key
定期删除
检查 删除 但是是随机的
淘汰机制
LUR
最少使用
备用方案
主从+哨兵+cluster
ecache+Hystrix+降级+熔断+隔离
持久化
限流
setnx ex
zset
窗口滑动
zset会越来越大
令牌
定时push
然后leftpop
问题
空轮训
blpop
空连接异常
重试
漏桶 funnel
make_space 灌水之前调用漏水 腾出空间 取决于流水速率
Hash
原子性有问题
Redis-Cell
redis cell
选择方案
热点key
二级缓存
备份热key 走不同的机器
扩展
跳跃表
令牌
漏桶
scan
多路IO复用
read得读到很多才返回 为0会卡在那 直到新数据来或者链接关闭
写不会阻塞除非缓冲区满了
非阻塞的IO 提供了一个选项 no_blocking 读写都不会阻塞 读多少写多少 取决于内核的套接字字节分配
非阻塞IO也有问题 线程要读数据 读了一点就返回了 线程什么时候知道继续读?写一样
一般都是select解决 但是性能低 现在都是epoll
数据库
MySQL
mysql调优
1.业务上的优化
查count时间太长,新增表,将count存到表里
慢查询警报,出现了慢查询往企业微信上发警报日志
2.代码层的优化
3.sql层面的优化(包含索引优化)
4.硬件层优化
分库分表
唯一主键
事务隔离级别
读未提交
没视图概念 都是返回最新的
读已提交
不同的read view
可重复度
用一个read view
序列化
回滚日志
没更早的read view删除
5.5之前回滚段删了文件也不会变小
索引
B+
Hash
等值查询
优化流程
预发跑sql explain
排除 缓存 sql nocache
看一下行数对不对 不对可以用analyze table t 矫正
添加索引 索引不一定是最优的 force index 强制走索引 不建议用
存在回表的情况
覆盖索引避免回表,不要*
主键索引
联合索引 不能无限建 高频场景
最左前缀原则 按照索引定义的字段顺序写sql
合理安排联合索引的顺序
5.6之后 索引下推 减少回表次数
给字符串加索引
前缀索引
倒序存储
Hash
数据库的flush的时机
redo log满了 修改checkpoint flush到磁盘
系统内存不足淘汰数据页
buffer pool
要知道磁盘的io能力 设置innodb_io_capacity 设置为磁盘的IOPS fio测试
innodb_io_capacity设置低了 会让innoDB错误估算系统能力 导致脏页累积
系统空闲的时候 找间隙刷脏页
MySQL正常关闭,会把内存脏页flush到磁盘
innodb刷盘速度
脏页比例
redolog 写盘速度
innodb_flush_neighbors 0
机械磁盘的随机io不太行 减少随机io性能大幅提升 设置为 1最好
现在都是ssd了 设置为0就够了 8.0之后默认是0
索引字段不要做函数操作,会破坏索引值的有序性,优化器会放弃走树结构
如果触发隐式转换 那也会走cast函数 会放弃走索引
字符集不同可能走不上索引
convert 也是函数所以走不上
聚集索引
非聚集索引
多扫描一次
减少回表
索引维护
页满了 页分裂 页利用率下降
数据删除 页合并
自增 只追加可以不考虑 也分页
索引长度
索引选择
普通索引
找到第一个之后 直到朋友不满足的
唯一索引
找到第一个不满足的就停止了
覆盖索引
包含主键索引值
最左前缀原则
安排字段顺序
索引空间问题
hash
5.6之后索引下推
不需要多个回表 一边遍历 一边判断
页的概念
更新
change buffer
更新操作来了 如果数据页不在内存 就缓存下来 下次来了 更新 在就直接更新
唯一索引 需要判断 所以 用不到change buffer
innodb的处理流程
记录在页内存
唯一索引 判断没冲突插入
普通索引 插入
记录不再页中
数据页读入内存 判断 插入
change buffer
数据读是随机IO 成本高
机械硬盘 change buffer 收益大 写多读少 marge
MVCC
版本链 在聚集索引中 有两个隐藏列 trx_id roll_pointer
读未提交
直接读取最新版本
序列化
加锁
Read View
读已提交
每次读取前生成一个
可重复度
第一次生成一个
锁
全局锁
全库逻辑备份
表锁
lock table read/write
MDL(metadata lock)
MySQL5.5引入 自动添加 读锁不互斥 写锁互斥
多个事务之前操作,如果查询的时候修改字段容易让线程池饱满
MySQL的information_schema 库的 innodb_trx 表 找到对应长事务 kill掉
alter table里面设定等待时间
Myisam是不支持表锁的
行锁
需要的时候才加上 并不是马上释放 等事务结束才释放 两阶段锁协议
死锁
超时时间
innodb_lock_wait_timeout
默认是50s太久 但是如果设置太短会误判 一般采用死锁监测
死锁机制 事务回滚
innodb_deadlock_detect = on
热点行
死锁消耗CPU
临时关闭
关掉死锁会出现大量重试
控制并发度
更多的机器 开启比较少的线程 消耗就少了
分治
间隙锁
读写锁
读
lock in share mode
for update
行锁
写
innodb如何加锁
Record lock: 对索引项加锁。
Gap lock:对索引项之间的‘间隙’,第一条记录前的间隙,或最后一条记录后的间隙 加锁
Next-Key:前两种的组合,对记录及前面的间隙加锁
Gap lock:对索引项之间的‘间隙’,第一条记录前的间隙,或最后一条记录后的间隙 加锁
Next-Key:前两种的组合,对记录及前面的间隙加锁
B+
log
undo log
回滚 mvcc
redo log
物理日志 内存操作记录
sync_binlog 可以优化日志写入时机
binlog
组提交机制,可以大幅度降低磁盘的IOPS消耗。
两段式提交 redo 准备 binglog 提交
count1 *
mvcc影响
主备延迟
强制走主
sleep
join
驱动表
id用完
bigint
row_id 没设置主键的时候
thread_id
mysql io性能瓶颈
设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
将sync_binlog 设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。
将sync_binlog 设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。
常见命令
show processlist
查看空闲忙碌链接
wait_timeout
客户端空闲时间
定时断开链接
mysql_reset_connection 恢复链接状态
innodb_flush_log_at_trx_commit
redolog事务持久化
sync_binlog
binlog事务持久化
真实故障
数据库挂了 show processlist 一千个查询在等待 有个超长sql kill 但是不会引起flush table 周末 优化脚本 analyze 会导致 MySQL 监测到对应的table 做了修改 必须flush close reopen 就不会释放表的占用了
Oracle
Zookeeper
选举机制
过半机制
预提交 ack 2pc
ZAB协议?
zk节点宕机如何处理?
如何实现分布式一致性
Dubbo
Netty
零拷贝
bio
阻塞IO 读写都阻塞
问题 带宽 资源等
每个请求过来 开一个线程阻塞
nio
不阻塞来着不拒
但是可能等待时间太久 响应延迟大了 太短了 会重试
IO多路复用
便捷的通知机制
程序注册一组socket文件描述符给操作系统,表示“我要监视这些fd是否有IO事件发生,有了就告诉程序处理”
select
遍历 判断事件是否可达 然后继续
poll
做了优化
epoll
有转态 会创建 文件描述符指向的表 监听增删改查
通道 buffer 多路复用
监听事件
aio
架构设计思路
执行链路
初始化channel
注册 channel到selector
任务队列
轮训accept事件 处理这些简历 channel的链接
注册 channel到selector 接收方
轮训写 事件 开线程去处理
任务队列
线程组
boss
监听端口所有准备就绪的时间
work
监听准备工作
调用链路
服务暴露过程
IOC 启动加载dubbo配置的标签
解析标签 ServiceBean 会生成一个Bean
实现了 initializingBean
afterpropertiesSet
get provider
set provider
各种信息 保存在 ServiceBean
IOC完成 还实现了一个 ApplicationListener监听器
回调onapplicationEvent
是否暴露 不是延迟暴露
暴露
信息校验 doexport
检查
doexportURL 暴露URL
加载注册中心信息
循环协议 端口
代理工厂获取invoke 封装
暴露invoke
根据spi来
本地暴露
打开 服务器 exchangeServer
启动服务器netty 监听端口
注册中心 注册服务
注册表
执行器
暴露 p 和 s 两个invoker的map保存了地址
spi
服务引用
factoryBean ->getObject ->get
init
信息检查
创建代理对象 createProxy
远程引用 获取到zk 获取到信息 订阅
dubbo 执行远程引用
创建 netty客户端
返回invoke
注册到注册表进去
成功
SPI
java spi
Java 没ioc aop
具体的spi kv形式
静态代理
容错机制
failover
直接切换
failfast
快速失败
failsafe
failback
forking cluster
broadcast cluster
整合 hystrix
失败回调
返回默认
降级
return null
失败返回空
负载均衡
随机 加权
轮训
最少活跃数
hash一致
选举算法
注册中心
协议
dubbo
默认 nio 单一长连接
二进制系列化 小数据量 100k
数据量中等 不适合文件传输
memcached
redis
webService
http
消息队列
RocketMQ
基础组成
NameServer
无状态模式
broker向发心跳顺便带上所有的Topic信息
早期是zk后来改了
Broker
中转消息,消息持久化
底层通信基于Netty
Producer
同步
异步
单向
Consumer
pull
push
支持集群模式
多master
多master多slave异步复制
多master多slave双写
消费保证
发送成功后返回consume_success
回溯消费
高可用
集群
NameService 集群
Broker 主从 双主 双从
Consumer 自动切换
producer 链接两个Broker
刷盘
同步 超时会返回错误
异步 不返回
消息的主从复制
同步复制
异步复制
主从同步 异步刷盘
顺序消息
Hash取模
RocketMQ提供了MessageQueueSelector队列选择机制
顺序发送 顺序消费由 消费者保证
消费者是多线程
消息去重
幂等
去重
消息表主键冲突
分布式事务
最大努力
消息表 不断轮训 人工干预
半消息
发送半消息 发送成功 本地事务 觉得是否提交还是回滚 服务端没收到回查 检查本地事务 根据本地事务决定 提交
2/3pc
最终一致
预发 持久化 返回状态 发送处理结果 判断是否干掉持久化的 发送
完整的一个调用链路
producer 和NameService 节点建立一个长连接
定期从NameService获取Topic信息
并且向Broker Master 建立链接 发送心跳
发送消息给 Broker Master
consumer 从 Mater 和 Slave 一起订阅消息
消息重试
顺序消息重试
不断重试 16次 4小时46分钟 可以修改尝试次数
对一个消费者设置 组内都会设置
可以获取消息重试次数
无序消息重试
死信队列
不再被正常消费
保存3天
面向消费者组
控制台 重发 重写消费者 单独消费
事务消息
消息丢失
消息积压
决定是否丢弃
判断吞吐量
停止消费 加机器 加topic
RabbitMQ
消息模型
简单模型;一个生产者,一个消费者
工作队列;一个生产者,多个消费者
订阅Fanout广播;一个生产者,一个交换机,多个队列
路由:Direct;直连交换机,多个生产者,一个队列接受
Direct:正则表达式去匹配routingKey
交换机
广播交换机
直连交换机Direct
消息可靠性
在项目使用RabbitMq时,一个订单付款成功,网Mq发送消息后,但是想要的状态未改变
未接受
投递失败
服务器宕机
怎么保证RabbitMq消息的可靠性
为了保证消息到达消息队列,在发送消息时设置Mandatory属性,如果mandatory不能正确的发送到消息队列中,将会出发return Method,正returnMethod中进行业务处理,如果消息不能正确发送到队列中,就会borke丢弃该消息
消费者的确认机制:生产者将Channel设置成Confirm模式,设置手动确认消息,如果第一次未收到消息,就会把消息重新发送回消息队列中,就会记录一次重投,如果接到消息已经重投了,还未接收到,就会丢弃该消息。
其他
消息幂等性
http幂等性:一次或者多次请求一个资源,有相同的副作用。
消息的幂等性和http幂等性相同,消费者在消费MQ时,MQ已经把消息发送给消费者了,消费者给MQ发送ack确认时网络中断,MQ认为未收到消息,该消息重新发送给消费者,但是消费者此时已经消费过了,造成消费者重复消费的问题。
所有的MQ的幂等性在于重试机制的问题,因为网络原因或者客户端延迟消费的问题
重复消费,如何保证消息幂等性
使用全局Messgeid确保消息时同一条消息,或者使用业务逻辑保证此消息时唯一的消息
插入操作前 先查询是否存在,存在则更新
如果是写redis,则不用处理,set天然幂等性
分布式锁:Redis token机制
每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证。如果验证通过删除token,下次请求再次判断token是否相等,如果不相等即重复提交。
每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证。如果验证通过删除token,下次请求再次判断token是否相等,如果不相等即重复提交。
比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
消息堆积原因
消费者延迟消费
生产者生产速率快,消费者消费时间过长。
解决消息堆积的问题
(1)解决问题一,要做好 灰度发布。每次新功能上线前,选取一定比例的消费实例做灰度,若出现问题,及时回滚;若消费者消费正常,平稳运行一段时间后,再升级其它实例。如果需要按规则选出一部分账号做灰度,则需要做好消息过滤,让正常消费实例排除灰度消息,让灰度消费实例过滤出灰度消息。
(2)解决问题二,要做到 多活。极端情况下,当一个IDC内消费实例全部宕机时,需要做到让其他IDC内的消费实例正常消费消息。同时,若一个IDC内Broker全部宕机,需要支持生产者将消息发送至其它IDC的Broker。
(3)解决问题三,要 增强消费能力。增强消费能力,主要是增加消费者线程数或增加消费者实例个数。增加消费者线程数要注意消费者及其下游服务的消费能力,上线前就要将线程池参数调至最优状态。增加消费者实例个数,要注意Queue数量,消费实例的数量要与Queue数量相同,如果消费实例数量超过Queue数量,多出的消费实例分不到Queue,只增加消费实例是没用的,如果消费实例数量比Queue数量少,每个消费实例承载的流量是不同的。
(4)解决问题四,要做到 熔断与隔离。当一个Broker的队列出现消息积压时,要对其熔断,将其隔离,将新消息发送至其它队列,过一定的时间,再解除其隔离。
(2)解决问题二,要做到 多活。极端情况下,当一个IDC内消费实例全部宕机时,需要做到让其他IDC内的消费实例正常消费消息。同时,若一个IDC内Broker全部宕机,需要支持生产者将消息发送至其它IDC的Broker。
(3)解决问题三,要 增强消费能力。增强消费能力,主要是增加消费者线程数或增加消费者实例个数。增加消费者线程数要注意消费者及其下游服务的消费能力,上线前就要将线程池参数调至最优状态。增加消费者实例个数,要注意Queue数量,消费实例的数量要与Queue数量相同,如果消费实例数量超过Queue数量,多出的消费实例分不到Queue,只增加消费实例是没用的,如果消费实例数量比Queue数量少,每个消费实例承载的流量是不同的。
(4)解决问题四,要做到 熔断与隔离。当一个Broker的队列出现消息积压时,要对其熔断,将其隔离,将新消息发送至其它队列,过一定的时间,再解除其隔离。
已出事故的解决措施
情况1:堆积的消息还需要使用
方案1:简单修复
修复consumer的问题,让他恢复消费速度,然后等待几个小时消费完毕
方案2:复杂修复
临时紧急扩容了,具体操作步骤和思路如下:
1)先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉
2)新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量
3)然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue
4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据
5)这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据
6)等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息
情况2:堆积的消息不需要使用
删除消息即可。
情况1:堆积的消息还需要使用
方案1:简单修复
修复consumer的问题,让他恢复消费速度,然后等待几个小时消费完毕
方案2:复杂修复
临时紧急扩容了,具体操作步骤和思路如下:
1)先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉
2)新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量
3)然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue
4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据
5)这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据
6)等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息
情况2:堆积的消息不需要使用
删除消息即可。
分布式锁
Zookeeper
死锁
羊群效应
临时节点顺序
性能没redis高
Redis
jedis.set(String key, String value, String nxxx, String expx, int time)
性能比较高
数据库
死锁
设置一个失效时间用定时任务去跑
数据库集群 主备同步
搞个死循环排队
可重入设计一个字段累加
排它锁
用数据库自身的锁就可以了 行锁 索引
select XX for update
记得 提交
宕机数据库也会自动释放锁
缺点
比其他的更消耗资源
复杂
特点
互斥
安全性
死锁
容错
算法
贪心
分治
动态规划
快排
堆排
二叉树
链表反转
成环
环节点
跳楼梯
扩展知识
内存屏障
指令乱序
分支预测
NUMA
CPU亲和性
遇到过的坑
sync遇到了bgsave
CPU彪升
bgsave之后会做一个emptyDB
这个时候做bgsave cow的机制就没了
会重新加载整个RDB
然后就swap
高并发场景下无限同步
tps过高的时候
master 复制缓存挤压区的时候 有个参数client-output-buffer-limit 默认1M
大量请求会让值升高 超出阈值断开
重连
slave做RDB同步的时候会导致TPS过高无法加载
阻塞久了复制缓冲区的数据就被冲掉了,是个队列会踢掉之前的数据
slave重连对比offset发现空档重新sync
预估体量参数动态调整
canal 并发修改
es 自动机构建
redis es 深分页
实在没有可以去网上找找 但是得记熟悉
有什么想问我的
我的回答有什么建议么
阿里的解决方案
团队主要做的事情
项目
一定要有核心亮点
并发 多线程
业务亮点
技术亮点
比如我自己之前做的一些中间件改造优化
大数据底层优化 融合改造
突出你在项目中做的事情和收获
可以是业务上的也可以技术上的
最好让对方看到你在团队视角的视野和格局,而不是局限于一个coder
一定要对你简历上的每一个细节都了如指掌
个人项目
全局报警
分布式事务组件
拦截spring事务
设计方案
高并发下单
订单生成
HR面
为什么换工作
想去XX
成长受限
薪资
个人原因
渴望大平台
职业规划
之前的公司你怎么评价
有什么想问我的么?
0 条评论
下一页