龙空技术网

斩获多家大厂 offer 的 Java 面试宝典,速速收藏(下篇)

你又不是蜗牛 87

前言:

今天各位老铁们对“java多叉树解析xml”大概比较关怀,兄弟们都想要知道一些“java多叉树解析xml”的相关文章。那么小编在网络上网罗了一些对于“java多叉树解析xml””的相关文章,希望各位老铁们能喜欢,我们快快来了解一下吧!

5. 设计模式

5.1 单例模式

5.2 工厂模式

5.3 构建者模式

5.4 代理模式

5.5 适配器模式

6. 数据结构和算法

6.1 数据结构

6.1.1 线性表

6.1.1.1数组

6.1.1.2链表

6.1.1.3 栈

6.1.1.4 队列

6.1.2 散列表

6.1.3 树

6.1.3.1 二叉树

6.1.3.2 满二叉树

6.1.3.3 完全二叉树

6.1.3.4 平衡二叉树

6.1.3.5 二叉查找树

6.1.3.6 红黑树

6.1.3.7 B 树

6.1.3.8 B+ 树

6.2 算法

6.2.1 排序

6.2.1.1 冒泡

6.2.1.2 快排

6.2.1.3 堆排序

7. 消息队列

7.1 消息队列的使用场景

7.2 RocketMQ 的角色

7.3 RocketMQ 的执行流程

7.4 RocketMQ 消息过滤

7.5 零拷贝

7.6 同步复制异步复制

7.7 刷盘机制

7.8 延时消息

7.9 事务消息

7.10 顺序消息

8. mysql

8.1 mysql 体系架构

8.2 mysql 运行机制

8.3 mysql 存储引擎InnoDB

8.3.1 内存结构

8.4 mysql 索引

8.4.1 普通索引

8.4.2 唯一索引

8.4.3 主键索引

8.4.4 复合索引

8.4.5 聚集索引

8.4.6 索引原理

8.4.7 like 查询

8.4.8 explain 有哪些字段

8.4.9 慢查询优化

8.5 mysql 锁

8.5.1 锁分类

8.5.2 悲观锁

8.5.3 乐观锁

8.5.4 死锁

8.6 mysql 事务

8.6.1 事务特性

8.6.2 事务隔离级别

8.6.3 MVCC

8.7 mysql 分库分表

8.7.1 主键策略

8.7.2 分片策略

8.8 mysql 主从同步

8.8.1 适用场景

8.8.2 主从同步作用

8.8.3 主从同步实现原理

9. redis

9.1 跳跃表

9.2 字典

9.3 压缩列表

9.4 快速列表

9.5 缓存过期和淘汰策略

9.5.1 过期删除策略

9.5.1.1 定时删除

9.5.1.2 惰性删除

9.5.1.3 主动删除(定期删除)

9.5.2 淘汰策略

9.5.2.1 LRU

9.5.2.2 随机

9.5.2.3 volatile-ttl

9.6 缓存穿透、缓存雪崩、缓存击穿

9.6.1 缓存穿透

9.6.2 缓存雪崩

9.6.3 缓存击穿

9.7 单线程的Redis 为什么这么快

9.8 redis 分布式锁实现

10. 中间件

10.1 zookeeper

10.1.1 zookeeper 数据结构

10.1.2 监听器

10.1.3 zookeeper 应用场景

10.1.4 ZAB 协议

10.2 dubbo

11. 分布式

11.1 分布式下读写一致性

11.2 单调一致性

11.3 CAP 理论

11.4 BASE 理论

11.5 2PC 和 3PC

11.6 paxos 一致性算法

11.7 Raft 一致性算法

12. mybatis

12.1 mybatis 初始化

12.2 sql 执行过程

12.3 mybatis 插件

5. 设计模式

5.1 单例模式

保证一个类仅有一个实例,并提供一个访问它的方法。包含一个静态变量,一个私有的构造方法,一个 public 的静态方法。

饿汉式、懒汉式、双重校验锁。

静态内部类。

5.2 工厂模式

工厂模式就是通过工厂来创建想要的对象,而不是自己去创建对象。这样降低了代码间的耦合度。比如一个服饰工厂可以生产衣服,裤子,鞋子,袜子,帽子等等。我们想要用衣服,不用自己来造了,而是和工厂说你想要什么,那工厂就给你生产什么。这就是工厂模式。主要是将创建对象的实例交给工厂类来完成,并进行管理。

工厂分为简单工厂模式、工厂方法模式和抽象工厂模式。

简单工厂模式:是工厂模式最简单的一种,实例有工厂直接创建。也就是上面的例子中,你想要衣服,那这个服饰工厂就给你做一件衣服给你。

工厂方法模式 :就是工厂本身不进行实体对象的创建,而是通过对应的下游工厂来创建然后在返回,有点总工厂子工厂的意思,总工厂管理着所有的子工厂,每个子工厂只生产指定的商品,当通知总部想要什么东西时,总部就通知对应的子工厂生产,拿到产品后再返回客户。

抽象工厂:就是一个抽象工厂类,里面只是声明可以实现哪些对象,但是具体的实现就交给具体的工厂完成。抽象工厂就好比包皮公司,它告诉你他可生产服饰,也就可以生产食品。那比如你想要衣服,它就给你一个服饰工厂的联系方式,你通过这个服饰工厂来获取到衣服。想要辣条,那他就给你推一个食品公司的联系方式,让这个食品公司给你做。

工厂方法模式和抽象工厂模式的区别:在于工厂方法模式主要是生产某一类商品。而抽象工厂,我不关心你怎么实现,只要你说你能做这个商品,我就可以为你代言。

5.3 构建者模式

将构建一个复杂的对象可以进行拆分成构建一个个简单的对象。然后组装成复杂的对象,就是构建者模式。比如mybatis 中为了构建Configuration 这个复杂对象,就先构建了Dadasource、MapperStment、等很多的对象组成,在解析SqlMapConfig配置文件的时候,就会先将各种配置文件封装到对应的对象中,最后组装成 Configuration 对象

5.4 代理模式

代理模式就是我们想要执行的方法通过代理对象来完成,就好比我们要抢票回家,我们不想自己盯着抢,所以就找代理帮我们抢从而达到目的。代理模式分为静态代理和动态代理。

静态代理:就是具体的代理类,在编译阶段就知道这个代理能做什么事情。就好比抢票的代理一样,它只能做抢票这个代理,而不能代你抢钱哈哈。

动态代理:动态代理就不同了,它在编译阶段也没有具体的实现。而是在程序运行期间根据JVM的反射机制动态生成的。比较出名的就JDK 动态代理 和cglib 动态代理。

JDK 动态代理:主要实现InvocationHandle 接口并实现其 invoke 方法。

cglib 动态代理:需要引入其依赖,使用方法和JDK动态代理差不多。实现MethodInterceptor 接口并实现 intercept 方法。

JDK 动态代理是java 语言自带的功能,提供了稳定的支持。是通过拦截器和反射实现的,jdk 动态代理只能代理继承接口的类。

cglib 动态代理是第三方提供的工具,基于ASM 实现的,性能比较高,cglib 动态代理无需通过接口实现,它是通过实现子类的方式来完成调用的。

5.5 适配器模式

6. 数据结构和算法

6.1 数据结构

常见的数据结构如下:

6.1.1 线性表

6.1.1.1数组

有限个相同类型的变量组成的集合。物理地址连续(连续的内存空间存储)。

删除和插入元素的时候,后续的元素需要进行位移。

扩容数组的时候,需要先创建一个新数据,然后将原数组的值填充到新数组中。

优点:具有高效的随机访问能力。只需要给出下标,就可以常量时间找到对应元素。

缺点:插入和删除,会导致其他元素发生位移。影响效率。

6.1.1.2链表

链表是物理上不连续的节点组成。每个节点包含两部分。存储数据元素以及指向下个节点的指针。

常见的链表有:单链表,双链表,循环链表。

优点:插入,删除、更新的效率高。

缺点:查询的效率低,不能随机访问。在查询节点的时候,只能从头结点开始一个个的往后找。

6.1.1.3 栈

栈是一种线性结构,栈中的元素只能先入后出。最早进入的元素为栈底,最后进入的元素叫栈顶。

栈可以用数组实现也可以用链表实现。

6.1.1.4 队列

队列也是一种线性结构,只能先入先出。队列的出口端叫做队头,队列的入口端加做队尾。

队列可以用数组实现也可以用链表实现。

6.1.2 散列表

散列表也是 hash 表。这种数据结构提供了key 和value 的映射关系。只要给出 key ,就可以高效的查询出它所匹配的 value 。

6.1.3 树

树又分为二叉树和多叉树。

6.1.3.1 二叉树

二叉树每个节点最多只有两个子节点。

6.1.3.2 满二叉树

一个二叉树所有的非叶子节点都存在左右子节点。且所有的叶子节点都在同一层,就是满二叉树。

6.1.3.3 完全二叉树

如果二叉树的深度为k。k-1 层为满二叉树。第k 层,所有节点都连续集中在最左边,则是一颗完全二叉树。

6.1.3.4 平衡二叉树

左子树和右子树的深度差的绝对值不超过1。并且左子树和右子树也为平衡二叉树。

6.1.3.5 二叉查找树

在二叉树的基础上。如果有左节点,左节点的的值都小于根节点。如果有右节点,右节点的值都大于根节点。二叉查找树又叫二叉排序树。

6.1.3.6 红黑树

一种自平衡的二叉查找树。有以下特征:

1、每个节点不是红色节点就是黑色节点。

2、根节点是黑色。

3、每个叶子节点都是黑色的空节点。

4、如果一个节点是红色的。那么它的子节点必须为黑色。

5、每个节点到各个叶子节点的包含相同数量的黑节点。

6、新加入的节点为红色,然后校验是否满足红黑树的规则,。如果不满足则需要进行颜色翻转、左旋、右旋等操作。

6.1.3.7 B 树

B 树为一种多叉树。一颗 M 阶的B 树 特征如下:

1、树的每个节点至多有M 个子节点。

2、根节点至少有两个子节点。

3、除根节点外,其他非叶子节点至少有 m/2 个子节点。

4、所有叶子节点都在同一层。

6.1.3.8 B+ 树

B+ 树是B树的变种。如下特征:

1、多路平衡树

2、只有叶子节点保存数据

3、搜索时 也相当于二分查找

4、增加了相邻叶子节点指针

6.2 算法

常见的算法如下:

6.2.1 排序

6.2.1.1 冒泡

相邻的两个元素比较,左边大于右边则进行交换。时间复杂度O(n^2)。是稳定排序

6.2.1.2 快排

快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。每次选择一个基准元素,将比基准元素大的元素放到右边,比基准小的元素放到左边。然后分别对左右进行快排。是不稳定的排序

6.2.1.3 堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。是不稳定的排序。

7. 消息队列

主流的消息队列有RabbitMQ、kafka、RocketMQ。三者各有优劣。RocketMQ 支撑高并发高可用的。

7.1 消息队列的使用场景

比如 创建订单减少库存、以及秒杀场景就可以使用消息队列。消息队列的使用,主要有三大优点:降低耦合、流量削峰填谷、异步。在一些不需要同步处理的场景,可以先把请求保存到消息队列中,直接返回结果给前端。提升响应速度。后续再进行消息的处理。如我们感知项目中,对没有接口日志都要进行日志入库的操作。我们之前是返回结果之前,将请求信息写入日志文件,后台线程定期执行入库。这样做将业务和日志入库进行了强耦合,进行了磁盘io ,接口的整体响应效率受到影响。后面引入 RocketMQ,接口再响应之前,向RocketMQ 中发送一条请求的消息后就返回给前端了。然后单独启用一个日志入库的服务消费RocketMQ 中的日志消息,进行日志入库。

7.2 RocketMQ 的角色

Producer:消息的发送者;Producer与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供Topic 服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

Consumer:消息接收者;Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,消费者在向Master拉取消息时,Master服务器会根据拉取偏移量与最大偏移量的距离(判断是否读老消息,产生读I/O),以及从服务器是否可读等因素建议下一次是从Master还 是Slave拉取。

Broker:暂存和传输消息;Broker分为Master与Slave,一个Master可以对应多个Slave,Master与Slave 的对应关系通过指定相同的BrokerName,不

同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。 注意:当前RocketMQ版本在部署架构上支持一Master多Slave,但只有BrokerId=1的从服务器才会参与消息的读负载。

NameServer:管理Broker;是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。

Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息

Message Queue:相当于是Topic的分区;用于并行发送和接收消息

7.3 RocketMQ 的执行流程

1、启动 NameServer,NameServer 起来后监听端口,等待 Broker、Producer、Consumer 连上来,相当于一个路由控制中心。

2、Broker 启动,跟所有的 NameServer 保持长连接,定时发送心跳包。心跳包中包含当前 Broker 信息(IP+端口等)以及存储所有 Topic 信息。注册成功后,NameServer 集群中就有 Topic 跟 Broker 的映射关系。

3、收发消息前,先创建 Topic,创建 Topic 时需要指定该 Topic 要存储在哪些 Broker上,也可以在发送消息时自动创建 Topic。

4、Producer 发送消息,启动时先跟 NameServer 集群中的其中一台建立长连接,并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker上,轮询从队列列表中选择一个队列,然后与队列所在的 Broker 建立长连接从而向 Broker 发消息。

5、Consumer 跟 Producer 类似,跟其中一台 NameServer 建立长连接,获取当前订阅 Topic 存在哪些 Broker 上,然后直接跟 Broker 建立连接通道,开始消费消息

7.4 RocketMQ 消息过滤

RocketMQ 的消息过滤主要是在 consumer 端订阅消息时再做消息过滤的。主要基于 Tag 进行过滤。consumer 在订阅消息时,除了指定 topic 还可以指定 tag 。根据 tag 来过滤消息。

7.5 零拷贝

所谓的零拷贝就是数据在项目的虚拟内存和服务器的物理内存之间没有发生数据拷贝。正常的数据和磁盘的交互流程是。数据首先在项目的虚拟内存中,然后拷贝到物理内存,最后拷贝到磁盘上。读取的话先从磁盘到物理内存,再到项目的虚拟内存。使用零拷贝技术可以减少拷贝的次数。内存中不发生数据拷贝,提升数据传输效率。

7.6 同步复制异步复制

如果一个broker 组中存在master 和slave .消息需要从 master 同步到 slave.有同步复制和异步复制两种方式。

同步复制:是等 master 和 slave 都写入成功后才返回客户端写成功状态。

异步复制:只等 master 写入成功就返回。

Master 角色的broker 支持读和写。slave 角色的broker 仅支持读。consumer 可以连接master 或者slave 进行消息的读取。

7.7 刷盘机制

RocketMQ 的所有消息都是持久化的,消息会先写入物理内存,然后刷盘保存到磁盘上。刷盘有同步刷盘和异步刷盘

同步刷盘:同步刷盘等待消息保存到了磁盘后才返回。

异步刷盘:消息保存到了物理内存就返回了。

7.8 延时消息

延时消息是指消息发送到 broker 后,不会立即被消费。而是等待特定的时间投递给真正的topic .一共有18个等级。可以通过messageDelayLevel 配置。最久延时2小时。

7.9 事务消息

RocketMQ 采用的是两阶段提交的方式实现事务消息。

流程如下:

1、发送方向RocketMQ发送“待确认”消息。

2、RocketMQ将收到的“待确认”消息持久化成功后,向发送方回复消息已经发送成功,此时第一阶段消息发送完成。

3、发送方开始执行本地事件逻辑。

4、发送方根据本地事件执行结果向 RocketMQ 发送二次确认(Commit或是Rollback)消息。RocketMQ收到Commit状态则将第一阶段消息标记为可投递,订阅方将能够收到该消息;收到 Rollback 状态则删除第一阶段的消息,订阅方接收不到该消息。

7.10 顺序消息

实现顺序消息的话,只需要生产者只往一个队列中发送消息,并且发送模式调整为同步发送。这样就可以保证消费中从这个队列中消费的消息都是有序的。

8. mysql

8.1 mysql 体系架构

mysql server 架构分为:网络连接层、服务层、存储引擎层、和系统文件层。

网络连接层:客户端连接器提供与 mysql 服务端连接的支持。java、c、typhon 通过各自的 api 与 mysql 进行连接。

服务层:系统管理和控制工具、连接池、SQL 接口、解析器、查询优化器、缓存。

8.2 mysql 运行机制

1、建立连接。mysql 客户端和服务端通信方式为半双工。也就是某一时刻,只能接受数据或者发送数据。

2、查询缓存。先在缓存中查询是否有完全一致的 sql ,如果存在就直接将结果返回。

3、解析器解析。解析器将 sql 进行语法解析,检测语法是否正确。

4、查询优化器根据解析树生成最优的执行计划。

5、查询存储引擎执行计划返回结果。

8.3 mysql 存储引擎InnoDB

InnoDB 支持事务、有行锁。支持多种索引。主要分为内存结构和磁盘结构两大部分。

8.3.1 内存结构

内存结构包含:buffer pool 、change buffer、log buffer、Adaptive Hash index 四部分。

1、buffer pool(缓冲池)。以page 为基本单位,在InnoDB 访问表和索引时会在 Page 页中缓存,从而下次查询的时候从 缓冲池中查找,从而减少 磁盘的IO ,提升效率。

2、change buffer(写缓存)。主要在进行增删改的操作是,如果 buffer pool 中没有对应的 Page 数据。并不会立刻从磁盘页中加载数据到缓存池,而是先在 change buffer 中记录下来,等下次数据读取时,再将数据合并到buffer pool 中。减少IO ,提升效率。

3、Adaptive Hash Index (自适应哈希索引),用户优化对缓存池的查询。

4、Log buffer(日志缓冲区)。用来保存要写入磁盘的 undo/redo 数据 。

8.4 mysql 索引

mysql 索引可以提升查询效率。有如下索引类型

从应用层次划分:普通索引、唯一索引、主键索引、复合索引

从数据存储和索引键值逻辑关系划分:聚集索引(聚簇索引)、非聚集索引(非聚簇索引)

8.4.1 普通索引

最普通的索引类型,基于普通字段简历的索引,没有任何限制。

8.4.2 唯一索引

和普通所以的区别,索引字段的值必须唯一,但是可以为空值。

8.4.3 主键索引

它是一种特殊的索引,不允许有空值,且字段值必须唯一。一张表只能有一个主键,也只能有一个主键索引。

8.4.4 复合索引

复合索引是对表的多个字段创建的联合索引。

如果表已经建立了(col1,col2),就没有必要再单独建立(col1);如果现在有(col1)索引,如果查询需要col1和col2条件,可以建立(col1,col2)复合索引,对于查询有一定提高。

8.4.5 聚集索引

B+树叶子节点存放的索引值和行记录就属性聚集索引,如果叶子节点只存储了索引值和主键 那就是非聚集索引。

简单说不需要回表查询的就是聚集索引,需要回表查询的就是非聚集索引。主键索引就是聚集索引。

8.4.6 索引原理

Mysql 索引采用的是B+ 树的数据结构。

1、非叶子节点不存储数据,值存储索引值。

2、叶子节点包含在同一层,包含所有的索引值和数据。

3、相邻的叶子节点通过指针连接,提高区间访问性能。

8.4.7 like 查询

MySQL 在使用 Like 模糊查询时,索引是可以被使用的,只有把%字符写在后面才会使用到索引。

8.4.8 explain 有哪些字段

1、select_type 表示查询类型。常用值有SIMPLE

2、type 表示存储引擎查询数据时采用的方式。ALL 全表查询、index 基于索引的全表扫描、range 范围查询、ref 索引查询。

3、possible_keys 表示查询时能够使用到的索引。

4、key:表示查询时真正使用到的索引,显示的是索引名称。

8.4.9 慢查询优化

先分析慢查询的原因:

1、全表扫描

2、全索引扫描

3、索引过滤性不好

4、频繁的回表查查询

如果没有使用索引就创建所以,如果索引效率不高,就优化索引,最好使用覆盖索引。避免回表查询。

8.5 mysql 锁

8.5.1 锁分类

在 MySQL中锁有很多不同的分类

从操作的粒度可分为表级锁、行级锁和页级锁。

表级锁:每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB 等存储引擎中。

行级锁:每次操作锁住一行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB 存储引擎中。

页级锁:每次锁定相邻的一组记录,锁定粒度界于表锁和行锁之间,开销和加锁时间界于表锁和行锁之间,并发度一般。应用在BDB 存储引擎中。

从操作的类型可分为读锁和写锁

读锁(S锁):共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响。

写锁(X锁):排他锁,当前写操作没有完成前,它会阻断其他写锁和读锁。

从操作的性能可分为乐观锁和悲观锁。

乐观锁:一般的实现方式是对记录数据版本进行比对,在数据更新提交的时候才会进行冲突检测,如果发现冲突了,则提示错误信息。

悲观锁:在对一条数据修改的时候,为了避免同时被其他人修改,在修改数据之前先锁定,再修改的控制方式。共享锁和排他锁是悲观锁的不同实现,但都属于悲观锁范畴。

8.5.2 悲观锁

悲观锁(Pessimistic Locking),是指在数据处理过程,将数据处于锁定状态,一般使用数据库的锁机制实现。从广义上来讲,前面提到的行锁、表锁、读锁、写锁、共享锁、排他锁等,这些都属于悲观锁范畴。

表锁:表级读锁会阻塞写操作,但是不会阻塞读操作。而写锁则会把读和写操作都阻塞。

共享锁:是行锁-读锁,事务使用了共享锁(读锁),只能读取,不能修改,修改操作被阻塞。

排它锁:是行锁-写锁。事务使用了排他锁(写锁),当前事务可以读取和修改,其他事务不能修改,也不能获取记录

8.5.3 乐观锁

乐观锁是相对于悲观锁而言的,它不是数据库提供的功能,需要开发者自己去实现。在数据库操作时,想法很乐观,认为这次的操作不会导致冲突,因此在数据库操作时并不做任何的特殊处理,即不加锁,而是在进行事务提交时再去判断是否有冲突了。

乐观锁实现的关键点:冲突的检测。

悲观锁和乐观锁都可以解决事务写写并发,在应用中可以根据并发处理能力选择区分,比如对并发率要求高的选择乐观锁;对于并发率要求低的可以选择悲观锁。

实现方式:添加版本和时间戳

8.5.4 死锁

死锁产生的四个必要条件:

互斥条件:一个资源同一时刻只能被一个进程占用。

不可剥夺:一个资源除非被占用的进程主动释放,否则不能被剥夺

请求保持:一个进行请求资源时,没有获取到则保持请求,直到获取资源

循环等待:进程a 等待进程b 持有的资源,进程b 等待进程a 持有的资源

破坏其中一个条件就可以避免死锁。

8.6 mysql 事务

8.6.1 事务特性

mysql 事务有4个特性,即所谓的 ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

原子性:事务是一个原子操作单元,对数据的修改,要么全都执行,要么全都不执行。

一致性:是指事务在开始前和事务结束后,数据库的完整性限制没有被破坏。

隔离性:是指一个事务的执行,不会被其他事务干扰,一个事务的内部操作及使用的数据对其他的并发事务是隔离的。

持久性:指事务一旦提交,它对数据库中的数据修改时永久性的,后续操作不会对其有任何影响,不会丢失。

8.6.2 事务隔离级别

数据库系统提供了以下 4 种事务隔离级别

读未提交:解决了回滚覆盖类型的更新丢失,但可能发生脏读现象(一个事务读取到了另一个事务修改但未提交的数据),也就是可能读取到其他会话中未提交事务修改的数据。

已提交读:只能读取到其他会话中已经提交的数据,解决了脏读。但可能发生不可重复读现象(一个事务中多次读取同一行记录不一致,后面读取的跟前面读取的不一致),也就是可能在一个事务中两次查询结果不一致。

可重复度:解决了不可重复读,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上会出现幻读(一个事务中多次按相同条件查询,结果不一致。后续查询的结果和面前查询结果不同,多了或少了几行记录),简单的说幻读指的的当用户读取某一范围的数据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据时会发现有新的幻影行。

可串行化:所有的增删改查串行执行。它通过强制事务排序,解决相互冲突,从而解决幻度的问题。这个级别可能导致大量的超时现象的和锁竞争,效率低下

8.6.3 MVCC

多版本控制,支持读读、读写、写读并行。但为了保证一致性,写写是无法并行的。

在事务1开始写操作的时候会copy一个记录的副本,其他事务读操作会读取这个记录副本,因此不会影响其他事务对此记录的读取,实现写和读并行。

8.7 mysql 分库分表

使用分库分表时,主要有垂直拆分和水平拆分两种拆分模式,都属于物理空间的拆分。

垂直拆分:由于表数量多导致的单个库大。将表拆分到多个库中。

水平拆分:由于表记录多导致的单个库大。将表记录拆分到多个表中。

8.7.1 主键策略

1、uuid

2、雪花片算法。ID 按照时间有序生成。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0

3、redis 生成id.利用 redis 的原子操作 INCR 来实现。

8.7.2 分片策略

1、范围分片

2、hash 取模分片

3、一致性hash 分片

8.8 mysql 主从同步

8.8.1 适用场景

MySQL 主从模式是指数据可以从一个 MySQL 数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,从节点可以复制主数据库中的所有数据库,或者特定的数据库,或者特定的表。

8.8.2 主从同步作用

1、实时灾备,用于故障切换

2、读写分离,提供查询服务

3、数据备份,避免影响业务(高可用)

8.8.3 主从同步实现原理

主要分为三个步骤

1、主库将数据库的变更记录到Binlog 日志文件中

2、从库读取主库的 Binlog 日志文件信息写入到从库的 Ready log 日志中。

3、从库读取 Ready Log 日志信息在从库中 Replay ,更新从库信息

异步复制:master 不会等待slave 节点返回,直接提交事务,会导致数据不一致。

半同步复制:master 会等待slave 节点的ack 消息后才会进行事务提交。

9. redis

9.1 跳跃表

跳跃表是有序集合的底层实现。就是将有序集合的部分节点进行分层。每一层都是有序集合,并且层次越高,节点数量就越少。最底层的包含所有节点数据。典型的空间换时间。

9.2 字典

字典是 redis 用来存储键值对的一种数据结构。

hash 函数: 把redis 的key(字符串、整型、浮点型)统一转换成统一长度的整数。

hash 冲突:采用单链表解决。

字典扩容:新申请的容量为原来的一倍,将老的数据迁移到新字典中需要rehash。比较缓慢。

9.3 压缩列表

由一系列特殊编码的连续内存块组成的顺序型数据结构。没有压缩列表包头部、entry数据和尾部。头部信息包含压缩列表的字节长度、压缩列表尾部元素的偏移量、压缩列表元素个数;尾部为一个字节,为结束标志。

entry 数据包含:前一个元素的字节长度、当前元素的编码、以及当前元素的数据内容。

9.4 快速列表

quicklist 是列表的底层实现。quicklist 是一个双向链表,同时每个节点是一个压缩列表。这样quicklist 的每个节点都可以存储多个元素。

9.5 缓存过期和淘汰策略

9.5.1 过期删除策略

redis 淘汰策略有三种。定时删除、惰性删除和主动删除。

Redis 目前采用惰性删除和主动删除。

9.5.1.1 定时删除

在设置键的过期时间时,创建一个定时器,让定时器在该键过期时,执行对键的删除操作。(需要创建定时器,消耗cpu)

9.5.1.2 惰性删除

在访问 key 的时候发现它失效了,就删除这个key

9.5.1.3 主动删除(定期删除)

每隔一段时间,我们就对一些key 进行检查,删除过期的 key

9.5.2 淘汰策略

9.5.2.1 LRU

LRU (Least recently used )最近最少使用。基于链表实现,每次新数据插入到链表头部,如果链表中的节点有被用到,就将该节点移到链表头部。这样链表尾部的元素就是最近最少使用的了。删除的 时候从尾部开始删除。

volatile-lru:从已经设置过期的数据集中挑选最近最少使用的进行数据淘汰。

allkeys-lru:从所有数据集中挑选最近最少使用的数据进行淘汰。

9.5.2.2 随机

从数据集中任意选择一个数据淘汰

9.5.2.3 volatile-ttl

从已经设置过期时间的数据集中挑选将要过期的数据进行淘汰。从过期时间表中随机挑选几个键值对,取出其中ttl 最小的键值对进行淘汰。

9.6 缓存穿透、缓存雪崩、缓存击穿

9.6.1 缓存穿透

缓存穿透是指在高并发下查询 key 不存在数据,会穿透缓存查询数据库,导致数据库压力过大而宕机。

解决方案:

一:设置一个短暂的随机延时,避免同一时刻所有的请求都穿过缓存增大数据的压力。这样先从数据库查询的数据查询出后,添加到缓存中,其他的请求就不会从数据库中请求了。

二:设置布隆过滤器,在缓存之前再加一个层布隆过滤器,在查询的时候先去布隆过滤器中查询key 是否存在,如果不存在就直接返回,如果存在再查询缓存和DB.

布隆过滤器的原理是一组hash函数。当一个元素被加入集合中时,通过k个 hash 函数将这个元素映射成一个数据中的k个点,把他们置为1。检索是,我们只要看看这些点是不是1就知道集合中存不存在这个元素了。只要有一个点为0,则被检测的元素一定不存在集合中。

9.6.2 缓存雪崩

大量缓存集中在某一时间段内失效,这样在失效的时候,大量请求达到数据库,造成数据库崩溃。

解决方案:

1、key 的失效期分散开,不同的key 设置不同的有效期。

2、设置二级缓存

3、搭建集群。单节点故障后,由从节点补上。

9.6.3 缓存击穿

主要针对热点数据,当key 在某一时间点过期的时候,正好有大量的并发请求访问这个key。从而请求都达到数据库,加大的数据库压力。

解决方案:

1、针对热点 key.不设置超时时间。定期的充数据库同步到redis 中。

2、用分布式锁控制访问的线程。使用redis 的setnx互斥锁进行判断,只有获取锁的线程才能访问数据库,其他的线程进行等待。

9.7 单线程的Redis 为什么这么快

1、正常情况下都是内存操作,不会和磁盘交换,从而访问速度更快。

2、单线程没有锁,没有多线程的切换和调度,不会死锁,没有性能损耗。

3、采用I/O 多路复用模型,非阻塞io 。效率更高

4、数据结构简单,压缩处理。

9.8 redis 分布式锁实现

加锁:使用setnx 加锁,如果执行成功,然后设置过期时间。

解锁:删除加锁的key

10. 中间件

10.1 zookeeper

10.1.1 zookeeper 数据结构

zookeeper 将所有的数据存储在内存中,数据模型是一个Znode tree。用斜杠分割路径,每个znode 上还保留自己的数据内容,同时保留一系列的属性信息。

可以根据需求创建如下四种类型的节点:

持久节点:在节点创建后,会一直存在服务器上,直到删除操作主动删除。

持久顺序节点:和持久节点一样,创建后就一直存在,并且多了一个特性,在创建节点的时候,会加上一个数字后缀,来表示顺序。

临时节点:它的生命周期和客户端会话绑定在一起,客户端会话结束,节点就会被删除掉。如持续节点不同的是,临时节点不能创建子节点。

临时顺序节点:有顺序的临时节点,相对于临时节点而言,在创建的时候,会加一个数字后缀,表示顺序。

10.1.2 监听器

Watcher(事件监听器)是zookeeper 中一个重要的特性,zookeeper 允许用户在指定的节点上注册一些watcher。并在一些特定的事件触发的时候,zookeeper 服务端会将事件通知到感兴趣的客户端。

10.1.3 zookeeper 应用场景

1、数据发布订阅

2、集群管理

3、分布式锁。利用zookeeper 可以实现分布式锁的排它锁和共享锁。

排他锁:利用zk 的强一致性,以及临时节点的特性。首先定义一二锁节点。然后多线程在锁节点下创建一个临时节点,哪个节点创建成功了,就说明该线程获取到了分布式锁,没有获取到锁的线程,就注册一个所节点的子节点监听事件,以便监听到锁节点的变化情况。如果一个线程执行完成或者执行异常,都会关闭连接,表示释放了锁,这样其他节点就可以通过监听事前来重新获取锁了。

共享锁:一样的是利用zk 节点的强一致性。不过创建的是顺序的临时节点。分别表示读锁和写锁。对于读请求,如果没有比自己小的节点或者所有比自己小的节点都是读请求,那么表明自己已经成功的获取到了共享锁,开始执行读取逻辑。对于写请求,如果自己不是序号最小的节点,就需要等待。

4、分布式队列

10.1.4 ZAB 协议

在 zookeeper 中主要是利用原子广播协议来保证数据一致性的。

ZAB 协议的核心是定义了对于那些会改变 Zookeeper 服务器数据状态的事务请求的处理方式

即:所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,余下的服务器则称为Follower服务器,Leader服务器负责将一个客户端事务请求转化成一个事务Proposal(提议),并将该Proposa1分发给集群中所有的Follower服务器,之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。

10.2 dubbo

在集群负载均衡时,dubbo 提供了多种均衡策略。包含随机、轮询、最小活跃调用数、一致性hash。 默认为随机调用。

11. 分布式

11.1 分布式下读写一致性

用户读取自己结果的一致性。保证用户永远能够第一时间看到自己更新的内容。

如:我们发朋友圈,别人能不能第一时间看到没关系,但是需要我们自己第一时间就能看到,这个应该怎么实现?

解决方案:

1、我们设置一个更新时间窗口,在刚刚更新的这段时间内,我们默认都从主库读取,过了这个窗口后,挑选最近有从主库更新的从库读取。

2、我们直接记录用户操作的时间戳。在请求的时候,直接把这个时间戳带上,凡是最后更新时间小于这个时间戳的分库就不予以响应。

11.2 单调一致性

本次读取到的数据不能比上次读取到的数据旧。

解决方案:

根据用户id 计算hash 值,映射到对应的服务器上,这样同一个用户不管怎样刷新,都会被映射到同一台机器,保证不会出现数据变旧的情况。

11.3 CAP 理论

一个分布式系统不能同时满足一致性、可用性、分区容错性这三个基本需求,只能满足其中两个。

11.4 BASE 理论

BASE 由三个单词组成。基本可用(Basically Available)、软状态(Soft state)、最终一致性(Eventually consistent)。核心是想是:即使无法做到强一致性,但是每个应用都可以根据自己的特点,采用适当的方式来使得系统达到最终一致性。

11.5 2PC 和 3PC

两阶段提交协议:分为准备阶段和提交阶段。在准备阶段,事务管理器会给每个参入者发送prepare 消息,每个参数者在自己本地执行事务,并写入undo/redo 日志(undo 是修改前的日志,用于事务回滚,redo是修改后的日志,用来数据提交),这个时候事务还没有提交。在提交阶段,如果事务管理器收到了参入者执行失败或者超时的消息时,会发送回滚的消息给所有参入者进行回滚。如果都成功了,就发送提交的消息给所有参入者进行数据提交操作。

三阶段提交协议:是将2PC 的提交阶段一分为二。precommit 阶段发送预提交请求,各个参入者向协调者反馈执行结果。docommit 阶段执行事务提交。

11.6 paxos 一致性算法

paxos 算法核心就是提案。

1、proposer 选择一个提案为N ,向半数以上的Acceptor 发送编号为N 的 prepare 请求。

2、如果 Acceptor 收到一个编号为N的 prepare请求,并且N为当前Acceptor 所响应的请求中编号最大的,它就接收这个提案,并且不再接收比这个编号更小的提案了。

3、如果proposer 收到了半数以上Acceptor 对其发出的编号为N 的prepare请求响应。就会给半数以上的 Acceptor 发送提案的accept 请求。

4、如果acceptor 收到一个编号为N的accept 请求,如果当前acceptor 没有对编号大于N 的prepare 请求做出响应,它就接收该提案。

11.7 Raft 一致性算法

Raft 一致性算法分为两个阶段:首先是选举阶段,然后通过选举出来的领导人带领进行正常操作。

选举的过程:

1、集群中所有的节点开始的时候都是 follower 跟随者。当发现没有领导者的时候,

2、部分的跟随者会变成候选者,候选者会向跟随者发起投票请求。如果当前候选者获得了超过半数的选票,就自动变成了领导者。

3、如果集群中存在领导者,其他的候选者也会变成跟随者,所有的跟随者都会从领导者中进行数据同步,保证数据一致性。

12. mybatis

12.1 mybatis 初始化

1、通过jdk 文件流加载器读取xml配置文件。

2、通过XmlConfigBuilder 来解析配置文件,并封装到configuration 对象中。

3、创建SqlSessionFactory 对象,并传递configuration

4、通过SqlSessionFactory.openSqlSession() 来创建sqlSession 对象并返回。

12.2 sql 执行过程

1、通过调用Sqlsession 对象的query()或者update() 方法。

2、通过statmentid 从configuration 中获取到对应的mapperstatement

3、将configuration 和mapperstatement 对象传递给Executor 执行器来执行sql 操作。

4、Executor 执行器用来处理缓存,以及动态sql 的生成,并将sql 传递给statementhandle

5、statementHandle 将调用parameterHandle 进行参数的解析

6、statementHandle 执行sql 得到结果。

7、statementHandle 通过ResultSetHandle 将返回的结果进行对象映射,最终返回结果。

12.3 mybatis 插件

mybatis 作为应用广泛的优秀的ORM开源框架,具有强大的扩展性,在mybatis 中的 Executor、StatementHandle、ParameterHandle、ResultSet 这四大组件提供了简易的拓展支持。mybatis 支持插件对上面这四大对象进行拦截,从而实现前置增强和后置增强。

自定义插件只需要实现mybatis 的interceptor 接口,并通过注册来声明拦截的类和方法。

如果本文对你有帮助,请点个转发和关注支持一下!

标签: #java多叉树解析xml #java 多叉树实现