2024.11.04 周一
本周周一没有课,庆祝一下上午考科目三一把过,下午一直在睡觉…晚上稍微学一会
八股
TCP 的三次握手与四次挥手是什么?
- TCP的头部格式:
- 序列号
- 在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
- 确认应答号
- 指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
- 控制位
ACK
(Acknowledge character):该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。RST
(Reset the connection):该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。SYN
(Synchronize Sequence Numbers):该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。FIN
(Finish):该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
- 序列号
- TCP连接:用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。
- Socket:由 IP 地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
- 三次握手的过程(建立TCP连接的过程):
- 一开始,客户端和服务端都处于
CLOSE
状态。先是服务端主动监听某个端口,处于LISTEN
状态 - 客户端会随机初始化序号(
client_isn
),将此序号置于 TCP 首部的「序号」字段中,同时把SYN
标志位置为 1,表示SYN
报文。接着把第一个SYN
报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT
状态。
(三次握手的第一个报文:SYN报文)
- 服务端收到客户端的
SYN
报文后,首先服务端也随机初始化自己的序号(server_isn
),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入client_isn + 1
, 接着把SYN
和ACK
标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于SYN-RCVD
状态。
(三次握手的第二个报文:SYN+ACK报文)
- 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部
ACK
标志位置为 1 ,其次「确认应答号」字段填入server_isn + 1
,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于ESTABLISHED
状态。
(三次握手的第三个报文:ACK报文)
- 一开始,客户端和服务端都处于
- 四次挥手的过程(断开TCP连接的过程):
- 客户端打算关闭连接,此时会发送一个 TCP 首部
FIN
标志位被置为 1 的报文,也即FIN
报文,之后客户端进入FIN_WAIT_1
状态。
服务端收到该报文后,就向客户端发送ACK
应答报文,接着服务端进入CLOSE_WAIT
状态。 - 客户端收到服务端的
ACK
应答报文后,之后进入FIN_WAIT_2
状态。 - 等待服务端处理完数据后,也向客户端发送
FIN
报文,之后服务端进入LAST_ACK
状态。 - 客户端收到服务端的
FIN
报文后,回一个ACK
应答报文,之后进入TIME_WAIT
状态 - 服务端收到了
ACK
应答报文后,就进入了CLOSE
状态,至此服务端已经完成连接的关闭。 - 客户端在经过
2MSL
一段时间后,自动进入CLOSE
状态,至此客户端也完成连接的关闭。
- 客户端打算关闭连接,此时会发送一个 TCP 首部
为什么MySQL采用B+树作为索引?
MySQL索引结构与存储引擎
MySQL 是会将数据持久化在硬盘,而存储功能是由 MySQL 存储引擎实现的,所以讨论 MySQL 使用哪种数据结构作为索引,实际上是在讨论存储引擎使用哪种数据结构作为索引,InnoDB 是 MySQL 默认的存储引擎,它就是采用了 B+ 树作为索引的数据结构。
要设计一个 MySQL 的索引数据结构,不仅仅考虑数据结构增删改的时间复杂度,更重要的是要考虑磁盘 I/0 的操作次数。因为索引和记录都是存放在硬盘,硬盘是一个非常慢的存储设备,我们在查询数据的时候,最好能在尽可能少的磁盘 I/0 的操作次数内完成。
为什么不用二分查找树(BST)?
二分查找树虽然是一个天然的二分结构,能很好的利用二分查找快速定位数据,但是它存在一种极端的情况,每当插入的元素都是树内最大的元素,就会导致二分查找树退化成一个链表,此时查询复杂度就会从 O(logn)降低为 O(n)。
为什么不用平衡二叉树?
为了解决二分查找树退化成链表的问题,就出现了自平衡二叉树,保证了查询操作的时间复杂度就会一直维持在 O(logn) 。但是它本质上还是一个二叉树,每个节点只能有 2 个子节点,随着元素的增多,树的高度会越来越高。
而树的高度决定于磁盘 I/O 操作的次数,因为树是存储在磁盘中的,访问每个节点,都对应一次磁盘 I/O 操作,也就是说树的高度就等于每次查询数据时磁盘 IO 操作的次数,所以树的高度越高,就会影响查询性能。
为什么不用B树?
B 树和 B+ 都是通过多叉树的方式,会将树的高度变矮,所以这两个数据结构非常适合检索存于磁盘中的数据。
但是 MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有:
- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。
- B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化;
- B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。
算法
108.将有序数组转换为二叉搜索树(平衡二叉树)(中序遍历-左根右)
94.二叉树的中序遍历(递归)
98.验证二叉搜索树(每一层递归都用到上界和下界的参数)
230.二叉搜索树中第k小的元素(中序遍历+栈后进先出)
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
- Deque: 双端队列 (Double Ended Queue) 是一种可以在两端进行添加和删除操作的数据结构。
- ArrayDeque: ArrayDeque 是 Java 中 Deque 接口的一个实现,它使用数组来存储元素,并提供 O(1) 时间复杂度的添加和删除操作。
二叉搜索树(BST)
二叉查找树(BST, Binary Search Tree)的特点是一个节点的左子树的所有节点都小于这个节点,右子树的所有节点都大于这个节点。
平衡二叉树
平衡二叉树(Balanced BST)是一种特殊的二叉搜索树,每个节点的左子树和右子树的高度差不能超过 1。
二叉搜索树中第k小的元素
class Solution {
public int kthSmallest(TreeNode root, int k) {
// 通过Deque接口定义一个栈存放TreeNode
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
// 当前节点不为空 或 栈不为空时
while (root != null || !stack.isEmpty()){
// 当前节点不为空时
while (root != null){
stack.push(root); // 将该节点入栈
root = root.left; // 节点变为下一个较小的节点
}
// 上面的循环结束后,从根节点到值最小的节点已全部入栈
root = stack.pop();
--k;
// 检查是否为第k小的元素
if (k == 0){
break;
}
// 按二叉搜索树的大小顺序检查树
root = root.right;
}
return root.val;
}
}
项目
暂时还没开始写接口,搭建完了开发环境,给定的资料中前后端联调成功。
序号 | 名称 | 说明 |
---|---|---|
1 | sky-take-out | maven父工程,统一管理依赖版本,聚合其他子模块 |
2 | sky-common | 子模块,存放公共类,例如:工具类、常量类、异常类等 |
3 | sky-pojo | 子模块,存放实体类、VO、DTO等 |
4 | sky-server | 子模块,后端服务,存放配置文件、Controller、Service、Mapper等 |
名称 | 说明 |
---|---|
constant | 存放相关常量类 |
context | 存放上下文类 |
enumeration | 项目的枚举类存储 |
exception | 存放自定义异常类 |
json | 处理json转换的类 |
properties | 存放SpringBoot相关的配置属性类 |
result | 返回结果类的封装 |
utils | 常用工具类 |
名称 | 说明 |
---|---|
Entity | 实体,通常和数据库中的表对应 |
DTO | 数据传输对象,通常用于程序中各层之间传递数据 |
VO | 视图对象,为前端展示数据提供的对象 |
POJO | 普通Java对象,只有属性和对应的getter和setter |
名称 | 说明 |
---|---|
config | 存放配置类 |
controller | 存放controller类 |
interceptor | 存放拦截器类 |
mapper | 存放mapper接口 |
service | 存放service类 |
SkyApplication | 启动类 |