From 7b00fb2dbe43a6aa90453025ee0293876bbffcd2 Mon Sep 17 00:00:00 2001 From: wtt Date: Mon, 28 Aug 2023 20:57:20 +0800 Subject: [PATCH 01/78] update master_slave_replication.md --- redis/cluster/master_slave_replication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/cluster/master_slave_replication.md b/redis/cluster/master_slave_replication.md index 991e4940..1ab7c53b 100644 --- a/redis/cluster/master_slave_replication.md +++ b/redis/cluster/master_slave_replication.md @@ -316,7 +316,7 @@ Redis 配置里有一个参数 min-slaves-max-lag,表示一旦所有的从节 如果主节点的网络突然发生了问题,它与所有的从节点都失联了,但是此时的主节点和客户端的网络是正常的,这个客户端并不知道 Redis 内部已经出现了问题,还在照样的向这个失联的主节点写数据(过程 A),此时这些数据被主节点缓存到了缓冲区里,因为主从节点之间的网络问题,这些数据都是无法同步给从节点的。 -这时,哨兵也发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在从节点中选举出一个 leeder 作为主节点,这时集群就有两个主节点了 —— **脑裂出现了**。 +这时,哨兵也发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在从节点中选举出一个 leader 作为主节点,这时集群就有两个主节点了 —— **脑裂出现了**。 这时候网络突然好了,哨兵因为之前已经选举出一个新主节点了,它就会把旧主节点降级为从节点(A),然后从节点(A)会向新主节点请求数据同步,**因为第一次同步是全量同步的方式,此时的从节点(A)会清空掉自己本地的数据,然后再做全量同步。所以,之前客户端在过程 A 写入的数据就会丢失了,也就是集群产生脑裂数据丢失的问题**。 From ff38e0fa463c050bc468a8347e03f8740d56be31 Mon Sep 17 00:00:00 2001 From: chiperman Date: Tue, 29 Aug 2023 17:22:52 +0800 Subject: [PATCH 02/78] Update how_select.md --- mysql/base/how_select.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/base/how_select.md b/mysql/base/how_select.md index 93dc8265..bf97dffc 100644 --- a/mysql/base/how_select.md +++ b/mysql/base/how_select.md @@ -317,7 +317,7 @@ select * from t_user where age > 20 and reward = 100000; 可以看到,使用了索引下推后,虽然 reward 列无法使用到联合索引,但是因为它包含在联合索引(age,reward)里,所以直接在存储引擎过滤出满足 reward = 100000 的记录后,才去执行回表操作获取整个记录。相比于没有使用索引下推,节省了很多回表操作。 -当你发现执行计划里的 Extr 部分显示了“Using index condition”,说明使用了索引下推。 +当你发现执行计划里的 Extra 部分显示了“Using index condition”,说明使用了索引下推。 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/mysql/sql执行过程/索引下推执行计划.png) From 0483e818c49ec464abc4ac5d3a499c74ae85d69d Mon Sep 17 00:00:00 2001 From: wtt Date: Tue, 5 Sep 2023 16:40:15 +0800 Subject: [PATCH 03/78] update zero_copy.md --- os/8_network_system/zero_copy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/8_network_system/zero_copy.md b/os/8_network_system/zero_copy.md index 9ad1ba2b..e3c51f60 100644 --- a/os/8_network_system/zero_copy.md +++ b/os/8_network_system/zero_copy.md @@ -68,7 +68,7 @@ write(socket, tmp_buf, len); 首先,期间共**发生了 4 次用户态与内核态的上下文切换**,因为发生了两次系统调用,一次是 `read()` ,一次是 `write()`,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。 -上下文切换到成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。 +上下文切换的成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。 其次,还**发生了 4 次数据拷贝**,其中两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝的,下面说一下这个过程: @@ -351,4 +351,4 @@ Kafka 和 Nginx 都有实现零拷贝技术,这将大大提高文件传输的 另外,当传输大文件时,不能使用零拷贝,因为可能由于 PageCache 被大文件占据,而导致「热点」小文件无法利用到 PageCache,并且大文件的缓存命中率不高,这时就需要使用「异步 IO + 直接 IO」的方式。 -在 Nginx 里,可以通过配置,设定一个文件大小阈值,针对大文件使用异步 IO 和直接 IO,而对小文件使用零拷贝。 \ No newline at end of file +在 Nginx 里,可以通过配置,设定一个文件大小阈值,针对大文件使用异步 IO 和直接 IO,而对小文件使用零拷贝。 From 038ee4dadb66ee49d849cb2baf13c3ae471d52c7 Mon Sep 17 00:00:00 2001 From: ilvsx Date: Sun, 24 Sep 2023 19:02:42 +0800 Subject: [PATCH 04/78] Update how_cpu_run.md --- os/1_hardware/how_cpu_run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/1_hardware/how_cpu_run.md b/os/1_hardware/how_cpu_run.md index 8376fd38..4b9e49b0 100644 --- a/os/1_hardware/how_cpu_run.md +++ b/os/1_hardware/how_cpu_run.md @@ -318,7 +318,7 @@ CPU 的硬件参数都会有 `GHz` 这个参数,比如一个 1 GHz 的 CPU, - *指令数*,表示执行程序所需要多少条指令,以及哪些指令。这个层面是基本靠编译器来优化,毕竟同样的代码,在不同的编译器,编译出来的计算机指令会有各种不同的表示方式。 - *每条指令的平均时钟周期数 CPI*,表示一条指令需要多少个时钟周期数,现代大多数 CPU 通过流水线技术(Pipeline),让一条指令需要的 CPU 时钟周期数尽可能的少; -- *时钟周期时间*,表示计算机主频,取决于计算机硬件。有的 CPU 支持超频技术,打开了超频意味着把 CPU 内部的时钟给调快了,于是 CPU 工作速度就变快了,但是也是有代价的,CPU 跑的越快,散热的压力就会越大,CPU 会很容易奔溃。 +- *时钟周期时间*,表示计算机主频,取决于计算机硬件。有的 CPU 支持超频技术,打开了超频意味着把 CPU 内部的时钟给调快了,于是 CPU 工作速度就变快了,但是也是有代价的,CPU 跑的越快,散热的压力就会越大,CPU 会很容易崩溃。 很多厂商为了跑分而跑分,基本都是在这三个方面入手的哦,特别是超频这一块。 From 9edddf543c2a5ecef2df03e0f4d090d6d19c0d92 Mon Sep 17 00:00:00 2001 From: Coco <1461349565@qq.com> Date: Mon, 16 Oct 2023 10:04:17 +0800 Subject: [PATCH 05/78] Update cache_problem.md fixed a typo. --- redis/cluster/cache_problem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/cluster/cache_problem.md b/redis/cluster/cache_problem.md index 8f40ba7f..1d361b47 100644 --- a/redis/cluster/cache_problem.md +++ b/redis/cluster/cache_problem.md @@ -139,7 +139,7 @@ 第一种方案,非法请求的限制 -当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。 +当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断出请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。 第二种方案,缓存空值或者默认值 From 818593e246efe24218058cfc5613f393c340912f Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:21:58 +0800 Subject: [PATCH 06/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index c1d49ebb..851f7060 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -14,7 +14,7 @@ 要想客户端和服务器能在网络中通信,那必须得使用 Socket 编程,它是进程间通信里比较特别的方式,特别之处在于它是可以跨主机间通信。 -Socket 的中文名叫作插口,咋一看还挺迷惑的。事实上,双方要进行网络通信前,各自得创建一个 Socket,这相当于客户端和服务器都开了一个“口子”,双方读取和发送数据的时候,都通过这个“口子”。这样一看,是不是觉得很像弄了一根网线,一头插在客户端,一头插在服务端,然后进行通信。 +Socket 的中文名叫作插口,乍一看还挺迷惑的。事实上,双方要进行网络通信前,各自得创建一个 Socket,这相当于客户端和服务器都开了一个“口子”,双方读取和发送数据的时候,都通过这个“口子”。这样一看,是不是觉得很像弄了一根网线,一头插在客户端,一头插在服务端,然后进行通信。 创建 Socket 的时候,可以指定网络层使用的是 IPv4 还是 IPv6,传输层使用的是 TCP 还是 UDP。 @@ -270,4 +270,4 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll ***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** -![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) \ No newline at end of file +![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From 97d47abda61dc0d3ed33d85bd2dcb16e89a92e74 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:38:03 +0800 Subject: [PATCH 07/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index c1d49ebb..dc8c6cbc 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -27,7 +27,7 @@ UDP 的 Socket 编程相对简单些,这里我们只介绍基于 TCP 的 Socke - 绑定端口的目的:当内核收到 TCP 报文,通过 TCP 头里面的端口号,来找到我们的应用程序,然后把数据传递给我们。 - 绑定 IP 地址的目的:一台机器是可以有多个网卡的,每个网卡都有对应的 IP 地址,当绑定一个网卡时,内核在收到该网卡上的包,才会发给我们; -绑定完 IP 地址和端口后,就可以调用 `listen()` 函数进行监听,此时对应 TCP 状态图中的 `listen`,如果我们要判定服务器中一个网络程序有没有启动,可以通过 `netstat` 命令查看对应的端口号是否有被监听。 +绑定完 IP 地址和端口后,就可以调用 `listen()` 函数进行监听,此时对应 TCP 状态图中的 `listen`,如果我们要判断服务器中一个网络程序有没有启动,可以通过 `netstat` 命令查看对应的端口号是否有被监听。 服务端进入了监听状态后,通过调用 `accept()` 函数,来从内核获取客户端的连接,如果没有客户端连接,则会阻塞等待客户端连接的到来。 @@ -144,7 +144,7 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 当服务器与客户端 TCP 完成连接后,通过 `pthread_create()` 函数创建线程,然后将「已连接 Socket」的文件描述符传递给线程函数,接着在线程里和客户端进行通信,从而达到并发处理的目的。 -如果每来一个连接就创建一个线程,线程运行完后,还得操作系统还得销毁线程,虽说线程切换的上写文开销不大,但是如果频繁创建和销毁线程,系统开销也是不小的。 +如果每来一个连接就创建一个线程,线程运行完后,还得操作系统还得销毁线程,虽说线程切换的上下文开销不大,但是如果频繁创建和销毁线程,系统开销也是不小的。 那么,我们可以使用**线程池**的方式来避免线程的频繁创建和销毁,所谓的线程池,就是提前创建若干个线程,这样当由新连接建立时,将这个已连接的 Socket 放入到一个队列里,然后线程池里的线程负责从队列中取出已连接 Socket 进程处理。 @@ -270,4 +270,4 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll ***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** -![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) \ No newline at end of file +![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From eb7cd6b3f070dd68245e719f09482fbfc98d15c2 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:40:06 +0800 Subject: [PATCH 08/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index c1d49ebb..e264cba0 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -163,7 +163,7 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost4@main/操作系统/多路复用/多路复用.png) -一个进程虽然任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。 +一个进程虽然任意时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。 我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,**进程可以通过一个系统调用函数从内核中获取多个事件**。 @@ -270,4 +270,4 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll ***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** -![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) \ No newline at end of file +![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From b284a5dfc17f6b2beb972d6beb8177b4b6d9611e Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:42:29 +0800 Subject: [PATCH 09/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index c1d49ebb..e99cccca 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -177,7 +177,7 @@ select/poll/epoll 这是三个多路复用接口,都能实现 C10K 吗?接 select 实现多路复用的方式是,将已连接的 Socket 都放到一个**文件描述符集合**,然后调用 `select()` 函数将文件描述符集合**拷贝**到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过**遍历**文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写,接着再把整个文件描述符集合**拷贝**回用户态里,然后用户态还需要再通过**遍历**的方法找到可读或可写的 Socket,然后再对其处理。 -所以,对于 select 这种方式,需要进行 **2 次「遍历」文件描述符集合**,一次是在内核态里,一个次是在用户态里,而且还会发生 **2 次「拷贝」文件描述符集合**,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。 +所以,对于 select 这种方式,需要进行 **2 次「遍历」文件描述符集合**,一次是在内核态里,一次是在用户态里,而且还会发生 **2 次「拷贝」文件描述符集合**,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。 select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制,默认最大值为 `1024`,只能监听 0~1023 的文件描述符。 @@ -270,4 +270,4 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll ***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** -![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) \ No newline at end of file +![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From 7f3b5ee7d8ee5d3f4519a1a7821d447ba4e3c6b2 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:55:42 +0800 Subject: [PATCH 10/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index c1d49ebb..d0b061bf 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -192,7 +192,7 @@ poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动 epoll 通过两个方面,很好解决了 select/poll 的问题。 -*第一点*,epoll 在内核里使用**红黑树来跟踪进程所有待检测的文件描述字**,把需要监控的 socket 通过 `epoll_ctl()` 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删查一般时间复杂度是 `O(logn)`,通过对这棵黑红树进行操作,这样就不需要像 select/poll 每次操作时都传入整个 socket 集合,只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。 +*第一点*,epoll 在内核里使用**红黑树来跟踪进程所有待检测的文件描述字**,把需要监控的 socket 通过 `epoll_ctl()` 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删查一般时间复杂度是 `O(logn)`,通过对这棵红黑树进行操作,这样就不需要像 select/poll 每次操作时都传入整个 socket 集合,只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。 *第二点*,epoll 使用事件驱动的机制,内核里**维护了一个链表来记录就绪事件**,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 `epoll_wait()` 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。 @@ -270,4 +270,4 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll ***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** -![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) \ No newline at end of file +![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From a77792ee34661db6e524a61432c4ac07b412f16c Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:50:31 +0800 Subject: [PATCH 11/78] Update redis_interview.md --- redis/base/redis_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/base/redis_interview.md b/redis/base/redis_interview.md index ea8a557a..02bded39 100644 --- a/redis/base/redis_interview.md +++ b/redis/base/redis_interview.md @@ -23,7 +23,7 @@ Redis 是一种基于内存的数据库,对数据的读写操作都是在内 Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、List (列表)、Set(集合)、Zset(有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流),并且对数据类型的操作都是**原子性**的,因为执行命令由单线程负责的,不存在并发竞争的问题。 -除此之外,Redis 还支持**事务、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布/订阅模式,内存淘汰机制、过期删除机制**等等。 +除此之外,Redis 还支持**事务、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片集群模式)、发布/订阅模式,内存淘汰机制、过期删除机制**等等。 ### Redis 和 Memcached 有什么区别? 很多人都说用 Redis 作为缓存,但是 Memcached 也是基于内存的数据库,为什么不选择它作为缓存呢?要解答这个问题,我们就要弄清楚 Redis 和 Memcached 的区别。 From c8a165976c6c36a4254f95a0ee5f697c8c0e97fc Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Thu, 19 Oct 2023 21:04:42 +0800 Subject: [PATCH 12/78] Update command.md --- redis/data_struct/command.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index a21a373f..5c852880 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -42,7 +42,7 @@ SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/数据类型/int.png) -如果字符串对象保存的是一个字符串,并且这个字符申的长度小于等于 32 字节(redis 2.+版本),那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码设置为`embstr`, `embstr`编码是专门用于保存短字符串的一种优化编码方式: +如果字符串对象保存的是一个字符串,并且这个字符串的长度小于等于 32 字节(redis 2.+版本),那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码设置为`embstr`, `embstr`编码是专门用于保存短字符串的一种优化编码方式: ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/数据类型/embstr.png) @@ -1425,4 +1425,4 @@ Redis 后续版本又支持四种数据类型,它们的应用场景如下: 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From 0dda492e3199e6bfee8971c27b05797ed8616858 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:43:47 +0800 Subject: [PATCH 13/78] Update command.md --- redis/data_struct/command.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index a21a373f..2153fe8a 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -498,7 +498,7 @@ SDIFFSTORE destination key [key ...] 集合的主要几个特性,无序、不可重复、支持并交差等操作。 -因此 Set 类型比较适合用来数据去重和保障数据的唯一性,还可以用来统计多个集合的交集、错集和并集等,当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。 +因此 Set 类型比较适合用来数据去重和保障数据的唯一性,还可以用来统计多个集合的交集、差集和并集等,当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。 但是要提醒你一下,这里有一个潜在的风险。**Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞**。 @@ -1425,4 +1425,4 @@ Redis 后续版本又支持四种数据类型,它们的应用场景如下: 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From 5a5a8a519a71b072d9210469811e4767fce26554 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:39:19 +0800 Subject: [PATCH 14/78] Update rdb.md --- redis/storage/rdb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/storage/rdb.md b/redis/storage/rdb.md index 2ddf2d35..4c88a766 100644 --- a/redis/storage/rdb.md +++ b/redis/storage/rdb.md @@ -102,7 +102,7 @@ save 60 10000 尽管 RDB 比 AOF 的数据恢复速度快,但是快照的频率不好把握: -- 如果频率太低,两次快照间一旦服务器发生宕机,就可能会比较多的数据丢失; +- 如果频率太低,两次快照间一旦服务器发生宕机,就可能会丢失比较多的数据; - 如果频率太高,频繁写入磁盘和创建子进程会带来额外的性能开销。 那有没有什么方法不仅有 RDB 恢复速度快的优点和,又有 AOF 丢失数据少的优点呢? From 01099e4673efc36b2f9f3a3f5718c713508314a3 Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:11:03 +0800 Subject: [PATCH 15/78] Update cache_problem.md --- redis/cluster/cache_problem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/cluster/cache_problem.md b/redis/cluster/cache_problem.md index 1d361b47..859a8c67 100644 --- a/redis/cluster/cache_problem.md +++ b/redis/cluster/cache_problem.md @@ -2,7 +2,7 @@ 用户的数据一般都是存储于数据库,数据库的数据是落在磁盘上的,磁盘的读写速度可以说是计算机里最慢的硬件了。 -当用户的请求,都访问数据库的话,请求数量一上来,数据库很容易就奔溃的了,所以为了避免用户直接访问数据库,会用 Redis 作为缓存层。 +当用户的请求,都访问数据库的话,请求数量一上来,数据库很容易就崩溃的了,所以为了避免用户直接访问数据库,会用 Redis 作为缓存层。 因为 Redis 是内存数据库,我们可以将数据库的数据缓存在 Redis 里,相当于数据缓存在内存,内存的读写速度比硬盘快好几个数量级,这样大大提高了系统性能。 From 3832d939e3b67949d327d8361d7d8b143079323b Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:18:08 +0800 Subject: [PATCH 16/78] Update cache_problem.md --- redis/cluster/cache_problem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/cluster/cache_problem.md b/redis/cluster/cache_problem.md index 1d361b47..3fe181c2 100644 --- a/redis/cluster/cache_problem.md +++ b/redis/cluster/cache_problem.md @@ -89,7 +89,7 @@ 因为 Redis 故障宕机而导致缓存雪崩问题时,我们可以启动**服务熔断**机制,**暂停业务应用对缓存服务的访问,直接返回错误**,不用再继续访问数据库,从而降低对数据库的访问压力,保证数据库系统的正常运行,然后等到 Redis 恢复正常后,再允许业务应用访问缓存服务。 -服务熔断机制是保护数据库的正常允许,但是暂停了业务应用访问缓存服系统,全部业务都无法正常工作 +服务熔断机制是保护数据库的正常运行,但是暂停了业务应用访问缓存服系统,全部业务都无法正常工作 为了减少对业务的影响,我们可以启用**请求限流**机制,**只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务**,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。 From 75e7d2957f14d84d75f33bba3916892080dfe54a Mon Sep 17 00:00:00 2001 From: Bin <49082129+songzhibin97@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:09:22 +0800 Subject: [PATCH 17/78] Update why_index_chose_bpuls_tree.md --- mysql/index/why_index_chose_bpuls_tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/index/why_index_chose_bpuls_tree.md b/mysql/index/why_index_chose_bpuls_tree.md index c0dfeded..8695a6f4 100644 --- a/mysql/index/why_index_chose_bpuls_tree.md +++ b/mysql/index/why_index_chose_bpuls_tree.md @@ -241,7 +241,7 @@ Innodb 根据索引类型不同,分为聚簇和二级索引。他们区别在 ## 总结 -MySQL 是会将数据持久化在硬盘,而存储功能是由 MySQL 存储引擎实现的,所以讨论 MySQL 使用哪种数据结构作为索引,实际上是在讨论存储引使用哪种数据结构作为索引,InnoDB 是 MySQL 默认的存储引擎,它就是采用了 B+ 树作为索引的数据结构。 +MySQL 是会将数据持久化在硬盘,而存储功能是由 MySQL 存储引擎实现的,所以讨论 MySQL 使用哪种数据结构作为索引,实际上是在讨论存储引擎使用哪种数据结构作为索引,InnoDB 是 MySQL 默认的存储引擎,它就是采用了 B+ 树作为索引的数据结构。 要设计一个 MySQL 的索引数据结构,不仅仅考虑数据结构增删改的时间复杂度,更重要的是要考虑磁盘 I/0 的操作次数。因为索引和记录都是存放在硬盘,硬盘是一个非常慢的存储设备,我们在查询数据的时候,最好能在尽可能少的磁盘 I/0 的操作次数内完成。 From 8f78170c869fbf8c474aabd22081027d6f76d662 Mon Sep 17 00:00:00 2001 From: gamble369 <64143142+gamble369@users.noreply.github.com> Date: Sun, 12 Nov 2023 15:48:23 +0800 Subject: [PATCH 18/78] Update http_interview.md Fix typos --- network/2_http/http_interview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/2_http/http_interview.md b/network/2_http/http_interview.md index 58242eaa..4e79bd17 100644 --- a/network/2_http/http_interview.md +++ b/network/2_http/http_interview.md @@ -265,9 +265,9 @@ POST 的语义是根据请求负荷(报文主体)对指定的资源做出处 曾经有个笑话,有人写了个博客,删除博客用的是 GET 请求,他觉得没人访问就连鉴权都没做。然后 Google 服务器爬虫爬了一遍,他所有博文就没了。。。 -如果「安全」放入概念是指信息是否会被泄漏的话,虽然 POST 用 body 传输数据,而 GET 用 URL 传输,这样数据会在浏览器地址拦容易看到,但是并不能说 GET 不如 POST 安全的。 +如果「安全」放入概念是指信息是否会被泄漏的话,虽然 POST 用 body 传输数据,而 GET 用 URL 传输,这样数据会在浏览器地址栏容易看到,但是并不能说 GET 不如 POST 安全的。 -因为 HTTP 传输的内容都是明文的,虽然在浏览器地址拦看不到 POST 提交的 body 数据,但是只要抓个包就都能看到了。 +因为 HTTP 传输的内容都是明文的,虽然在浏览器地址栏看不到 POST 提交的 body 数据,但是只要抓个包就都能看到了。 所以,要避免传输过程中数据被窃取,就要使用 HTTPS 协议,这样所有 HTTP 的数据都会被加密传输。 From 4bc7eac9f9d66a2ce758c18b72756f2141f90f10 Mon Sep 17 00:00:00 2001 From: gamble369 <64143142+gamble369@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:07:39 +0800 Subject: [PATCH 19/78] Update tcp_interview.md --- network/3_tcp/tcp_interview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/3_tcp/tcp_interview.md b/network/3_tcp/tcp_interview.md index 36a00c64..485b3d75 100644 --- a/network/3_tcp/tcp_interview.md +++ b/network/3_tcp/tcp_interview.md @@ -224,7 +224,7 @@ TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而** ![第一个报文 —— SYN 报文](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL3hpYW9saW5jb2Rlci9JbWFnZUhvc3QyLyVFOCVBRSVBMSVFNyVBRSU5NyVFNiU5QyVCQSVFNyVCRCU5MSVFNyVCQiU5Qy9UQ1AtJUU0JUI4JTg5JUU2JUFDJUExJUU2JThGJUExJUU2JTg5JThCJUU1JTkyJThDJUU1JTlCJTlCJUU2JUFDJUExJUU2JThDJUE1JUU2JTg5JThCLzE1LmpwZw?x-oss-process=image/format,png) -- 客户端会随机初始化序号(`client_isn`),将此序号置于 TCP 首部的「序号」字段中,同时把 `SYN` 标志位置为 `1`,表示 `SYN` 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 `SYN-SENT` 状态。 +- 客户端会随机初始化序列号(`client_isn`),将此序号置于 TCP 首部的「序号」字段中,同时把 `SYN` 标志位置为 `1`,表示 `SYN` 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 `SYN-SENT` 状态。 ![第二个报文 —— SYN + ACK 报文](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL3hpYW9saW5jb2Rlci9JbWFnZUhvc3QyLyVFOCVBRSVBMSVFNyVBRSU5NyVFNiU5QyVCQSVFNyVCRCU5MSVFNyVCQiU5Qy9UQ1AtJUU0JUI4JTg5JUU2JUFDJUExJUU2JThGJUExJUU2JTg5JThCJUU1JTkyJThDJUU1JTlCJTlCJUU2JUFDJUExJUU2JThDJUE1JUU2JTg5JThCLzE2LmpwZw?x-oss-process=image/format,png) @@ -1167,4 +1167,4 @@ accpet 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP **小林是专为大家图解的工具人,Goodbye,我们下次见!** -![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost2/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost2/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From badc0c410cd83c1675979e1bf30e394adda08de3 Mon Sep 17 00:00:00 2001 From: gamble369 <64143142+gamble369@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:11:59 +0800 Subject: [PATCH 20/78] Update tcp_interview.md --- network/3_tcp/tcp_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/tcp_interview.md b/network/3_tcp/tcp_interview.md index 485b3d75..532059f4 100644 --- a/network/3_tcp/tcp_interview.md +++ b/network/3_tcp/tcp_interview.md @@ -224,7 +224,7 @@ TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而** ![第一个报文 —— SYN 报文](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL3hpYW9saW5jb2Rlci9JbWFnZUhvc3QyLyVFOCVBRSVBMSVFNyVBRSU5NyVFNiU5QyVCQSVFNyVCRCU5MSVFNyVCQiU5Qy9UQ1AtJUU0JUI4JTg5JUU2JUFDJUExJUU2JThGJUExJUU2JTg5JThCJUU1JTkyJThDJUU1JTlCJTlCJUU2JUFDJUExJUU2JThDJUE1JUU2JTg5JThCLzE1LmpwZw?x-oss-process=image/format,png) -- 客户端会随机初始化序列号(`client_isn`),将此序号置于 TCP 首部的「序号」字段中,同时把 `SYN` 标志位置为 `1`,表示 `SYN` 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 `SYN-SENT` 状态。 +- 客户端会随机初始化序号(`client_isn`),将此序号置于 TCP 首部的「序号」字段中,同时把 `SYN` 标志位置为 `1`,表示 `SYN` 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 `SYN-SENT` 状态。 ![第二个报文 —— SYN + ACK 报文](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL3hpYW9saW5jb2Rlci9JbWFnZUhvc3QyLyVFOCVBRSVBMSVFNyVBRSU5NyVFNiU5QyVCQSVFNyVCRCU5MSVFNyVCQiU5Qy9UQ1AtJUU0JUI4JTg5JUU2JUFDJUExJUU2JThGJUExJUU2JTg5JThCJUU1JTkyJThDJUU1JTlCJTlCJUU2JUFDJUExJUU2JThDJUE1JUU2JTg5JThCLzE2LmpwZw?x-oss-process=image/format,png) From 423bd7a2cb58b54948f6cac6561a07c32e22c406 Mon Sep 17 00:00:00 2001 From: pengzhisheng Date: Fri, 17 Nov 2023 17:36:10 +0800 Subject: [PATCH 21/78] fix typos --- mysql/lock/mysql_lock.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/lock/mysql_lock.md b/mysql/lock/mysql_lock.md index 9b7b725b..57623fcd 100644 --- a/mysql/lock/mysql_lock.md +++ b/mysql/lock/mysql_lock.md @@ -117,7 +117,7 @@ unlock tables 再来说说**元数据锁**(MDL)。 -我们不需要显示的使用 MDL,因为当我们对数据库表进行操作时,会自动给这个表加上 MDL: +我们不需要显式的使用 MDL,因为当我们对数据库表进行操作时,会自动给这个表加上 MDL: - 对一张表进行 CRUD 操作时,加的是 **MDL 读锁**; - 对一张表做结构变更操作的时候,加的是 **MDL 写锁**; From ad44e48377a2f25c44a255e0836f6fd12c342790 Mon Sep 17 00:00:00 2001 From: pengzhisheng Date: Fri, 17 Nov 2023 17:51:57 +0800 Subject: [PATCH 22/78] =?UTF-8?q?=E4=BD=BF=E8=AF=AD=E5=8F=A5=E6=9B=B4?= =?UTF-8?q?=E9=80=9A=E9=A1=BA=E8=87=AA=E7=84=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/lock/mysql_lock.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/lock/mysql_lock.md b/mysql/lock/mysql_lock.md index c429f2cc..70b42c4f 100644 --- a/mysql/lock/mysql_lock.md +++ b/mysql/lock/mysql_lock.md @@ -138,9 +138,9 @@ MDL 是在事务提交后才会释放,这意味着**事务执行期间,MDL 2. 然后,线程 B 也执行了同样的 select 语句,此时并不会阻塞,因为「读读」并不冲突; 3. 接着,线程 C 修改了表字段,此时由于线程 A 的事务并没有提交,也就是 MDL 读锁还在占用着,这时线程 C 就无法申请到 MDL 写锁,就会被阻塞, -那么在线程 C 阻塞后,后续有对该表的 select 语句,就都会被阻塞,如果此时有大量该表的 select 语句的请求到来,就会有大量的线程被阻塞住,这时数据库的线程很快就会爆满了。 +那么在线程 C 阻塞后,后续有其他线程对该表的 select 语句,就都会被阻塞。如果此时有大量该对表的 select 语句的请求到来,就会有大量的线程被阻塞住,这时数据库的线程很快就会爆满了。 -> 为什么线程 C 因为申请不到 MDL 写锁,而导致后续的申请读锁的查询操作也会被阻塞? +> 为什么因为线程 C 申请不到 MDL 写锁,会导致后续线程申请读锁的查询操作也会被阻塞? 这是因为申请 MDL 锁的操作会形成一个队列,队列中**写锁获取优先级高于读锁**,一旦出现 MDL 写锁等待,会阻塞后续该表的所有 CRUD 操作。 From 996790ec29638f2bd82889ddfec054a30aa07cd3 Mon Sep 17 00:00:00 2001 From: pengzhisheng Date: Fri, 17 Nov 2023 18:16:59 +0800 Subject: [PATCH 23/78] =?UTF-8?q?=E4=BD=BF=E8=AF=AD=E5=8F=A5=E6=9B=B4?= =?UTF-8?q?=E9=80=9A=E9=A1=BA=E8=87=AA=E7=84=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/lock/mysql_lock.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/lock/mysql_lock.md b/mysql/lock/mysql_lock.md index 70b42c4f..65585447 100644 --- a/mysql/lock/mysql_lock.md +++ b/mysql/lock/mysql_lock.md @@ -185,9 +185,9 @@ select ... for update; AUTO-INC 锁是特殊的表锁机制,锁**不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放**。 -**在插入数据时,会加一个表级别的 AUTO-INC 锁**,然后为被 `AUTO_INCREMENT` 修饰的字段赋值递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。 +**在插入数据时,MySQL 会加一个表级别的 AUTO-INC 锁**,然后会为被 `AUTO_INCREMENT` 修饰的字段赋递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。 -那么,一个事务在持有 AUTO-INC 锁的过程中,其他事务的如果要向该表插入语句都会被阻塞,从而保证插入数据时,被 `AUTO_INCREMENT` 修饰的字段的值是连续递增的。 +那么,一个事务在持有 AUTO-INC 锁的过程中,其他的事务如果要向该表插入语句都会被阻塞,从而保证插入数据时,被 `AUTO_INCREMENT` 修饰的字段的值是连续递增的。 但是,AUTO-INC 锁再对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。 From 5026ea1eb1844b4c51f76d571c3b28f484c79f0b Mon Sep 17 00:00:00 2001 From: Aden-q Date: Thu, 23 Nov 2023 15:25:24 -0600 Subject: [PATCH 24/78] docs: update the os content page --- os/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/os/README.md b/os/README.md index 81a69e4e..8523e320 100644 --- a/os/README.md +++ b/os/README.md @@ -79,12 +79,12 @@ - [I/O 多路复用:select/poll/epoll](/os/8_network_system/selete_poll_epoll.md) - [高性能网络模式:Reactor 和 Proactor](/os/8_network_system/reactor.md) - [什么是一致性哈希?](/os/8_network_system/hash.md) -- **学习心得** :point_down: +- **Linux 命令** :point_down: - [如何查看网络的性能指标?](/os/9_linux_cmd/linux_network.md) - - [画图经验分享](/os/9_linux_cmd/pv_uv.md) + - [如何从日志分析 PV、UV?](/os/9_linux_cmd/pv_uv.md) - **学习心得** :point_down: - - [计算机网络怎么学?](/os/10_learn/learn_os.md) - - [画图经验分享](/os/10_learn/draw.md) + - [操作系统怎么学?](/os/10_learn/learn_os.md) + - [画图经验分享](/os/10_learn/draw.md) ## 有错误怎么办? From 701be7a1b22946d99ea4ff6ee4e5e938b4416521 Mon Sep 17 00:00:00 2001 From: block666 <47653926+smartboy37597@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:07:17 +0800 Subject: [PATCH 25/78] Update what_happen_url.md Update what_happen_url.md --- network/1_base/what_happen_url.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/1_base/what_happen_url.md b/network/1_base/what_happen_url.md index c06f0572..67d29734 100644 --- a/network/1_base/what_happen_url.md +++ b/network/1_base/what_happen_url.md @@ -511,7 +511,7 @@ HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地 [1] 户根勤。网络是怎么连接的。人民邮电出版社。 -[2]刘超。趣谈网络协议。极客时间。。 +[2] 刘超。趣谈网络协议。极客时间。 ---- From 5b17bb59b2fc0ed1fffc3f8c730f78248c874972 Mon Sep 17 00:00:00 2001 From: block <1181882120@qq.com> Date: Fri, 1 Dec 2023 14:06:37 +0800 Subject: [PATCH 26/78] update https_rsa doc --- network/2_http/https_rsa.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/2_http/https_rsa.md b/network/2_http/https_rsa.md index 260032e4..55cfe29f 100644 --- a/network/2_http/https_rsa.md +++ b/network/2_http/https_rsa.md @@ -131,7 +131,7 @@ TLS 协议是如何解决 HTTP 的风险的呢? #### 数字证书签发和验证流程 -如下图图所示,为数字证书签发和验证流程: +如下图所示,为数字证书签发和验证流程: ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost4@main/网络/https/证书的校验.png) From c98ea046f4bf1ba6533b44619a0a7d6b62cc34a7 Mon Sep 17 00:00:00 2001 From: Xiaok29 <1526783667@qq.com> Date: Fri, 19 Jan 2024 18:32:11 +0800 Subject: [PATCH 27/78] =?UTF-8?q?docs:mysql=E5=A4=8D=E5=90=88=E7=B4=A2?= =?UTF-8?q?=E5=BC=95,=E5=8C=B9=E9=85=8D=E8=81=94=E5=90=88=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E6=BC=8F=E4=BA=86=E4=B8=80=E4=B8=AA=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/index/index_interview.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql/index/index_interview.md b/mysql/index/index_interview.md index ff8e1a7d..810f83e0 100644 --- a/mysql/index/index_interview.md +++ b/mysql/index/index_interview.md @@ -278,6 +278,7 @@ CREATE INDEX index_product_no_name ON product(product_no, name); - where a=1. - where a=1 and b=2 and c=3. - where a=1 and b=2. +- where a=1 and c=3. 需要注意的是,因为有查询优化器,所以 a 字段在 where 子句的顺序并不重要。 From 18e196fc07f9e07fa3bdb09ce50df03f8cba7ddd Mon Sep 17 00:00:00 2001 From: Aden-q Date: Sat, 20 Jan 2024 12:53:25 -0600 Subject: [PATCH 28/78] docs: correct grammar errors --- redis/base/redis_interview.md | 2 +- redis/cluster/cache_problem.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redis/base/redis_interview.md b/redis/base/redis_interview.md index 02bded39..e8b006b5 100644 --- a/redis/base/redis_interview.md +++ b/redis/base/redis_interview.md @@ -694,7 +694,7 @@ Redis 的内存淘汰的内容就暂时提这些,想更详细了解的,可 - **设置缓存不过期:** 我们可以通过后台服务来更新缓存数据,从而避免因为缓存失效造成的缓存雪崩,也可以在一定程度上避免缓存并发问题。 > 如何避免缓存击穿? -我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频地访问的数据被称为热点数据。 +我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频繁访问的数据被称为热点数据。 如果缓存中的**某个热点数据过期**了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是**缓存击穿**的问题。 diff --git a/redis/cluster/cache_problem.md b/redis/cluster/cache_problem.md index 48d5eba8..ea0bffc3 100644 --- a/redis/cluster/cache_problem.md +++ b/redis/cluster/cache_problem.md @@ -103,7 +103,7 @@ ## 缓存击穿 -我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频地访问的数据被称为热点数据。 +我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频繁访问的数据被称为热点数据。 如果缓存中的**某个热点数据过期**了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是**缓存击穿**的问题。 From c89c4e5bdacf325b7ce0af55ce2c67937fc11c3a Mon Sep 17 00:00:00 2001 From: Aden-q Date: Sat, 20 Jan 2024 19:12:17 -0600 Subject: [PATCH 29/78] docs: correct grammar errors --- redis/data_struct/command.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index 8cedbc4e..dcfed54c 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -1010,7 +1010,7 @@ BITCOUNT destmap ### 介绍 -Redis HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,是一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。但要注意,HyperLogLog 是统计规则是基于概率完成的,不是非常准确,标准误算率是 0.81%。 +Redis HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,是一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。但要注意,HyperLogLog 统计规则是基于概率完成的,不是非常准确,标准误算率是 0.81%。 所以,简单来说 HyperLogLog **提供不精确的去重计数**。 From 74a4078c1d1001392c85b407cc183c64645f61e4 Mon Sep 17 00:00:00 2001 From: Aden-q Date: Sat, 20 Jan 2024 20:45:16 -0600 Subject: [PATCH 30/78] docs: correct grammar errors --- redis/data_struct/data_struct.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis/data_struct/data_struct.md b/redis/data_struct/data_struct.md index bb4769b6..daefd026 100644 --- a/redis/data_struct/data_struct.md +++ b/redis/data_struct/data_struct.md @@ -83,7 +83,7 @@ Redis 的哈希桶是怎么保存键值对数据的呢? ![](https://img-blog.csdnimg.cn/img_convert/3c386666e4e7638a07b230ba14b400fe.png) -接下里,就好好聊一下底层数据结构! +接下来,就好好聊一下底层数据结构! ## SDS @@ -405,7 +405,7 @@ Redis 的链表实现优点如下: 压缩列表在表头有三个字段: -- ***zlbytes***,记录整个压缩列表占用对内存字节数; +- ***zlbytes***,记录整个压缩列表占用的内存字节数; - ***zltail***,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量; - ***zllen***,记录压缩列表包含的节点数量; - ***zlend***,标记压缩列表的结束点,固定值 0xFF(十进制 255)。 @@ -498,7 +498,7 @@ e1 原本的长度在 250~253 之间,因为刚才的扩展空间,此时 e1 解决哈希冲突的方式,有很多种。 -**Redis 采用了「链式哈希」来解决哈希冲突**,在不扩容哈希表的前提下,将具有相同哈希值的数据串起来,形成链接起,以便这些数据在表中仍然可以被查询到。 +**Redis 采用了「链式哈希」来解决哈希冲突**,在不扩容哈希表的前提下,将具有相同哈希值的数据串起来,形成链表,以便这些数据在表中仍然可以被查询到。 接下来,详细说说哈希表。 @@ -890,7 +890,7 @@ zskiplist *zslCreate(void) { - 它们不是非常内存密集型的。基本上由你决定。改变关于节点具有给定级别数的概率的参数将使其比 btree 占用更少的内存。 - Zset 经常需要执行 ZRANGE 或 ZREVRANGE 的命令,即作为链表遍历跳表。通过此操作,跳表的缓存局部性至少与其他类型的平衡树一样好。 -- 它们更易于实现、调试等。例如,由于跳表的简单性,我收到了一个补丁(已经在 Redis master 中),其中扩展了跳表,在 O(log(N) 中实现了 ZRANK。它只需要对代码进行少量修改。 +- 它们更易于实现、调试等。例如,由于跳表的简单性,我收到了一个补丁(已经在 Redis master 中),其中扩展了跳表,在 O(log(N)) 中实现了 ZRANK。它只需要对代码进行少量修改。 我再详细补充点: From cf094525aa3ea9f2beed3e9869cb8e673550478a Mon Sep 17 00:00:00 2001 From: Aden-q Date: Sun, 21 Jan 2024 11:50:40 -0600 Subject: [PATCH 31/78] docs: correct grammar errors --- redis/cluster/sentinel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/cluster/sentinel.md b/redis/cluster/sentinel.md index 34495a70..a5ca15b6 100644 --- a/redis/cluster/sentinel.md +++ b/redis/cluster/sentinel.md @@ -210,7 +210,7 @@ Redis 有个叫 slave-priority 配置项,可以给从节点设置优先级。 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/哨兵/从节点转换成功.png) -### 步骤三:通知客户的主节点已更换 +### 步骤三:通知客户端主节点已更换 经过前面一系列的操作后,哨兵集群终于完成主从切换的工作,那么新主节点的信息要如何通知给客户端呢? From 92da197a5e7f8da1b68140d86d65292c948029ee Mon Sep 17 00:00:00 2001 From: Taurres Wen Date: Mon, 22 Jan 2024 17:53:56 -0500 Subject: [PATCH 32/78] Update http_interview.md Fix typo. --- network/2_http/http_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/2_http/http_interview.md b/network/2_http/http_interview.md index 4e79bd17..691947db 100644 --- a/network/2_http/http_interview.md +++ b/network/2_http/http_interview.md @@ -589,7 +589,7 @@ HTTPS 采用的是**对称加密**和**非对称加密**结合的「混合加密 后面你的老师和父亲发现了你伪造公私钥的事情后,决定重新商量一个对策来应对你这个臭家伙。 -正所谓魔高一丈,道高一尺。 +正所谓魔高一尺,道高一丈。 既然伪造公私钥那么随意,所以你爸把他的公钥注册到**警察局**,警察局用他们自己的私钥对你父亲的公钥做了个数字签名,然后把你爸爸的「个人信息 + 公钥 + 数字签名」打包成一个**数字证书,也就是说这个数字证书包含你爸爸的公钥。** From db98be0a694178ca3fd70031dd0025c57d7ccc1b Mon Sep 17 00:00:00 2001 From: Taurres Wen Date: Mon, 22 Jan 2024 22:03:52 -0500 Subject: [PATCH 33/78] Update http_interview.md --- network/2_http/http_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/2_http/http_interview.md b/network/2_http/http_interview.md index 691947db..52940970 100644 --- a/network/2_http/http_interview.md +++ b/network/2_http/http_interview.md @@ -888,7 +888,7 @@ Header: :status: 200 OK 的编码内容为:1000 1000,那么表达的含义 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/network/http/index.png) -1. 最前面的 1 标识该 Header 是静态表中已经存在的 KV。(至于什么是静态表,可以看这篇:[HTTP/2 牛逼在哪?](https://xiaolincoding.com/network/2_http/http2.html)) +1. 最前面的 1 表示该 Header 是静态表中已经存在的 KV。(至于什么是静态表,可以看这篇:[HTTP/2 牛逼在哪?](https://xiaolincoding.com/network/2_http/http2.html)) 2. 在静态表理,“:status: 200 ok”静态表编码是 8,二进制即是 1000。 因此,整体加起来就是 1000 1000。 From 1402a6cbec62978c4ab32b7f164b79d291733639 Mon Sep 17 00:00:00 2001 From: Taurres Wen Date: Tue, 23 Jan 2024 22:04:05 -0500 Subject: [PATCH 34/78] Update page.md Fix typo --- mysql/index/page.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/index/page.md b/mysql/index/page.md index 70163307..4b3d2cc5 100644 --- a/mysql/index/page.md +++ b/mysql/index/page.md @@ -55,7 +55,7 @@ MySQL 支持多种存储引擎,不同的存储引擎,存储数据的方式 以上面那张图举个例子,5 个槽的编号分别为 0,1,2,3,4,我想查找主键为 11 的用户记录: - 先二分得出槽中间位是 (0+4)/2=2,2 号槽里最大的记录为 8。因为 11 > 8,所以需要从 2 号槽后继续搜索记录; -- 再使用二分搜索出 2 号和 4 槽的中间位是 (2+4)/2= 3,3 号槽里最大的记录为 12。因为 11 < 12,所以主键为 11 的记录在 3 号槽里; +- 再使用二分搜索出 2 号和 4 号槽的中间位是 (2+4)/2= 3,3 号槽里最大的记录为 12。因为 11 < 12,所以主键为 11 的记录在 3 号槽里; - 这里有个问题,**「槽对应的值都是这个组的主键最大的记录,如何找到组里最小的记录」**?比如槽 3 对应最大主键是 12 的记录,那如何找到最小记录 9。解决办法是:通过槽 3 找到 槽 2 对应的记录,也就是主键为 8 的记录。主键为 8 的记录的下一条记录就是槽 3 当中主键最小的 9 记录,然后开始向下搜索 2 次,定位到主键为 11 的记录,取出该条记录的信息即为我们想要查找的内容。 看到第三步的时候,可能有的同学会疑问,如果某个槽内的记录很多,然后因为记录都是单向链表串起来的,那这样在槽内查找某个记录的时间复杂度不就是 O(n) 了吗? @@ -133,4 +133,4 @@ InnoDB 的数据是按「数据页」为单位来读写的,默认数据页大 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From cbbeee6d5d8b6d933a0dd738e0497f10fcf6cf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=93=87=E5=A1=9E=E5=A4=A7=E5=98=B4=E5=A5=BD=E5=B8=A5?= <66861267+DaZuiZui@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:47:38 +0800 Subject: [PATCH 35/78] Update command.md --- redis/data_struct/command.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index 8cedbc4e..f3dd5d0c 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -1043,27 +1043,27 @@ PFMERGE destkey sourcekey [sourcekey ...] ### 应用场景 -#### 百万级网页 UV 计数 +#### 百万级网页 UA (User Agent) 计数 Redis HyperLogLog 优势在于只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。 -所以,非常适合统计百万级以上的网页 UV 的场景。 +所以,非常适合统计百万级以上的网页 UA 的场景。 -在统计 UV 时,你可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。 +在统计 UA 时,你可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。 ```shell -PFADD page1:uv user1 user2 user3 user4 user5 +PFADD page1:ua user1 user2 user3 user4 user5 ``` -接下来,就可以用 PFCOUNT 命令直接获得 page1 的 UV 值了,这个命令的作用就是返回 HyperLogLog 的统计结果。 +接下来,就可以用 PFCOUNT 命令直接获得 page1 的 UA 值了,这个命令的作用就是返回 HyperLogLog 的统计结果。 ```shell -PFCOUNT page1:uv +PFCOUNT page1:ua ``` 不过,有一点需要你注意一下,HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。 -这也就意味着,你使用 HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果你需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。 +这也就意味着,你使用 HyperLogLog 统计的 UA 是 100 万,但实际的 UA 可能是 101 万。虽然误差率不算大,但是,如果你需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。 ## GEO From 59e01f6a5fef15c47276210094239c7477e5d313 Mon Sep 17 00:00:00 2001 From: Checo Chan Date: Mon, 11 Mar 2024 23:17:51 +0800 Subject: [PATCH 36/78] update: how_update.md --- mysql/log/how_update.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/log/how_update.md b/mysql/log/how_update.md index 5007901d..94060d81 100644 --- a/mysql/log/how_update.md +++ b/mysql/log/how_update.md @@ -275,7 +275,7 @@ redo log 是循环写的方式,相当于一个环形,InnoDB 用 write pos 前面介绍的 undo log 和 redo log 这两个日志都是 Innodb 存储引擎生成的。 -MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件。 +MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事务执行过程中产生的所有 binlog 统一写 入 binlog 文件。 binlog 文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作,比如 SELECT 和 SHOW 操作。 From a5c6a35de1bf9c3649cd34c45590539c82abb901 Mon Sep 17 00:00:00 2001 From: smy1999 <865988027@qq.com> Date: Tue, 12 Mar 2024 19:33:41 +0800 Subject: [PATCH 37/78] Fix a typo. --- network/3_tcp/quic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/quic.md b/network/3_tcp/quic.md index 61df2d89..93fce6ac 100644 --- a/network/3_tcp/quic.md +++ b/network/3_tcp/quic.md @@ -281,7 +281,7 @@ TCP 更改拥塞控制算法是对系统中所有应用都生效,无法根据 ## QUIC 更快的连接建立 -对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手(1RTT),再 TLS 握手(2RTT),所以需要 3RTT 的延迟才能传输数据,就算 Session 会话服用,也需要至少 2 个 RTT。 +对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手(1RTT),再 TLS 握手(2RTT),所以需要 3RTT 的延迟才能传输数据,就算 Session 会话复用,也需要至少 2 个 RTT。 HTTP/3 在传输数据前虽然需要 QUIC 协议握手,这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。 From dc780b5a2eb02bcc2104569a510b2293b5a0706a Mon Sep 17 00:00:00 2001 From: shijianlong <1447855920@qq.com> Date: Thu, 14 Mar 2024 09:40:57 +0800 Subject: [PATCH 38/78] =?UTF-8?q?docs:mysql=20explain=E7=9A=84extra?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=EF=BC=8C=E5=BD=93=E4=B8=BAUsing=20filesort?= =?UTF-8?q?=20=E8=AF=B4=E6=98=8Eorder=20by=E7=9A=84=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=A7=A6=E5=8F=91=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/index/index_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/index/index_interview.md b/mysql/index/index_interview.md index 810f83e0..342637fa 100644 --- a/mysql/index/index_interview.md +++ b/mysql/index/index_interview.md @@ -569,7 +569,7 @@ const 类型表示使用了主键或者唯一索引与常量值进行比较, 这里说几个重要的参考指标: -- Using filesort:当查询语句中包含 group by 操作,而且无法利用索引完成排序操作的时候,这时不得不选择相应的排序算法进行,甚至可能会通过文件排序,效率是很低的,所以要避免这种问题的出现。 +- Using filesort:当查询语句中包含 order by 操作,而且无法利用索引完成排序操作的时候,这时不得不选择相应的排序算法进行,甚至可能会通过文件排序,效率是很低的,所以要避免这种问题的出现。 - Using temporary:使了用临时表保存中间结果,MySQL 在对查询结果排序时使用临时表,常见于排序 order by 和分组查询 group by。效率低,要避免这种问题的出现。 - Using index:所需数据只需在索引即可全部获得,不须要再到表中取数据,也就是使用了覆盖索引,避免了回表操作,效率不错。 From 301830d25ab358fbff2f3d526893d88f616c756a Mon Sep 17 00:00:00 2001 From: AsindE <96158490+smlwang@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:19:10 +0800 Subject: [PATCH 39/78] fix a typo --- network/4_ip/ip_base.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/4_ip/ip_base.md b/network/4_ip/ip_base.md index c9c7c717..eda6d40d 100644 --- a/network/4_ip/ip_base.md +++ b/network/4_ip/ip_base.md @@ -506,7 +506,7 @@ DHCP 在生活中我们是很常见的了,我们的电脑通常都是通过 DH 一旦客户端收到 DHCP ACK 后,交互便完成了,并且客户端能够在租用期内使用 DHCP 服务器分配的 IP 地址。 -如果租约的 DHCP IP 地址快期后,客户端会向服务器发送 DHCP 请求报文: +如果租约的 DHCP IP 地址快过期了,客户端会向服务器发送 DHCP 请求报文: - 服务器如果同意继续租用,则用 DHCP ACK 报文进行应答,客户端就会延长租期。 - 服务器如果不同意继续租用,则用 DHCP NACK 报文,客户端就要停止使用租约的 IP 地址。 From 8287e9f7a89801135d2fc4e925a846ecd224f5d5 Mon Sep 17 00:00:00 2001 From: Zonglei Dong Date: Tue, 19 Mar 2024 20:01:20 +0800 Subject: [PATCH 40/78] Update how_to_lock.md --- mysql/lock/how_to_lock.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/lock/how_to_lock.md b/mysql/lock/how_to_lock.md index 3c59b407..7bea7ea2 100644 --- a/mysql/lock/how_to_lock.md +++ b/mysql/lock/how_to_lock.md @@ -4,7 +4,7 @@ 是不是很多人都对 MySQL 加行级锁的规则搞的迷迷糊糊,对记录一会加的是 next-key 锁,一会加是间隙锁,一会又是记录锁。 -坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加了什么类型的行级锁。 +坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何用命令分析加了什么类型的行级锁。 之前我写过一篇关于「MySQL 是怎么加行级锁的?」的文章,随着我写 MySQL 锁相关的文章越来越多时,后来发现当时的文章写的不够详细。 @@ -747,4 +747,4 @@ mysql> select * from user where age >= 22 for update; 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From d2697e3ba496b8ecbe13f53d20b35df5e77b600e Mon Sep 17 00:00:00 2001 From: smy1999 <865988027@qq.com> Date: Tue, 19 Mar 2024 20:21:12 +0800 Subject: [PATCH 41/78] Fix typos. --- mysql/index/why_index_chose_bpuls_tree.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql/index/why_index_chose_bpuls_tree.md b/mysql/index/why_index_chose_bpuls_tree.md index 8695a6f4..1f51b3e8 100644 --- a/mysql/index/why_index_chose_bpuls_tree.md +++ b/mysql/index/why_index_chose_bpuls_tree.md @@ -181,9 +181,9 @@ B+ 树与 B 树差异的点,主要是以下这几点: B 树进行单个索引查询时,最快可以在 O(1) 的时间代价内就查到,而从平均时间代价来看,会比 B+ 树稍快一些。 -但是 B 树的查询波动会比较大,因为每个节点即存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。 +但是 B 树的查询波动会比较大,因为每个节点既存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。 -**B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少**。 +**B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少**。 ### 2、插入和删除效率 @@ -255,7 +255,7 @@ B 树和 B+ 都是通过多叉树的方式,会将树的高度变矮,所以 但是 MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有: -- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少。 +- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少。 - B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化; - B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。 From 2042f74a9948b7b82cb2be0b5fb2219e9c785f4f Mon Sep 17 00:00:00 2001 From: AsindE <96158490+smlwang@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:17:35 +0800 Subject: [PATCH 42/78] fix a typo about nagle send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使描述与下方伪代码匹配 --- network/3_tcp/tcp_feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/tcp_feature.md b/network/3_tcp/tcp_feature.md index 9bd3176c..31e78e28 100644 --- a/network/3_tcp/tcp_feature.md +++ b/network/3_tcp/tcp_feature.md @@ -430,7 +430,7 @@ probe ) 报文**,而对方在确认这个探测报文时,给出自己现在 使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据: -- 条件一:要等到窗口大小 >= `MSS` 并且 数据大小 >= `MSS`; +- 条件一:要等到可用窗口大小 >= `MSS` 并且 数据大小 >= `MSS`; - 条件二:收到之前发送数据的 `ack` 回包; 只要上面两个条件都不满足,发送方一直在囤积数据,直到满足上面的发送条件。 From cd847b67c47e1a45730ea70a2dabca0c0948700b Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:52:23 +0800 Subject: [PATCH 43/78] Update index_lose.md to fix typo --- mysql/index/index_lose.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/index/index_lose.md b/mysql/index/index_lose.md index 759c3660..8f3212c8 100644 --- a/mysql/index/index_lose.md +++ b/mysql/index/index_lose.md @@ -214,7 +214,7 @@ select * from t_user where phone = 1300000001; 要明白这个原因,首先我们要知道 MySQL 的数据类型转换规则是什么?就是看 MySQL 是会将字符串转成数字处理,还是将数字转换成字符串处理。 -我在看《mysql45 讲的时候》看到一个简单的测试方式,就是通过 select“10” > 9 的结果来知道 MySQL 的数据类型转换规则是什么: +我在看《MySQL45 讲》的时候看到一个简单的测试方式,就是通过 select“10” > 9 的结果来知道 MySQL 的数据类型转换规则是什么: - 如果规则是 MySQL 会将自动「字符串」转换成「数字」,就相当于 select 10 > 9,这个就是数字比较,所以结果应该是 1; - 如果规则是 MySQL 会将自动「数字」转换成「字符串」,就相当于 select "10" > "9",这个是字符串比较,字符串比较大小是逐位从高位到低位逐个比较(按 ascii 码) ,那么"10"字符串相当于“1”和“0”字符的组合,所以先是拿“1”字符和“9”字符比较,因为“1”字符比“9”字符小,所以结果应该是 0。 @@ -334,4 +334,4 @@ select * from t_user where id = 1 or age = 18; 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From 3498b1cd04a51aa58d58bb22f14497199513d887 Mon Sep 17 00:00:00 2001 From: Checo Chan Date: Sat, 23 Mar 2024 23:24:33 +0800 Subject: [PATCH 44/78] update: mysql_lock.md --- mysql/lock/mysql_lock.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/lock/mysql_lock.md b/mysql/lock/mysql_lock.md index 65585447..b5831c20 100644 --- a/mysql/lock/mysql_lock.md +++ b/mysql/lock/mysql_lock.md @@ -189,7 +189,7 @@ AUTO-INC 锁是特殊的表锁机制,锁**不是再一个事务提交后才释 那么,一个事务在持有 AUTO-INC 锁的过程中,其他的事务如果要向该表插入语句都会被阻塞,从而保证插入数据时,被 `AUTO_INCREMENT` 修饰的字段的值是连续递增的。 -但是,AUTO-INC 锁再对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。 +但是,AUTO-INC 锁在对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。 因此,在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种**轻量级的锁**来实现自增。 From 97a82f02a6d0beecfd7124723216043473b8d5d8 Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:43:51 +0800 Subject: [PATCH 45/78] Fix typo in mysql_lock.md --- mysql/lock/mysql_lock.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql/lock/mysql_lock.md b/mysql/lock/mysql_lock.md index 65585447..b2957e1d 100644 --- a/mysql/lock/mysql_lock.md +++ b/mysql/lock/mysql_lock.md @@ -183,7 +183,7 @@ select ... for update; 之后可以在插入数据时,可以不指定主键的值,数据库会自动给主键赋值递增的值,这主要是通过 **AUTO-INC 锁**实现的。 -AUTO-INC 锁是特殊的表锁机制,锁**不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放**。 +AUTO-INC 锁是特殊的表锁机制,锁**不是在一个事务提交后才释放,而是在执行完插入语句后就会立即释放**。 **在插入数据时,MySQL 会加一个表级别的 AUTO-INC 锁**,然后会为被 `AUTO_INCREMENT` 修饰的字段赋递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。 @@ -328,4 +328,4 @@ Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![img](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From cd2ba401cf2e1480e759819dc55a34b752bea31b Mon Sep 17 00:00:00 2001 From: Jie Qiu Date: Mon, 25 Mar 2024 13:57:31 -0700 Subject: [PATCH 46/78] selete_poll_epoll typo --- os/8_network_system/selete_poll_epoll.md | 33 +++++++++--------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index 1c649b55..290d2b81 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -12,9 +12,9 @@ ## 最基本的 Socket 模型 -要想客户端和服务器能在网络中通信,那必须得使用 Socket 编程,它是进程间通信里比较特别的方式,特别之处在于它是可以跨主机间通信。 +要想客户端和服务器能在网络中通信,那必须得使用 Socket 编程,它是进程间通信里比较特别的方式,特别之处在于它是可以跨主机间通信。 -Socket 的中文名叫作插口,乍一看还挺迷惑的。事实上,双方要进行网络通信前,各自得创建一个 Socket,这相当于客户端和服务器都开了一个“口子”,双方读取和发送数据的时候,都通过这个“口子”。这样一看,是不是觉得很像弄了一根网线,一头插在客户端,一头插在服务端,然后进行通信。 +Socket 的中文名叫作插口,乍一看还挺迷惑的。事实上,双方要进行网络通信前,各自得创建一个 Socket,这相当于客户端和服务器都开了一个“口子”,双方读取和发送数据的时候,都通过这个“口子”。这样一看,是不是觉得很像弄了一根网线,一头插在客户端,一头插在服务端,然后进行通信。 创建 Socket 的时候,可以指定网络层使用的是 IPv4 还是 IPv6,传输层使用的是 TCP 还是 UDP。 @@ -33,28 +33,25 @@ UDP 的 Socket 编程相对简单些,这里我们只介绍基于 TCP 的 Socke 那客户端是怎么发起连接的呢?客户端在创建好 Socket 后,调用 `connect()` 函数发起连接,该函数的参数要指明服务端的 IP 地址和端口号,然后万众期待的 TCP 三次握手就开始了。 -在 TCP 连接的过程中,服务器的内核实际上为每个 Socket 维护了两个队列: +在 TCP 连接的过程中,服务器的内核实际上为每个 Socket 维护了两个队列: - 一个是还没完全建立连接的队列,称为 **TCP 半连接队列**,这个队列都是没有完成三次握手的连接,此时服务端处于 `syn_rcvd` 的状态; - 一个是一件建立连接的队列,称为 **TCP 全连接队列**,这个队列都是完成了三次握手的连接,此时服务端处于 `established` 状态; -当 TCP 全连接队列不为空后,服务端的 `accept()` 函数,就会从内核中的 TCP 全连接队列里拿出一个已经完成连接的 Socket 返回应用程序,后续数据传输都用这个 Socket。 +当 TCP 全连接队列不为空后,服务端的 `accept()` 函数,就会从内核中的 TCP 全连接队列里拿出一个已经完成连接的 Socket 返回应用程序,后续数据传输都用这个 Socket。 注意,监听的 Socket 和真正用来传数据的 Socket 是两个: - 一个叫作**监听 Socket**; - 一个叫作**已连接 Socket**; - 连接建立后,客户端和服务端就开始相互传输数据了,双方都可以通过 `read()` 和 `write()` 函数来读写数据。 至此,TCP 协议的 Socket 程序的调用过程就结束了,整个过程如下图: - ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost4@main/操作系统/多路复用/tcp_socket.png) - -看到这,不知道你有没有觉得读写 Socket 的方式,好像读写文件一样。 +看到这,不知道你有没有觉得读写 Socket 的方式,好像读写文件一样。 是的,基于 Linux 一切皆文件的理念,在内核中 Socket 也是以「文件」的形式存在的,也是有对应的文件描述符。 @@ -89,7 +86,7 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 相信你知道 TCP 连接是由四元组唯一确认的,这个四元组就是:**本机 IP, 本机端口,对端 IP, 对端端口**。 -服务器作为服务方,通常会在本地固定监听一个端口,等待客户端的连接。因此服务器的本地 IP 和端口是固定的,于是对于服务端 TCP 连接的四元组只有对端 IP 和端口是会变化的,所以**最大 TCP 连接数 = 客户端 IP 数×客户端端口数**。 +服务器作为服务方,通常会在本地固定监听一个端口,等待客户端的连接。因此服务器的本地 IP 和端口是固定的,于是对于服务端 TCP 连接的四元组只有对端 IP 和端口是会变化的,所以**最大 TCP 连接数 = 客户端 IP 数 × 客户端端口数**。 对于 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是**服务端单机最大 TCP 连接数约为 2 的 48 次方**。 @@ -118,8 +115,6 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 正因为子进程会**复制父进程的文件描述符**,于是就可以直接使用「已连接 Socket」和客户端通信了, - - 可以发现,子进程不需要关心「监听 Socket」,只需要关心「已连接 Socket」;父进程则相反,将客户服务交给子进程来处理,因此父进程不需要关心「已连接 Socket」,只需要关心「监听 Socket」。 下面这张图描述了从连接请求到连接建立,父进程创建生子进程为客户服务。 @@ -134,7 +129,7 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。 ----- +--- ## 多线程模型 @@ -150,7 +145,6 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost4@main/操作系统/多路复用/线程池.png) - 需要注意的是,这个队列是全局的,每个线程都会操作,为了避免多线程竞争,线程在操作这个队列前要加锁。 上面基于进程或者线程模型的,其实还是有问题的。新到来一个 TCP 连接,就需要分配一个进程或者线程,那么如果要达到 C10K,意味着要一台机器维护 1 万个连接,相当于要维护 1 万个进程/线程,操作系统就算死扛也是扛不住的。 @@ -181,7 +175,6 @@ select 实现多路复用的方式是,将已连接的 Socket 都放到一个** select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制,默认最大值为 `1024`,只能监听 0~1023 的文件描述符。 - poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。 但是 poll 和 select 并没有太大的本质区别,**都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合**,这种方式随着并发数上来,性能的损耗会呈指数级增长。 @@ -192,9 +185,9 @@ poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动 epoll 通过两个方面,很好解决了 select/poll 的问题。 -*第一点*,epoll 在内核里使用**红黑树来跟踪进程所有待检测的文件描述字**,把需要监控的 socket 通过 `epoll_ctl()` 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删查一般时间复杂度是 `O(logn)`,通过对这棵红黑树进行操作,这样就不需要像 select/poll 每次操作时都传入整个 socket 集合,只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。 +_第一点_,epoll 在内核里使用**红黑树来跟踪进程所有待检测的文件描述符**,把需要监控的 socket 通过 `epoll_ctl()` 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删查一般时间复杂度是 `O(logn)`,通过对这棵红黑树进行操作,这样就不需要像 select/poll 每次操作时都传入整个 socket 集合,只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。 -*第二点*,epoll 使用事件驱动的机制,内核里**维护了一个链表来记录就绪事件**,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 `epoll_wait()` 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。 +_第二点_,epoll 使用事件驱动的机制,内核里**维护了一个链表来记录就绪事件**,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 `epoll_wait()` 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。 从下图你可以看到 epoll 相关的接口作用: @@ -210,8 +203,7 @@ epoll 的方式即使监听的 Socket 数量越多的时候,效率不会大幅 好了,这个题外话就说到这了,我们继续! - -epoll 支持两种事件触发模式,分别是**边缘触发(*edge-triggered,ET*)**和**水平触发(*level-triggered,LT*)**。 +epoll 支持两种事件触发模式,分别是**边缘触发(_edge-triggered,ET_)**和**水平触发(_level-triggered,LT_)**。 这两个术语还挺抽象的,其实它们的区别还是很好理解的。 @@ -244,7 +236,6 @@ select/poll 只有水平触发模式,epoll 默认的触发模式是水平触 ## 总结 - 最基础的 TCP 的 Socket 编程,它是阻塞 I/O 模型,基本上只能一对一通信,那为了服务更多的客户端,我们需要改进网络 I/O 模型。 比较传统的方式是使用多进程/线程模型,每来一个客户端连接,就分配一个进程/线程,然后后续的读写都在对应的进程/线程,这种方式处理 100 个客户端没问题,但是当客户端增大到 10000 个时,10000 个进程/线程的调度、上下文切换以及它们占用的内存,都会成为瓶颈。 @@ -264,10 +255,10 @@ epoll 是解决 C10K 问题的利器,通过两个方面解决了 select/poll 而且,epoll 支持边缘触发和水平触发的方式,而 select/poll 只支持水平触发,一般而言,边缘触发的方式会比水平触发的效率高。 ----- +--- ## 关注作者 -***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** +**_哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF_** ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From 01347a925e9d6409e8fd1984bd32bb68bbc153ce Mon Sep 17 00:00:00 2001 From: Jie Qiu Date: Mon, 25 Mar 2024 14:13:24 -0700 Subject: [PATCH 47/78] hash.md typo --- os/8_network_system/hash.md | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/os/8_network_system/hash.md b/os/8_network_system/hash.md index 32595f77..dd8597ce 100644 --- a/os/8_network_system/hash.md +++ b/os/8_network_system/hash.md @@ -34,7 +34,7 @@ 但是,加权轮询算法是无法应对「分布式系统」的,因为分布式系统中,每个节点存储的数据是不同的。 -当我们想提高系统的容量,就会将数据水平切分到不同的节点来存储,也就是将数据分布到了不同的节点。比如**一个分布式 KV(key-valu)缓存系统,某个 key 应该到哪个或者哪些节点上获得,应该是确定的**,不是说任意访问一个节点都可以得到缓存结果的。 +当我们想提高系统的容量,就会将数据水平切分到不同的节点来存储,也就是将数据分布到了不同的节点。比如**一个分布式 KV(key-value)缓存系统,某个 key 应该到哪个或者哪些节点上获得,应该是确定的**,不是说任意访问一个节点都可以得到缓存结果的。 因此,我们要想一个能应对分布式系统的负载均衡算法。 @@ -56,27 +56,19 @@ hash(key) % 3 举个例子,假设我们有一个由 A、B、C 三个节点组成分布式 KV 缓存系统,基于计算公式 `hash(key) % 3` 将数据进行了映射,每个节点存储了不同的数据: - - ![](https://img-blog.csdnimg.cn/img_convert/025ddcaabece1f4b5823dfb1fb7340ef.png) - - 现在有 3 个查询 key 的请求,分别查询 key-01,key-02,key-03 的数据,这三个 key 分别经过 hash() 函数计算后的值为 hash( key-01) = 6、hash( key-02) = 7、hash(key-03) = 8,然后再对这些值进行取模运算。 通过这样的哈希算法,每个 key 都可以定位到对应的节点。 ![](https://img-blog.csdnimg.cn/img_convert/ed14c96417e08b4f916e0cd23d12b7bd.png) - - 当 3 个节点不能满足业务需求了,这时我们增加了一个节点,节点的数量从 3 变化为 4,意味取模哈希函数中基数的变化,这样会导致**大部分映射关系改变**,如下图: ![](https://img-blog.csdnimg.cn/img_convert/392c54cfb9ec47f5191008aa1d27d6b5.png) - - -比如,之前的 hash(key-01) % `3` = 0,就变成了 hash(key-01) % `4` = 2,查询 key-01 数据时,寻址到了节点 C,而 key-01 的数据是存储在节点 A 上的,不是在节点 C,所以会查询不到数据。 +比如,之前的 hash(key-01) % `3` = 0,就变成了 hash(key-01) % `4` = 2,查询 key-01 数据时,寻址到了节点 C,而 key-01 的数据是存储在节点 A 上的,不是在节点 C,所以会查询不到数据。 同样的道理,如果我们对分布式系统进行缩容,比如移除一个节点,也会因为取模哈希函数中基数的变化,可能出现查询不到数据的问题。 @@ -111,9 +103,9 @@ hash(key) % 3 ![](https://img-blog.csdnimg.cn/img_convert/83d7f363643353c92d252e34f1d4f687.png) -接着,对要查询的 key-01 进行哈希计算,确定此 key-01 映射在哈希环的位置,然后从这个位置往顺时针的方向找到第一个节点,就是存储该 key-01 数据的节点。 +接着,对要查询的 key-01 进行哈希计算,确定此 key-01 映射在哈希环的位置,然后从这个位置往顺时针的方向找到第一个节点,就是存储该 key-01 数据的节点。 -比如,下图中的 key-01 映射的位置,往顺时针的方向找到第一个节点就是节点 A。 +比如,下图中的 key-01 映射的位置,往顺时针的方向找到第一个节点就是节点 A。 ![](https://img-blog.csdnimg.cn/img_convert/30c2c70721c12f9c140358fbdc5f2282.png) @@ -128,7 +120,7 @@ hash(key) % 3 ![](https://img-blog.csdnimg.cn/img_convert/f8909edef2f3949f8945bb99380baab3.png) -你可以看到,key-01、key-03 都不受影响,只有 key-02 需要被迁移节点 D。 +你可以看到,key-01、key-03 都不受影响,只有 key-02 需要被迁移节点 D。 假设节点数量从 3 减少到了 2,比如将节点 A 移除: @@ -154,8 +146,6 @@ hash(key) % 3 所以,**一致性哈希算法虽然减少了数据迁移量,但是存在节点分布不均匀的问题**。 - - ## 如何通过虚拟节点提高均衡度? 要想解决节点能在哈希环上分配不均匀的问题,就是要有大量的节点,节点数越多,哈希环上的节点分布的就越均匀。 @@ -208,6 +198,6 @@ hash(key) % 3 ## 关注作者 -***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** +**_哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF_** ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) From 4498d89093d8143a067dfbe937dd8e0af19d9b4f Mon Sep 17 00:00:00 2001 From: AsindE <96158490+smlwang@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:27:42 +0800 Subject: [PATCH 48/78] fix typo 1024000=1000m --- os/4_process/create_thread_max.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/4_process/create_thread_max.md b/os/4_process/create_thread_max.md index 73227fc3..f12fa164 100644 --- a/os/4_process/create_thread_max.md +++ b/os/4_process/create_thread_max.md @@ -154,7 +154,7 @@ echo 99999 > /proc/sys/kernel/max_map_count 我只有这台服务器,如果你们有性能更强的服务器来测试的话,有兴趣的小伙伴可以去测试下。 -接下来,我们换个思路测试下,把创建线程时分配的栈空间调大,比如调大为 100M,在大就会创建线程失败。 +接下来,我们换个思路测试下,把创建线程时分配的栈空间调大,比如调大为 1000M,在大就会创建线程失败。 ```plain ulimit -s 1024000 From 43d698d24162ea1538cdd53e97fc55bc98fc8f19 Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:25:35 +0800 Subject: [PATCH 49/78] Fix typo in command.md --- redis/data_struct/command.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index d71fdc17..f325925f 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -953,13 +953,13 @@ BITPOS uid:sign:100:202206 1 需要注意的是,因为 offset 从 0 开始的,所以我们需要将返回的 value + 1。 -#### 判断用户登陆态 +#### 判断用户登录态 Bitmap 提供了 `GETBIT、SETBIT` 操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。 -只需要一个 key = login_status 表示存储用户登陆状态集合数据,将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 `GETBIT`判断对应的用户是否在线。50000 万 用户只需要 6 MB 的空间。 +只需要一个 key = login_status 表示存储用户登录状态集合数据,将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 `GETBIT`判断对应的用户是否在线。50000 万 用户只需要 6 MB 的空间。 -假如我们要判断 ID = 10086 的用户的登陆情况: +假如我们要判断 ID = 10086 的用户的登录情况: 第一步,执行以下指令,表示用户已登录。 @@ -967,7 +967,7 @@ Bitmap 提供了 `GETBIT、SETBIT` 操作,通过一个偏移值 offset 对 bit SETBIT login_status 10086 1 ``` -第二步,检查该用户是否登陆,返回值 1 表示已登录。 +第二步,检查该用户是否登录,返回值 1 表示已登录。 ```apache GETBIT login_status 10086 @@ -1401,7 +1401,7 @@ Redis 五种数据类型的应用场景: Redis 后续版本又支持四种数据类型,它们的应用场景如下: -- BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等; +- BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登录状态、连续签到用户总数等; - HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等; - GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车; - Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息 ID,支持以消费组形式消费数据。 From ac695bb34a3679739833251d70e1c7f6a990d97f Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:39:15 +0800 Subject: [PATCH 50/78] Fix Typo Update cpu_mesi.md --- os/1_hardware/cpu_mesi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/1_hardware/cpu_mesi.md b/os/1_hardware/cpu_mesi.md index c9131caa..4987c2e3 100644 --- a/os/1_hardware/cpu_mesi.md +++ b/os/1_hardware/cpu_mesi.md @@ -193,7 +193,7 @@ CPU 在读写数据的时候,都是在 CPU Cache 读写数据的,原因是 C 基于总线嗅探机制的 MESI 协议,就满足上面了这两点,因此它是保障缓存一致性的协议。 -MESI 协议,是已修改、独占、共享、已失效这四个状态的英文缩写的组合。整个 MSI 状态的变更,则是根据来自本地 CPU 核心的请求,或者来自其他 CPU 核心通过总线传输过来的请求,从而构成一个流动的状态机。另外,对于在「已修改」或者「独占」状态的 Cache Line,修改更新其数据不需要发送广播给其他 CPU 核心。 +MESI 协议,是已修改、独占、共享、已失效这四个状态的英文缩写的组合。整个 MESI 状态的变更,则是根据来自本地 CPU 核心的请求,或者来自其他 CPU 核心通过总线传输过来的请求,从而构成一个流动的状态机。另外,对于在「已修改」或者「独占」状态的 Cache Line,修改更新其数据不需要发送广播给其他 CPU 核心。 --- @@ -202,4 +202,4 @@ MESI 协议,是已修改、独占、共享、已失效这四个状态的英文 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/其他/公众号介绍.png) -***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** \ No newline at end of file +***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林 coding」,关注后,回复「网络」再送你图解网络 PDF*** From eac5f550105188be71b9d5b26d0beefb9f78edd7 Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:49:08 +0800 Subject: [PATCH 51/78] Update process_commu.md --- os/4_process/process_commu.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/4_process/process_commu.md b/os/4_process/process_commu.md index c4d6c457..9ea4b5c5 100644 --- a/os/4_process/process_commu.md +++ b/os/4_process/process_commu.md @@ -238,14 +238,14 @@ $ kill -l 我们来看看创建 socket 的系统调用: ```c -int socket(int domain, int type, int protocOl) +int socket(int domain, int type, int protocol) ``` 三个参数分别代表: - domain 参数用来指定协议族,比如 AF_INET 用于 IPV4、AF_INET6 用于 IPV6、AF_LOCAL/AF_UNIX 用于本机; - type 参数用来指定通信特性,比如 SOCK_STREAM 表示的是字节流,对应 TCP、SOCK_DGRAM 表示的是数据报,对应 UDP、SOCK_RAW 表示的是原始套接字; -- protocOl 参数原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成,protocol 目前一般写成 0 即可; +- protocol 参数原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成,protocol 目前一般写成 0 即可; 根据创建 socket 类型的不同,通信的方式也就不同: From 732c9d1f709681341a0e87a484ac7813091e2578 Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:39:26 +0800 Subject: [PATCH 52/78] Update multithread_sync.md --- os/4_process/multithread_sync.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/4_process/multithread_sync.md b/os/4_process/multithread_sync.md index 2bd8a987..85082326 100644 --- a/os/4_process/multithread_sync.md +++ b/os/4_process/multithread_sync.md @@ -52,7 +52,7 @@ 那么问题就来了,多个线程如果竞争共享资源,如果不采取有效的措施,则会造成共享数据的混乱。 -我们做个小实验,创建两个线程,它们分别对共享变量 `i` 自增 `1` 执行 `10000` 次,如下代码(虽然说是 C++ 代码,但是没学过 C++ 的同学也是看到懂的): +我们做个小实验,创建两个线程,它们分别对共享变量 `i` 自增 `1` 执行 `10000` 次,如下代码(虽然说是 C++ 代码,但是没学过 C++ 的同学也是能看懂的): ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/操作系统/互斥与同步/6-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%AB%9E%E4%BA%89C%2B%2B%E4%BB%A3%E7%A0%81%E4%BE%8B%E5%AD%90.jpeg) From d8da7997f4927286c7f3b544483550ff62685a8d Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:26:06 +0800 Subject: [PATCH 53/78] Update selete_poll_epoll.md --- os/8_network_system/selete_poll_epoll.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/selete_poll_epoll.md b/os/8_network_system/selete_poll_epoll.md index 290d2b81..de63c1fb 100644 --- a/os/8_network_system/selete_poll_epoll.md +++ b/os/8_network_system/selete_poll_epoll.md @@ -159,7 +159,7 @@ sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 一个进程虽然任意时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。 -我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,**进程可以通过一个系统调用函数从内核中获取多个事件**。 +我们熟悉的 select/poll/epoll 是内核提供给用户态的多路复用系统调用,**进程可以通过一个系统调用函数从内核中获取多个事件**。 select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可。 From ea30822242ed80074a2129b871b08347679ddb75 Mon Sep 17 00:00:00 2001 From: xinxinxiangying258 <923058707@qq.com> Date: Wed, 24 Apr 2024 16:20:21 +0800 Subject: [PATCH 54/78] #fix# remove useless space --- os/8_network_system/reactor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/reactor.md b/os/8_network_system/reactor.md index b6410637..669f65f7 100644 --- a/os/8_network_system/reactor.md +++ b/os/8_network_system/reactor.md @@ -32,7 +32,7 @@ 但是引入了线程池,那么一个线程要处理多个连接的业务,线程在处理某个连接的 `read` 操作时,如果遇到没有数据可读,就会发生阻塞,那么线程就没办法继续处理其他连接的业务。 -要解决这一个问题,最简单的方式就是将 socket 改成非阻塞,然后线程不断地轮询调用 `read` 操作来判断是否有数据,这种方式虽然该能够解决阻塞的问题,但是解决的方式比较粗暴,因为轮询是要消耗 CPU 的,而且随着一个 线程处理的连接越多,轮询的效率就会越低。 +要解决这一个问题,最简单的方式就是将 socket 改成非阻塞,然后线程不断地轮询调用 `read` 操作来判断是否有数据,这种方式虽然该能够解决阻塞的问题,但是解决的方式比较粗暴,因为轮询是要消耗 CPU 的,而且随着一个线程处理的连接越多,轮询的效率就会越低。 上面的问题在于,线程并不知道当前连接是否有数据可读,从而需要每次通过 `read` 去试探。 From 8257bdeee310e26c90d2d089646202253d19fa2f Mon Sep 17 00:00:00 2001 From: xinxinxiangying258 <923058707@qq.com> Date: Wed, 24 Apr 2024 16:26:36 +0800 Subject: [PATCH 55/78] #fix# add detail word --- os/8_network_system/reactor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/reactor.md b/os/8_network_system/reactor.md index 669f65f7..1ecdc06b 100644 --- a/os/8_network_system/reactor.md +++ b/os/8_network_system/reactor.md @@ -163,7 +163,7 @@ Redis 是由 C 语言实现的,它采用的正是「单 Reactor 单进程」 - Handler 对象不再负责业务处理,只负责数据的接收和发送,Handler 对象通过 read 读取到数据后,会将数据发给子线程里的 Processor 对象进行业务处理; - 子线程里的 Processor 对象就进行业务处理,处理完后,将结果发给主线程中的 Handler 对象,接着由 Handler 通过 send 方法将响应结果发送给 client; -单 Reator 多线程的方案优势在于**能够充分利用多核 CPU 的能**,那既然引入多线程,那么自然就带来了多线程竞争资源的问题。 +单 Reator 多线程的方案优势在于**能够充分利用多核 CPU 的性能**,那既然引入多线程,那么自然就带来了多线程竞争资源的问题。 例如,子线程完成业务处理后,要把结果传递给主线程的 Reactor 进行发送,这里涉及共享数据的竞争。 From 613812f475913fb944b3cf8bdd603080e19f2f2c Mon Sep 17 00:00:00 2001 From: xinxinxiangying258 <923058707@qq.com> Date: Wed, 24 Apr 2024 16:28:14 +0800 Subject: [PATCH 56/78] #fix# remove useless space --- os/8_network_system/reactor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/reactor.md b/os/8_network_system/reactor.md index 1ecdc06b..64bb0929 100644 --- a/os/8_network_system/reactor.md +++ b/os/8_network_system/reactor.md @@ -187,7 +187,7 @@ Redis 是由 C 语言实现的,它采用的正是「单 Reactor 单进程」 方案详细说明如下: -- 主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程; +- 主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程; - 子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听,并创建一个 Handler 用于处理连接的响应事件。 - 如果有新的事件发生时,SubReactor 对象会调用当前连接对应的 Handler 对象来进行响应。 - Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程。 From d05e101cb450aada33dd6b349e7af1259f7fafc0 Mon Sep 17 00:00:00 2001 From: xinxinxiangying258 <923058707@qq.com> Date: Wed, 24 Apr 2024 17:03:37 +0800 Subject: [PATCH 57/78] #fix# remove useless space --- os/8_network_system/zero_copy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/zero_copy.md b/os/8_network_system/zero_copy.md index e3c51f60..3ab88730 100644 --- a/os/8_network_system/zero_copy.md +++ b/os/8_network_system/zero_copy.md @@ -66,7 +66,7 @@ write(socket, tmp_buf, len); ![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost2/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E9%9B%B6%E6%8B%B7%E8%B4%9D/%E4%BC%A0%E7%BB%9F%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93.png) -首先,期间共**发生了 4 次用户态与内核态的上下文切换**,因为发生了两次系统调用,一次是 `read()` ,一次是 `write()`,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。 +首先,期间共**发生了 4 次用户态与内核态的上下文切换**,因为发生了两次系统调用,一次是 `read()` ,一次是 `write()`,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。 上下文切换的成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。 From f757c298728abdac9d13ef696ba60701bfcfb915 Mon Sep 17 00:00:00 2001 From: zweirdo <1606403438@qq.com> Date: Thu, 25 Apr 2024 15:35:22 +0800 Subject: [PATCH 58/78] a minor typo --- network/3_tcp/tcp_unplug_the_network_cable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/tcp_unplug_the_network_cable.md b/network/3_tcp/tcp_unplug_the_network_cable.md index 3d5bddee..9b5288ee 100644 --- a/network/3_tcp/tcp_unplug_the_network_cable.md +++ b/network/3_tcp/tcp_unplug_the_network_cable.md @@ -33,7 +33,7 @@ 此时,客户端和服务端的 TCP 连接依然存在的,就感觉什么事情都没有发生。 -但是,**如果如果在服务端重传报文的过程中,客户端一直没有将网线插回去**,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。 +但是,**如果在服务端重传报文的过程中,客户端一直没有将网线插回去**,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。 而等客户端插回网线后,如果客户端向服务端发送了数据,由于服务端已经没有与客户端相同四元祖的 TCP 连接了,因此服务端内核就会回复 RST 报文,客户端收到后就会释放该 TCP 连接。 From 68314b18915e3fbedb060b4f7c0b8fe01bafaedb Mon Sep 17 00:00:00 2001 From: xinxinxiangying258 <923058707@qq.com> Date: Fri, 10 May 2024 10:15:30 +0800 Subject: [PATCH 59/78] [update]remove useless spaces --- network/3_tcp/challenge_ack.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/3_tcp/challenge_ack.md b/network/3_tcp/challenge_ack.md index 0dd7bff5..8f0acf48 100644 --- a/network/3_tcp/challenge_ack.md +++ b/network/3_tcp/challenge_ack.md @@ -119,7 +119,7 @@ tcp_send_challenge_ack 函数里就会调用 tcp_send_ack 函数来回复一个 在 Linux 上有个叫 killcx 的工具,就是基于上面这样的方式实现的,它会主动发送 SYN 包获取 SEQ/ACK 号,然后利用 SEQ/ACK 号伪造两个 RST 报文分别发给客户端和服务端,这样双方的 TCP 连接都会被释放,这种方式活跃和非活跃的 TCP 连接都可以杀掉。 -killcx 的工具使用方式也很简单,如果在服务端执行 killcx 工具,只需指明客户端的 IP 和端口号,如果在客户端执行 killcx 工具,则就指明服务端的 IP 和端口号。 +killcx 的工具使用方式也很简单,如果在服务端执行 killcx 工具,只需指明客户端的 IP 和端口号,如果在客户端执行 killcx 工具,则就指明服务端的 IP 和端口号。 ```csharp ./killcx :<端口号> @@ -222,7 +222,7 @@ killcx 工具则是属于主动获取,它是主动发送一个 SYN 报文, 这两种工具都是通过伪造 RST 报文来关闭 TCP 连接的,但是它们获取「对方下一次期望收到的序列号的方式是不同的,也正因此,造就了这两个工具的应用场景有区别。 - tcpkill 工具只能用来关闭活跃的 TCP 连接,无法关闭非活跃的 TCP 连接,因为 tcpkill 工具是等双方进行 TCP 通信后,才去获取正确的序列号,如果这条 TCP 连接一直没有任何数据传输,则就永远获取不到正确的序列号。 -- killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。 +- killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。 完! From 0eb36e5a9f04061456a28401d01c6639b867d2e0 Mon Sep 17 00:00:00 2001 From: Navyum <36869790+Navyum@users.noreply.github.com> Date: Fri, 17 May 2024 16:51:26 +0800 Subject: [PATCH 60/78] Update how_update.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 依据: 1. 如果先更新内存记录,此时断电就不是crash-safe的 2. flush 链表的脏页需要保存redo log对应的lsn值,用来做checkpoint操作,所以先有redo log写入,再有脏页 --- mysql/log/how_update.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/log/how_update.md b/mysql/log/how_update.md index 94060d81..ab2160f8 100644 --- a/mysql/log/how_update.md +++ b/mysql/log/how_update.md @@ -597,7 +597,7 @@ commit 阶段队列的作用是承接 sync 阶段的事务,完成最后的引 - 如果一样的话就不进行后续更新流程; - 如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作; 3. 开启事务,InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在内存修改该 Undo 页面后,需要记录对应的 redo log。 -4. InnoDB 层开始更新记录,会先更新内存(同时标记为脏页),然后将记录写到 redo log 里面,这个时候更新就算完成了。为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 **WAL 技术**,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。 +4. InnoDB 层开始更新记录,先生成对应redo log,并存入redo log buffer里面,当事务提交时,将redo log写入redo log file,并更新buffer pool中的数据页,将其放入flush 链表并标记脏页和记录redo log对应的lsn到该页的oldest_modification,这个时候更新就算完成了。为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 **WAL 技术**,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。 5. 至此,一条记录更新完了。 6. 在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。 7. 事务提交(为了方便说明,这里不说组提交的过程,只说两阶段提交): From 138b813a3410a2c160d6c14a049c10227efcb9d2 Mon Sep 17 00:00:00 2001 From: fanwei Date: Tue, 21 May 2024 11:58:20 +0800 Subject: [PATCH 61/78] =?UTF-8?q?=F0=9F=93=83=20docs:=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=A4=B1=E6=95=88=E7=9A=84=E5=8F=82=E8=80=83=E8=B5=84=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/base/how_select.md | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql/base/how_select.md b/mysql/base/how_select.md index bf97dffc..76d5e64b 100644 --- a/mysql/base/how_select.md +++ b/mysql/base/how_select.md @@ -347,7 +347,6 @@ select * from t_user where age > 20 and reward = 100000; - 《MySQL 45 讲》 - 《MySQL 是怎样运行的:从根儿上理解 MySQL》 -- https://gohalo.me/post/mysql-executor.html - http://www.iskm.org/mysql56/sql__executor_8cc_source.html - https://tangocc.github.io/2018/10/11/mysql-sourcecode/ From d9e2e6bbd5451f537b9927091f45f6b522eb04fd Mon Sep 17 00:00:00 2001 From: Navyum <36869790+Navyum@users.noreply.github.com> Date: Tue, 21 May 2024 16:35:54 +0800 Subject: [PATCH 62/78] =?UTF-8?q?=E8=B7=B3=E8=A1=A8=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E8=A7=84=E5=88=99=E6=8F=8F=E8=BF=B0=E4=B8=8D=E6=B8=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 依据下面的例子: - 「元素:abc,权重:3」节点的 leve[1] 的下一个指针指向了「元素:abcde,权重:4」的节点,然后将其和要查找的节点比较。虽然「元素:abcde,权重:4」的节点的权重和要查找的权重相同,但是当前节点的 SDS 类型数据「大于」要查找的数据,所以会继续跳到「元素:abc,权重:3」节点的下一层去找,也就是 leve[0]; 说明:abcde的SDS不满足,权重满足,如果直接到该节点的下一层level0,已经没有后续节点,正确结果无法找到。 --- redis/data_struct/data_struct.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/data_struct/data_struct.md b/redis/data_struct/data_struct.md index daefd026..efdb70f3 100644 --- a/redis/data_struct/data_struct.md +++ b/redis/data_struct/data_struct.md @@ -807,7 +807,7 @@ typedef struct zskiplist { - 如果当前节点的权重「小于」要查找的权重时,跳表就会访问该层上的下一个节点。 - 如果当前节点的权重「等于」要查找的权重时,并且当前节点的 SDS 类型数据「小于」要查找的数据时,跳表就会访问该层上的下一个节点。 -如果上面两个条件都不满足,或者下一个节点为空时,跳表就会使用目前遍历到的节点的 level 数组里的下一层指针,然后沿着下一层指针继续查找,这就相当于跳到了下一层接着查找。 +如果上面两个条件都不满足,或者下一个节点为空时,需要先根据当前节点的*backward 后向指针跳回前一个节点,使用该节点的 level 数组里的下一层指针,然后沿着下一层指针继续查找,这就相当于跳到了下一层接着查找。 举个例子,下图有个 3 层级的跳表。 From 0e54e9045e07a9afad0350c21c56dfa2ae762978 Mon Sep 17 00:00:00 2001 From: Navyum <36869790+Navyum@users.noreply.github.com> Date: Tue, 21 May 2024 17:44:49 +0800 Subject: [PATCH 63/78] =?UTF-8?q?=E8=B7=B3=E8=A1=A8=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E6=8F=8F=E8=BF=B0=E4=B8=8D=E6=B8=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 参考源码: 循环中比较时,实际比的是当前节点的forward指针对应的节点的score和ele ``` for (i = zsl->level-1; i >= 0; i--) { /* store rank that is crossed to reach the insert position */ rank[i] = i == (zsl->level-1) ? 0 : rank[i+1]; while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && sdscmp(x->level[i].forward->ele,ele) < 0))) { x = x->level[i].forward; } update[i] = x; } ``` --- redis/data_struct/data_struct.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redis/data_struct/data_struct.md b/redis/data_struct/data_struct.md index efdb70f3..92ca2dcc 100644 --- a/redis/data_struct/data_struct.md +++ b/redis/data_struct/data_struct.md @@ -804,10 +804,10 @@ typedef struct zskiplist { 查找一个跳表节点的过程时,跳表会从头节点的最高层开始,逐一遍历每一层。在遍历某一层的跳表节点时,会用跳表节点中的 SDS 类型的元素和元素的权重来进行判断,共有两个判断条件: -- 如果当前节点的权重「小于」要查找的权重时,跳表就会访问该层上的下一个节点。 -- 如果当前节点的权重「等于」要查找的权重时,并且当前节点的 SDS 类型数据「小于」要查找的数据时,跳表就会访问该层上的下一个节点。 +- 如果当前节点指向的下一个节点(forward指向的节点)的权重「小于」要查找的权重时,跳表就会访问该层上的下一个节点。 +- 如果当前节点指向的下一个节点(forward指向的节点)的权重「等于」要查找的权重时,并且下一个节点的 SDS 类型数据「小于」要查找的数据时,跳表就会访问该层上的下一个节点。 -如果上面两个条件都不满足,或者下一个节点为空时,需要先根据当前节点的*backward 后向指针跳回前一个节点,使用该节点的 level 数组里的下一层指针,然后沿着下一层指针继续查找,这就相当于跳到了下一层接着查找。 +如果上面两个条件都不满足,或者下一个节点为空时,跳表就会使用目前遍历到的节点的 level 数组里的下一层指针,然后沿着下一层指针继续查找,这就相当于跳到了下一层接着查找。 举个例子,下图有个 3 层级的跳表。 From af601db5d4e1c8e31ab83c9d30b72b905ae42090 Mon Sep 17 00:00:00 2001 From: Qian <71264725+QianKuang8@users.noreply.github.com> Date: Thu, 30 May 2024 13:23:40 +0800 Subject: [PATCH 64/78] Fix typo in port.md --- network/3_tcp/port.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/3_tcp/port.md b/network/3_tcp/port.md index 7603b8c4..de4e62e2 100644 --- a/network/3_tcp/port.md +++ b/network/3_tcp/port.md @@ -259,7 +259,7 @@ TCP 和 UDP 传输协议,在内核中是由两个完全独立的软件模块 TCP 连接是由四元组(源 IP 地址,源端口,目的 IP 地址,目的端口)唯一确认的,那么只要四元组中其中一个元素发生了变化,那么就表示不同的 TCP 连接的。 -所以,如果客户端已使用端口 64992 与服务端 A 建立了连接,那么客户端要与服务端 B 建立连接,还是可以使用端口 64992 的,因为内核是通过四元祖信息来定位一个 TCP 连接的,并不会因为客户端的端口号相同,而导致连接冲突的问题。 +所以,如果客户端已使用端口 64992 与服务端 A 建立了连接,那么客户端要与服务端 B 建立连接,还是可以使用端口 64992 的,因为内核是通过四元组信息来定位一个 TCP 连接的,并不会因为客户端的端口号相同,而导致连接冲突的问题。 > 客户端 TCP 连接 TIME_WAIT 状态过多,会导致端口资源耗尽而无法建立新的连接吗? @@ -279,4 +279,4 @@ TCP 连接是由四元组(源 IP 地址,源端口,目的 IP 地址,目 最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。 -![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) \ No newline at end of file +![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost3@main/%E5%85%B6%E4%BB%96/%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BB%8B%E7%BB%8D.png) From fee9b79d6e559d75cc25e7a727f45c15ea2a53fc Mon Sep 17 00:00:00 2001 From: Navyum <36869790+Navyum@users.noreply.github.com> Date: Thu, 30 May 2024 16:12:11 +0800 Subject: [PATCH 65/78] =?UTF-8?q?=E6=9B=B4=E6=96=B0http2.0=E4=B8=AD?= =?UTF-8?q?=E5=AF=B9mesaage=E7=9A=84=E8=AF=AF=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit message消息对应了http1.0中的请求、响应, 所以一个stream只会有一个请求message和一个响应message即2个message。 此外两条message传输完成后,stream的生命周期就结束了,此时stream就会被销毁。如果有新http请求,会在当前tcp内新建一个stream进行处理,从而实现多路复用。 --- network/2_http/http2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/2_http/http2.md b/network/2_http/http2.md index 178a255d..d79db823 100644 --- a/network/2_http/http2.md +++ b/network/2_http/http2.md @@ -212,7 +212,7 @@ HTTP/2 **二进制帧**的结构如下图: 你可以从上图中看到: - 1 个 TCP 连接包含一个或者多个 Stream,Stream 是 HTTP/2 并发的关键技术; -- Stream 里可以包含 1 个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成; +- 1 个 Stream 里包含 2个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成; - Message 里包含一条或者多个 Frame,Frame 是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容(头部和包体); 因此,我们可以得出个结论:多个 Stream 跑在一条 TCP 连接,同一个 HTTP 请求与响应是跑在同一个 Stream 中,HTTP 消息可以由多个 Frame 构成,一个 Frame 可以由多个 TCP 报文构成。 From dfcce4fdfd30332e2d8e367c1c46aab060ffc50b Mon Sep 17 00:00:00 2001 From: gaohang <1341947277@qq.com> Date: Sat, 15 Jun 2024 17:58:25 +0800 Subject: [PATCH 66/78] fix typo --- network/3_tcp/tcp_stream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/tcp_stream.md b/network/3_tcp/tcp_stream.md index b1b47af7..e39ec568 100644 --- a/network/3_tcp/tcp_stream.md +++ b/network/3_tcp/tcp_stream.md @@ -100,4 +100,4 @@ struct { } message; ``` -当接收方接收到包头的大小(比如 4 个字节)后,就解析包头的内容,于是就可以知道数据的长度,然后接下来就继续读取数据,直到读满数据的长度,就可以组装成一个完整到用户消息来处理了。 \ No newline at end of file +当接收方接收到包头的大小(比如 4 个字节)后,就解析包头的内容,于是就可以知道数据的长度,然后接下来就继续读取数据,直到读满数据的长度,就可以组装成一个完整的用户消息来处理了。 From 7cb12034be38a216931473a8249177bb2a9322fb Mon Sep 17 00:00:00 2001 From: youzi-2333 Date: Tue, 2 Jul 2024 08:48:31 +0800 Subject: [PATCH 67/78] :pencil2: fix typo in `isn_deff` --- network/3_tcp/isn_deff.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/network/3_tcp/isn_deff.md b/network/3_tcp/isn_deff.md index 4e697afe..053dfd99 100644 --- a/network/3_tcp/isn_deff.md +++ b/network/3_tcp/isn_deff.md @@ -4,7 +4,7 @@ **为什么 TCP 每次建立连接时,初始化序列号都要不一样呢?** -接下来,我一步一步给大家讲明白,我觉得应该有不少人会有类似的问题,所以今天在肝一篇! +接下来,我一步一步给大家讲明白,我觉得应该有不少人会有类似的问题,所以今天再肝一篇! > 为什么 TCP 每次建立连接时,初始化序列号都要不一样呢? @@ -12,7 +12,7 @@ > TCP 四次挥手中的 TIME_WAIT 状态不是会持续 2 MSL 时长,历史报文不是早就在网络中消失了吗? -是的,如果能正常四次挥手,由于 TIME_WAIT 状态会持续 2 MSL 时长,历史报文会在下一个连接之前就会自然消失。 +是的,如果能正常四次挥手,由于 TIME_WAIT 状态会持续 2 MSL 时长,历史报文会在下一个连接之前就会自然消失。 但是来了,我们并不能保证每次连接都能通过四次挥手来正常关闭连接。 @@ -44,10 +44,10 @@ > 那客户端和服务端的初始化序列号都是随机的,那还是有可能随机成一样的呀? -RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, localport, remotehost, remoteport)。 +RFC793 提到初始化序列号 ISN 随机生成算法:`ISN = M + F(localhost, localport, remotehost, remoteport)`。 - M 是一个计时器,这个计时器每隔 4 微秒加 1。 -- F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值,要保证 hash 算法不能被外部轻易推算得出。 +- F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值,要保证 Hash 算法不能被外部轻易推算得出。 可以看到,随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。 @@ -66,11 +66,11 @@ RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, 通过前面我们知道,**序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据**。 -不要以为序列号的上限值是 4GB,就以为很大,很难发生回绕。在一个速度足够快的网络中传输大量数据时,序列号的回绕时间就会变短。如果序列号回绕的时间极短,我们就会再次面临之前延迟的报文抵达后序列号依然有效的问题。 +不要以为序列号的上限值是 4 GB,就以为很大,很难发生回绕。在一个速度足够快的网络中传输大量数据时,序列号的回绕时间就会变短。如果序列号回绕的时间极短,我们就会再次面临之前延迟的报文抵达后序列号依然有效的问题。 -为了解决这个问题,就需要有 TCP 时间戳。tcp_timestamps 参数是默认开启的,开启了 tcp_timestamps 参数,TCP 头部就会使用时间戳选项,它有两个好处,**一个是便于精确计算 RTT,另一个是能防止序列号回绕(PAWS)**。 +为了解决这个问题,就需要有 TCP 时间戳。`tcp_timestamps` 参数是默认开启的,开启了 `tcp_timestamps` 参数,TCP 头部就会使用时间戳选项,它有两个好处,**一个是便于精确计算 RTT,另一个是能防止序列号回绕(PAWS)**。 -试看下面的示例,假设 TCP 的发送窗口是 1 GB,并且使用了时间戳选项,发送方会为每个 TCP 报文分配时间戳数值,我们假设每个报文时间加 1,然后使用这个连接传输一个 6GB 大小的数据流。 +试看下面的示例,假设 TCP 的发送窗口是 1 GB,并且使用了时间戳选项,发送方会为每个 TCP 报文分配时间戳数值,我们假设每个报文时间加 1,然后使用这个连接传输一个 6 GB 大小的数据流。 ![图片](https://img-blog.csdnimg.cn/img_convert/1d497c38621ebc44ee3d8763fd03da67.png) @@ -92,8 +92,8 @@ RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, Linux 以本地时钟计数(jiffies)作为时间戳的值,不同的增长时间会有不同的问题: -- 如果时钟计数加 1 需要 1ms,则需要约 24.8 天才能回绕一半,只要报文的生存时间小于这个值的话判断新旧数据就不会出错。 -- 如果时钟计数提高到 1us 加 1,则回绕需要约 71.58 分钟才能回绕,这时问题也不大,因为网络中旧报文几乎不可能生存超过 70 分钟,只是如果 70 分钟没有报文收发则会有一个包越过 PAWS(这种情况会比较多见,相比之下 24 天没有数据传输的 TCP 连接少之又少),但除非这个包碰巧是序列号回绕的旧数据包而被放入接收队列(太巧了吧),否则也不会有问题; +- 如果时钟计数加 1 需要 1 ms,则需要约 24.8 天才能回绕一半,只要报文的生存时间小于这个值的话判断新旧数据就不会出错。 +- 如果时钟计数提高到 1 us 加 1,则回绕需要约 71.58 分钟才能回绕,这时问题也不大,因为网络中旧报文几乎不可能生存超过 70 分钟,只是如果 70 分钟没有报文收发则会有一个包越过 PAWS(这种情况会比较多见,相比之下 24 天没有数据传输的 TCP 连接少之又少),但除非这个包碰巧是序列号回绕的旧数据包而被放入接收队列(太巧了吧),否则也不会有问题; - 如果时钟计数提高到 0.1 us 加 1 回绕需要 7 分钟多一点,这时就可能会有问题了,连接如果 7 分钟没有数据收发就会有一个报文越过 PAWS,对于 TCP 连接而言这么短的时间内没有数据交互太常见了吧!这样的话会频繁有包越过 PAWS 检查,从而使得旧包混入数据中的概率大大增加; Linux 在 PAWS 检查做了一个特殊处理,如果一个 TCP 连接连续 24 天不收发数据则在接收第一个包时基于时间戳的 PAWS 会失效,也就是可以 PAWS 函数会放过这个特殊的情况,认为是合法的,可以接收该数据包。 @@ -104,7 +104,7 @@ static inline bool tcp_paws_check(const struct tcp_options_received *rx_opt, int { ...... - //从上次收到包到现在经历的时间多于 24 天,返回 true + // 从上次收到包到现在经历的时间多于 24 天,返回 true if (unlikely(get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS)) return true; @@ -115,7 +115,7 @@ static inline bool tcp_paws_check(const struct tcp_options_received *rx_opt, int 要解决时间戳回绕的问题,可以考虑以下解决方案: -1)增加时间戳的大小,由 32 bit 扩大到 64bit +1)增加时间戳的大小,由 32 bit 扩大到 64 bit 这样虽然可以在能够预见的未来解决时间戳回绕的问题,但会导致新旧协议兼容性问题,像现在的 IPv4 与 IPv6 一样 From db6977a54a0ed084aef36bce26aabbdcdb01e4bd Mon Sep 17 00:00:00 2001 From: youzi-2333 Date: Tue, 2 Jul 2024 09:08:48 +0800 Subject: [PATCH 68/78] =?UTF-8?q?:pencil2:=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=8B=BC=E5=86=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- network/2_http/https_optimize.md | 2 +- network/2_http/https_rsa.md | 2 +- network/4_ip/ip_base.md | 2 +- network/4_ip/ping.md | 2 +- os/4_process/deadlock.md | 2 +- os/4_process/process_commu.md | 2 +- os/6_file_system/pagecache.md | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/network/2_http/https_optimize.md b/network/2_http/https_optimize.md index 20c5e8f7..907e5719 100644 --- a/network/2_http/https_optimize.md +++ b/network/2_http/https_optimize.md @@ -70,7 +70,7 @@ 如果公司预算充足对于新的服务器是可以考虑购买更好的 CPU,但是对于已经在使用的服务器,硬件优化的方式可能就不太适合了,于是就要从软件的方向来优化了。 -软件的优化方向可以分层两种,一个是**软件升级**,一个是**协议优化**。 +软件的优化方向可以分成两种,一个是**软件升级**,一个是**协议优化**。 先说第一个软件升级,软件升级就是将正在使用的软件升级到最新版本,因为最新版本不仅提供了最新的特性,也优化了以前软件的问题或性能。比如: diff --git a/network/2_http/https_rsa.md b/network/2_http/https_rsa.md index 55cfe29f..34d77f38 100644 --- a/network/2_http/https_rsa.md +++ b/network/2_http/https_rsa.md @@ -87,7 +87,7 @@ TLS 协议是如何解决 HTTP 的风险的呢? 这个密码套件看起来真让人头晕,好一大串,但是其实它是有固定格式和规范的。基本的形式是「**密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法**」,一般 WITH 单词前面有两个单词,第一个单词是约定密钥交换的算法,第二个单词是约定证书的验证算法。比如刚才的密码套件的意思就是: -- 由于 WITH 单词只有一个 RSA,则说明握手时密钥交换算法和签名算法都是使用 RSA; +- 由于 WITH 单词前只有一个 RSA,则说明握手时密钥交换算法和签名算法都是使用 RSA; - 握手后的通信使用 AES 对称算法,密钥长度 128 位,分组模式是 GCM; - 摘要算法 SHA256 用于消息认证和产生随机数; diff --git a/network/4_ip/ip_base.md b/network/4_ip/ip_base.md index eda6d40d..9f83f438 100644 --- a/network/4_ip/ip_base.md +++ b/network/4_ip/ip_base.md @@ -581,7 +581,7 @@ IPv6 可用范围非常大,以至于每台设备都可以配置一个公有 IP *第二种 NAT 穿透技术* -NAT 穿越技术拥有这样的功能,它能够让网络应用程序主动发现自己位于 NAT 设备之后,并且会主动获得 NAT 设备的公有 IP,并为自己建立端口映射条目,注意这些都是 NAT 设备后的应用程序自动完成的。 +NAT 穿透技术拥有这样的功能,它能够让网络应用程序主动发现自己位于 NAT 设备之后,并且会主动获得 NAT 设备的公有 IP,并为自己建立端口映射条目,注意这些都是 NAT 设备后的应用程序自动完成的。 也就是说,在 NAT 穿透技术中,NAT 设备后的应用程序处于主动地位,它已经明确地知道 NAT 设备要修改它外发的数据包,于是它主动配合 NAT 设备的操作,主动地建立好映射,这样就不像以前由 NAT 设备来建立映射了。 diff --git a/network/4_ip/ping.md b/network/4_ip/ping.md index 7b5a913c..4c8774e3 100644 --- a/network/4_ip/ping.md +++ b/network/4_ip/ping.md @@ -293,7 +293,7 @@ traceroute 还有一个作用是**故意设置不分片,从而确定路径的 这样做的目的是为了**路径 MTU 发现**。 -因为有的时候我们并不知道路由器的 `MTU` 大小,以太网的数据链路上的 `MTU` 通常是 `1500` 字节,但是非以外网的 `MTU` 值就不一样了,所以我们要知道 `MTU` 的大小,从而控制发送的包大小。 +因为有的时候我们并不知道路由器的 `MTU` 大小,以太网的数据链路上的 `MTU` 通常是 `1500` 字节,但是非以太网的 `MTU` 值就不一样了,所以我们要知道 `MTU` 的大小,从而控制发送的包大小。 ![MTU 路径发现(UDP 的情况下)](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/ping/18.jpg) diff --git a/os/4_process/deadlock.md b/os/4_process/deadlock.md index 11b4da34..e070e547 100644 --- a/os/4_process/deadlock.md +++ b/os/4_process/deadlock.md @@ -1,6 +1,6 @@ # 5.4 怎么避免死锁? -面试过程中,死锁也是高频的考点,因为如果线上环境真多发生了死锁,那真的出大事了。 +面试过程中,死锁也是高频的考点,因为如果线上环境真的发生了死锁,那真的出大事了。 这次,我们就来系统地聊聊死锁的问题。 diff --git a/os/4_process/process_commu.md b/os/4_process/process_commu.md index 9ea4b5c5..18300d91 100644 --- a/os/4_process/process_commu.md +++ b/os/4_process/process_commu.md @@ -72,7 +72,7 @@ int pipe(int fd[2]) ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/5-%E7%AE%A1%E9%81%93-pipe.jpg) -其实,**所谓的管道,就是内核里面的一串缓存**。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。 +其实,**所谓的管道,就是内核里面的一串缓存**。从管道的一端写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。 看到这,你可能会有疑问了,这两个描述符都是在一个进程里面,并没有起到进程间通信的作用,怎么样才能使得管道是跨过两个进程的呢? diff --git a/os/6_file_system/pagecache.md b/os/6_file_system/pagecache.md index f6e55395..25643ca5 100644 --- a/os/6_file_system/pagecache.md +++ b/os/6_file_system/pagecache.md @@ -80,7 +80,7 @@ page 是内存管理分配的基本单位,Page Cache 由多个 page 构成。p Linux 系统上供用户可访问的内存分为两个类型,即: - File-backed pages:文件备份页也就是 Page Cache 中的 page,对应于磁盘上的若干数据块;对于这些页最大的问题是脏页回盘; -- Anonymous pages:匿名页不对应磁盘上的任何磁盘数据块,它们是进程的运行是内存空间(例如方法栈、局部变量表等属性); +- Anonymous pages:匿名页不对应磁盘上的任何磁盘数据块,它们是进程的运行时内存空间(例如方法栈、局部变量表等属性); **为什么 Linux 不把 Page Cache 称为 block cache,这不是更好吗?** @@ -193,11 +193,11 @@ Linux 提供多种机制来保证数据一致性,但无论是单机上的内 上述两种方式最终都依赖于系统调用,主要分为如下三种系统调用: -| 方法 | 含义 | -| :---------------- | :----------------------------------------------------------- | -| fsync(intfd) | fsync(fd):将 fd 代表的文件的脏数据和脏元数据全部刷新至磁盘中。 | +| 方法 | 含义 | +| :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| fsync(intfd) | fsync(fd):将 fd 代表的文件的脏数据和脏元数据全部刷新至磁盘中。 | | fdatasync(int fd) | fdatasync(fd):将 fd 代表的文件的脏数据刷新至磁盘,同时对必要的元数据刷新至磁盘中,这里所说的必要的概念是指:对接下来访问文件有关键作用的信息,如文件大小,而文件修改时间等不属于必要信息 | -| sync() | sync():则是对系统中所有的脏的文件数据元数据刷新至磁盘中 | +| sync() | sync():则是对系统中所有的脏的文件数据元数据刷新至磁盘中 | 上述三种系统调用可以分别由用户进程与内核进程发起。下面我们研究一下内核线程的相关特性。 From 9b3e47b24fb1229cfacaaf900dbe3f728a8cff68 Mon Sep 17 00:00:00 2001 From: hhuzzz Date: Mon, 8 Jul 2024 21:08:45 +0800 Subject: [PATCH 69/78] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- redis/data_struct/command.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index f325925f..2ef7cfa2 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -6,7 +6,7 @@ 随着 Redis 版本的更新,后面又支持了四种数据类型: **BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)**。 -每种数据对象都各自的应用场景,你能说出它们各自的应用场景吗? +每种数据对象都有各自的应用场景,你能说出它们各自的应用场景吗? 面试过程中,这个问题也很常被问到,又比如会举例一个应用场景来问你,让你说使用哪种 Redis 数据类型来实现。 From 15bf29da38e9c727fd62cbbf02d2bccfc7de4041 Mon Sep 17 00:00:00 2001 From: YuFeng <49507312+CodeXiaoXian@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:57:43 +0800 Subject: [PATCH 70/78] =?UTF-8?q?MySQL=E9=BB=98=E8=AE=A4=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E7=A9=BA=E9=97=B2=E6=97=B6=E9=95=BF=E6=9B=B4=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 默认最大空闲时长是28800秒 --- mysql/base/how_select.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql/base/how_select.md b/mysql/base/how_select.md index 76d5e64b..2d7a805d 100644 --- a/mysql/base/how_select.md +++ b/mysql/base/how_select.md @@ -59,7 +59,7 @@ mysql -h$ip -u$user -p > 空闲连接会一直占用着吗? -当然不是了,MySQL 定义了空闲连接的最大空闲时长,由 `wait_timeout` 参数控制的,默认值是 8 小时(28880 秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。 +当然不是了,MySQL 定义了空闲连接的最大空闲时长,由 `wait_timeout` 参数控制的,默认值是 8 小时(28800 秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。 ```sql mysql> show variables like 'wait_timeout'; From f482590f3ac6bea865053eed5192e9f85389fa86 Mon Sep 17 00:00:00 2001 From: wunameya <81896026+wunameya@users.noreply.github.com> Date: Sun, 15 Sep 2024 16:04:45 +0800 Subject: [PATCH 71/78] =?UTF-8?q?Update=20cache=5Flru.md=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E9=94=99=E5=88=AB=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/3_memory/cache_lru.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/3_memory/cache_lru.md b/os/3_memory/cache_lru.md index 8737ce6a..4edeaabd 100644 --- a/os/3_memory/cache_lru.md +++ b/os/3_memory/cache_lru.md @@ -224,7 +224,7 @@ select * from t_user where name like "%xiaolin%"; ### 怎么避免缓存污染造成的影响? -前面的 LRU 算法只要数据被访问一次,就将数据加入活跃 LRU 链表(或者 young 区域),**这种 LRU 算法进入活跃 LRU 链表的门槛太低了**!正式因为门槛太低,才导致在发生缓存污染的时候,很容就将原本在活跃 LRU 链表里的热点数据淘汰了。 +前面的 LRU 算法只要数据被访问一次,就将数据加入活跃 LRU 链表(或者 young 区域),**这种 LRU 算法进入活跃 LRU 链表的门槛太低了**!正是因为门槛太低,才导致在发生缓存污染的时候,很容就将原本在活跃 LRU 链表里的热点数据淘汰了。 所以,**只要我们提高进入到活跃 LRU 链表(或者 young 区域)的门槛,就能有效地保证活跃 LRU 链表(或者 young 区域)里的热点数据不会被轻易替换掉**。 From 386702e3203b84f5d5ef24ed8df423a83611f27f Mon Sep 17 00:00:00 2001 From: wufei2 Date: Fri, 20 Sep 2024 17:31:29 +0800 Subject: [PATCH 72/78] chore: add hash index description --- mysql/index/index_interview.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mysql/index/index_interview.md b/mysql/index/index_interview.md index 342637fa..1d4bb558 100644 --- a/mysql/index/index_interview.md +++ b/mysql/index/index_interview.md @@ -152,9 +152,15 @@ B+Tree 只在叶子节点存储数据,而 B 树 的非叶子节点也要存储 ***3, B+Tree vs Hash*** -Hash 在做等值查询的时候效率贼快,搜索复杂度为 O(1)。 +Hash 在做等值查询的时候效率贼快,搜索复杂度为 O(1)。但也有其局限性: -但是 Hash 表不适合做范围查询,它更适合做等值的查询,这也是 B+Tree 索引要比 Hash 表索引有着更广泛的适用场景的原因。 +- **数据顺序性**:哈希表无法提供数据的顺序访问,更适合做等值的查询。很多查询不仅需要找到特定的键值,还需要根据键值排序来返回结果,或者执行范围查询。B+Tree 可以很好地支持,Hash 表则无法做到。 + +- **空间效率**:可能导致空间利用效率不高,特别是在处理大量数据时。数据量变大时冲突也会增加。 + +- **需要重新构建**:哈希索引通常只存储在内存中,当数据库重启或发生崩溃时,需要重新构建。 + +因此,B+Tree 索引要比 Hash 表索引有着更广泛的适用场景。 ### 按物理存储分类 From 406cdc9370ded7a8941c8972a81707ac7d3ec807 Mon Sep 17 00:00:00 2001 From: Kjasn Date: Wed, 25 Sep 2024 18:58:05 +0800 Subject: [PATCH 73/78] Fix typo in zero_copy.md --- os/8_network_system/zero_copy.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/8_network_system/zero_copy.md b/os/8_network_system/zero_copy.md index 3ab88730..30a54b77 100644 --- a/os/8_network_system/zero_copy.md +++ b/os/8_network_system/zero_copy.md @@ -79,7 +79,7 @@ write(socket, tmp_buf, len); 我们回过头看这个文件传输的过程,我们只是搬运一份数据,结果却搬运了 4 次,过多的数据拷贝无疑会消耗 CPU 资源,大大降低了系统性能。 -这种简单又传统的文件传输方式,存在冗余的上文切换和数据拷贝,在高并发系统里是非常糟糕的,多了很多不必要的开销,会严重影响系统性能。 +这种简单又传统的文件传输方式,存在冗余的上下文切换和数据拷贝,在高并发系统里是非常糟糕的,多了很多不必要的开销,会严重影响系统性能。 所以,**要想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数**。 @@ -96,7 +96,7 @@ write(socket, tmp_buf, len); 而一次系统调用必然会发生 2 次上下文切换:首先从用户态切换到内核态,当内核执行完任务后,再切换回用户态交由进程代码执行。 -所以,**要想减少上下文切换到次数,就要减少系统调用的次数**。 +所以,**要想减少上下文切换的次数,就要减少系统调用的次数**。 @@ -243,7 +243,7 @@ sendfile 配置的具体意思: 我们都知道程序运行的时候,具有「局部性」,所以通常,刚被访问的数据在短时间内再次被访问的概率很高,于是我们可以用 **PageCache 来缓存最近被访问的数据**,当空间不足时淘汰最久未被访问的缓存。 -所以,读磁盘数据的时候,优先在 PageCache 找,如果数据存在则可以直接返回;如果没有,则从磁盘中读取,然后缓存 PageCache 中。 +所以,读磁盘数据的时候,优先在 PageCache 找,如果数据存在则可以直接返回;如果没有,则从磁盘中读取,然后缓存到 PageCache 中。 还有一点,读取磁盘数据的时候,需要找到数据所在的位置,但是对于机械磁盘来说,就是通过磁头旋转到数据所在的扇区,再开始「顺序」读取数据,但是旋转磁头这个物理动作是非常耗时的,为了降低它的影响,**PageCache 使用了「预读功能」**。 From a912cec9ed2a97557cd752c23310287a5e73ba32 Mon Sep 17 00:00:00 2001 From: Fitz161 Date: Mon, 11 Nov 2024 15:45:19 +0800 Subject: [PATCH 74/78] Fix typo in cpu_mesi.md --- os/1_hardware/cpu_mesi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/1_hardware/cpu_mesi.md b/os/1_hardware/cpu_mesi.md index 4987c2e3..2b0f7923 100644 --- a/os/1_hardware/cpu_mesi.md +++ b/os/1_hardware/cpu_mesi.md @@ -189,7 +189,7 @@ CPU 在读写数据的时候,都是在 CPU Cache 读写数据的,原因是 C 要想实现缓存一致性,关键是要满足 2 点: - 第一点是写传播,也就是当某个 CPU 核心发生写入操作时,需要把该事件广播通知给其他核心; -- 第二点是事物的串行化,这个很重要,只有保证了这个,才能保障我们的数据是真正一致的,我们的程序在各个不同的核心上运行的结果也是一致的; +- 第二点是事务的串行化,这个很重要,只有保证了这个,才能保障我们的数据是真正一致的,我们的程序在各个不同的核心上运行的结果也是一致的; 基于总线嗅探机制的 MESI 协议,就满足上面了这两点,因此它是保障缓存一致性的协议。 From 5dbbf7d0bae1d2955f872517aca8db1ba7eaadad Mon Sep 17 00:00:00 2001 From: Fitz161 Date: Fri, 15 Nov 2024 14:36:36 +0800 Subject: [PATCH 75/78] Fix typo in hash.md --- os/8_network_system/hash.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/8_network_system/hash.md b/os/8_network_system/hash.md index dd8597ce..97c9f790 100644 --- a/os/8_network_system/hash.md +++ b/os/8_network_system/hash.md @@ -192,7 +192,7 @@ hash(key) % 3 为了解决一致性哈希算法不能够均匀的分布节点的问题,就需要引入虚拟节点,对一个真实节点做多个副本。不再将真实节点映射到哈希环上,而是将虚拟节点映射到哈希环上,并将虚拟节点映射到实际节点,所以这里有「两层」映射关系。 -引入虚拟节点后,可以会提高节点的均衡度,还会提高系统的稳定性。所以,带虚拟节点的一致性哈希方法不仅适合硬件配置不同的节点的场景,而且适合节点规模会发生变化的场景。 +引入虚拟节点后,会提高节点的均衡度,还会提高系统的稳定性。所以,带虚拟节点的一致性哈希方法不仅适合硬件配置不同的节点的场景,而且适合节点规模会发生变化的场景。 完! From 5fb9eb5774db17c8bb7852afd4463f6c40998622 Mon Sep 17 00:00:00 2001 From: Fitz161 Date: Fri, 15 Nov 2024 14:40:22 +0800 Subject: [PATCH 76/78] Fix typo in reactor.md --- os/8_network_system/reactor.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/8_network_system/reactor.md b/os/8_network_system/reactor.md index 64bb0929..f25713d2 100644 --- a/os/8_network_system/reactor.md +++ b/os/8_network_system/reactor.md @@ -109,7 +109,7 @@ Reactor 模式是灵活多变的,可以应对不同的业务场景,灵活在 ### 单 Reactor 单进程 / 线程 -一般来说,C 语言实现的是「**单 Reactor *单进程***」的方案,因为 C 语编写完的程序,运行后就是一个独立的进程,不需要在进程中再创建线程。 +一般来说,C 语言实现的是「**单 Reactor *单进程***」的方案,因为 C 语言编写完的程序,运行后就是一个独立的进程,不需要在进程中再创建线程。 而 Java 语言实现的是「**单 Reactor *单线程***」的方案,因为 Java 程序是跑在 Java 虚拟机这个进程上面的,虚拟机中有很多线程,我们写的 Java 程序只是其中的一个线程而已。 @@ -140,7 +140,7 @@ Reactor 模式是灵活多变的,可以应对不同的业务场景,灵活在 - 第一个缺点,因为只有一个进程,**无法充分利用 多核 CPU 的性能**; - 第二个缺点,Handler 对象在业务处理时,整个进程是无法处理其他连接的事件的,**如果业务处理耗时比较长,那么就造成响应的延迟**; -所以,单 Reactor 单进程的方案**不适用计算机密集型的场景,只适用于业务处理非常快速的场景**。 +所以,单 Reactor 单进程的方案**不适用计算密集型的场景,只适用于业务处理非常快速的场景**。 Redis 是由 C 语言实现的,它采用的正是「单 Reactor 单进程」的方案,因为 Redis 业务处理主要是在内存中完成,操作的速度是很快的,性能瓶颈不在 CPU 上,所以 Redis 对于命令的处理是单进程的方案。 @@ -257,7 +257,7 @@ Proactor 正是采用了异步 I/O 技术,所以被称为异步网络模型。 -因此,**Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」**,而 **Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」**。这里的「事件」就是有新连接、有数据可读、有数据可写的这些 I/O 事件这里的「处理」包含从驱动读取到内核以及从内核读取到用户空间。 +因此,**Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」**,而 **Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」**。这里的「事件」就是有新连接、有数据可读、有数据可写的这些 I/O 事件,这里的「处理」包含从驱动读取到内核以及从内核读取到用户空间。 举个实际生活中的例子,Reactor 模式就是快递员在楼下,给你打电话告诉你快递到你家小区了,你需要自己下楼来拿快递。而在 Proactor 模式下,快递员直接将快递送到你家门口,然后通知你。 From e3d242a3c54da76d07ef7b7147797c8616490d15 Mon Sep 17 00:00:00 2001 From: mahaotian's debian Date: Wed, 20 Nov 2024 12:41:12 +0800 Subject: [PATCH 77/78] Added description of NAT --- network/1_base/what_happen_url.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/1_base/what_happen_url.md b/network/1_base/what_happen_url.md index 67d29734..49f13855 100644 --- a/network/1_base/what_happen_url.md +++ b/network/1_base/what_happen_url.md @@ -455,7 +455,7 @@ ARP 协议会在以太网中以**广播**的形式,对以太网所有的设备 接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。 -不知你发现了没有,在网络包传输的过程中,**源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址**,因为需要 MAC 地址在以太网内进行**两个设备**之间的包传输。 +不知你发现了没有,在网络包传输的过程中,**源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址(NAT除外)**,因为需要 MAC 地址在以太网内进行**两个设备**之间的包传输。 > 数据包通过多个路由器道友的帮助,在网络世界途经了很多路程,最终抵达了目的地的城门!城门值守的路由器,发现了这个小兄弟数据包原来是找城内的人,于是它就将数据包送进了城内,再经由城内的交换机帮助下,最终转发到了目的地了。数据包感慨万千的说道:“多谢这一路上,各路大侠的相助!” From 69c89ccf0d86522a9d945695ebbb79f7fd173c39 Mon Sep 17 00:00:00 2001 From: La vaguelette <37582641+shenqingyi9@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:08:16 +0800 Subject: [PATCH 78/78] Update tcp_interview.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “accept”拼写错误 --- network/3_tcp/tcp_interview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/3_tcp/tcp_interview.md b/network/3_tcp/tcp_interview.md index 532059f4..35d2e1cb 100644 --- a/network/3_tcp/tcp_interview.md +++ b/network/3_tcp/tcp_interview.md @@ -1145,7 +1145,7 @@ int listen (int socketfd, int backlog) 答案:**可以的**。 -accpet 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accpet 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。 +accept 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accept 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。 ![半连接队列与全连接队列](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/TCP-%E5%8D%8A%E8%BF%9E%E6%8E%A5%E5%92%8C%E5%85%A8%E8%BF%9E%E6%8E%A5/3.jpg)