- JUC 线程安全的应用
- JVM调优 oom排查方法
- kafka调优
- 项目的介绍
redis
- string、list、set、zset、hash
- 发布订阅模式用来处理消息通知,单机可以用enventBus
- 过期实现:1.随机取数据判断是否过期;2. 用户查询判断是否过期
- 内存策略:LRU 算法 6种 1. 对设置过期的数据集、全部数据集,采取剔除最长时间未使用、随机选取 这两种;2. 即将要过期的 3.从不删除
- 集群:多节点+主从结构+读写分离+心跳机制(超一半则dead)+数据分区(hashslot)
- 分区 hashslot就是总共有9000多slot,平均分到各个节点,扩容需重新计算和数据复制;
- 分区查询:查询到任意节点,计算出指定节点,重定向
- 单线程:内存(Hash 复杂度O(1)),避免上下文切换,基于epoll的io多路复用
mysql
- 索引失效:
全值匹配我最爱,最左前缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分写最右,覆盖索引不写星
不等空值还有or,索引失效要少用
- 索引类型:
- 存储: 聚集 非聚集 : b+tree的叶子节点=整行数据--》聚集,叶子节点=主键id--》非聚集;
- 逻辑: 主键、单列、联合、unique、fullText
- 数据结构:hash、b+Tree
- 主从复制,1. master开启binlog,2. slave IO线程读到relayLog,3. sql线程读取relayLog
- hash索引:等值+不支持排序+不支持范围查询+不支持联合索引的最左前缀+hash冲突
- b-tree 的结构就是为了降低对磁盘的IO次数;结构大概是一个m阶的bTree,每个节点都有m个关键字和 bTree
2.4 B TREE和B+TREE区别是什么?
B+Tree 关键字的搜索采用的是左闭合区间,之所以采用左闭合区间是因为他要最好的去支持自增id,这也是mysql的设计初衷。即,如果id = 1命中,会继续往下查找,直到找到叶子节点中的1。
B+Tree 根节点和支节点没有数据区,关键字对应的数据只保存在叶子节点中。即只有叶子节点中的关键字数据区才会保存真正的数据内容或者是内容的地址。而在B树种,如果根节点命中,则会直接返回数据。
在B+Tree中,叶子节点不会去保存子节点的引用。
B+Tree叶子节点是顺序排列的,并且相邻的节点具有顺序引用的关系,如上图中叶子节点之间有指针相连接。
2.5 MySQL为什么最终要去选择B+Tree?
B+Tree是B TREE的变种,B TREE能解决的问题,B+TREE也能够解决(降低树的高度,增大节点存储数据量)
B+Tree扫库和扫表能力更强。如果我们要根据索引去进行数据表的扫描,对B TREE进行扫描,需要把整棵树遍历一遍,而B+TREE只需要遍历他的所有叶子节点即可(叶子节点之间有引用)。
B+TREE磁盘读写能力更强。他的根节点和支节点不保存数据区,所以根节点和支节点同样大小的情况下,保存的关键字要比B TREE要多。而叶子节点不保存子节点引用,能用于保存更多的关键字和数据。所以,B+TREE读写一次磁盘加载的关键字比B TREE更多。
B+Tree排序能力更强。上面的图中可以看出,B+Tree天然具有排序功能。
B+Tree查询性能稳定。B+Tree数据只保存在叶子节点,每次查询数据,查询IO次数一定是稳定的。当然这个每个人的理解都不同,因为在B TREE如果根节点命中直接返回,确实效率更高。
JUC
- synchronized 锁升级 偏向锁(可重入)-> 轻量级锁(另外一个线程用自旋的方式获取锁) -> 重量级锁 (自旋次数超过限制)
- synchronized lock区别:1. lock只能放在代码块;2. lock更灵活,且可以主动中断;3. lock可以是公平锁;
- synchronized 是通过获取对象监视器monitor来实现锁功能的,而获取monitor是排他的; 锁加载方法上和代码块上是不同的实现通过javap 反编译class文件可以看出来,在方法上是通过获取ACC_SYNCHRONIZED标识来实现的锁;代码块上是听过monitorEnter和monitorExit来实现的锁;原理解析
对象在堆内存中存储模型:
1. 对象头
* markword - 运行时数据:hashCode、锁状态、锁的线程id、重入次数、分代标识、gc次数等;
* 元数据指针 - 指明当前对象是哪个class的对象
2. 实例
3. 填充数据
- notify、notifyAll是依赖monitor对象,所以必须要在synchronzied下面才能执行;如果有多个线程wait在一个对象上调用notify,jvm会随机notify一个线程;
- 线程池的配置: IO密集型 一般为cpu*2;其他 cpu+1;
- volatile 内存可见、禁止指令重排序、不保证原子性---单例模式
- CAS:atomInteger.getAndIncrement 通过volatile保证值的内存可见、通过Unsafe类来实现内存修改的原子性;ABA问题需要通过添加版本号的方式完善;
- 线程池 状态
running ---shutdown --》 shutdown (执行当前及队列中任务,不接受任务)
->> tidying ->> terminate
running ---shutdownNow --》 stop (全都不执行)
- 线程状态
- init
- runnable-》running tread执行start之后变成runnable等待cpu调度变成running
- blocking - 获取锁
- waiting - ojb.wait()
- timeWaiting - thread.sleep obj.wait(1)
- stop
- thread.interrupte 中断线程只是更改的线程的状态,真正中断需要线程自行判断结束;一些线程方法如果主动抛出interrupte异常则代表他是有对中断状态做自旋判断的,如:join、sleep、wait等;而且在死锁的时候是中断不了的;IO也可能会一直block如果想中断,实现了InterruptibleChannel的接口可以调用close接口中断;
classLoader
- 主要有四个方法findLoadClass、findClass、defineClass、loadClass
public Class loadClass(String name, boolean reslove) {
//1. 检查是否被加载过了
Class clazz = findLoadedClass(name);
if (clazz != null) {
return clazz;
}
//2. 双亲委托 略
//3. findClas 并且执行defineClass把byte[]转换为class对象
return findClass(name);
}
- 可以在loadClass中破坏双亲委托;
- 类的加载分为三步:加载(加载字节码到内存)、link(校验等操作)、init(初始化)
- 初始化也有顺序:父类静态(变量\代码块)-》子类静态-》父类变量初始化-》子类变量初始化-》父类构造-》子类构造;
engine 插件加载
需要支持类型不同、版本不同的数据源来进行执行任务操作; 为了避免直接引入各种类型、各种版本数据源插件,造成版本冲突。使用以下方法解决:
- 通过定义模版接口,定义插件需要做的各种功能;然后加载jar的时候通过SPI的方式获取指定实现;
- 通过自定义classLoader,实现loadClass方法,破坏双亲委托,保证当前jar的类加载器与众不同;因为判断两个实例相等的首要条件就是类加载器相同;
- 通过JDBC驱动加载深刻理解线程上下文类加载器机制
jdbc启动
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection(jdbc, u, p);
- class.forName手动加载driver类,是为了在加载类时执行静态代码块中的注册操作,将当前driver注册到DriverManager的driver列表中去;
- DriverManager.getConnection()获取连接时,会遍历所有注册的driver列表,
- isDriverAllowed 判断这个driver是不是由TCCL加载的。判断的方法是看使用TCCL初始化的实例和注册列表中的实例是否相等;经过此方法过后使用不同类加载器加载的Driver不会冲突;
- getConnection driver实现类中的的方法会对jdbcUrl的格式判断,保证不同类型数据源不会被加载错;
- 新版本的java做了优化,不需要再手动加载driver类。而是在DriverManager初始化的静态代码块中添加了扫描当前jar包driver类的操作。 实现方式是spi。如果是想要加载不同jar下的driver类还是要手动加载类。
分布式事务
TCC
- try - confirm - cancel
- 也是通过两阶段提交协议发挥作用的,try-先确保资源足够执行,锁定资源;如果有后面系列执行成功或者失败,则继续执行confirm和cancel操作;TCC例子,扣款、转账、冻结、解冻等
- 注意问题:
- 幂等性 多次访问要保证只执行一次
- 允许空回滚,有可能try丢包导致cancel空转;
- 防悬挂控制,由于网络问题 try、cancel顺序倒转; 这些问题都需要保留事务id和事务状态来保证事务执行的顺序问题;
- 优化
- try- confirm可以异步执行,先锁定资源,在异步提交或回滚
FMT框架
框架解析sql,保存业务sql修改前的快照和修改后的快照,而提交和回滚通过框架来执行; 需要保证无脏读